milessabin / shapeless

Generic programming for Scala

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Witness singleton type automatically erased by compile-time type inference

tribbloid opened this issue · comments

This is technically a compiler compatibility issue, so I'm not sure how much work can be done without touching the compiler.

Here is a simple example in scalatest:

class SingletonConstraintSpike extends AnyFunSpec {

  import SingletonConstraintSpike._


  it("3") {

    val v = singletonW.value

//    s3(v) // <----------------------------------------------------------------------------------  breaks!
  }

  it("4") {

    type VT = singletonW.T
    val v: VT = singletonW.value

    s3(v)
  }
}

object SingletonConstraintSpike {

  def s3[T](v: T)(implicit ev: Witness.Aux[T]) = {}

  def adhocW = Witness(3)

  val singletonW = Witness(3)

}

It should be noted that the type of the variable v in case 3 is impeccable (Int(3)), the only problem happens on calling s3 without specifying the correct type parameter Witness._3 (or something equal). The type Int(3) is automatically erased and become Int, for which there is no Witness that can be summoned.

The problem becomes particularly bad when s3 is not just a function with 1 parameter, but part of a type class that has to be recursively called to church-encode an HList-like object. In this case the type class is impossible to be used without declaring their type parameters, which is as bad as writing a chain of type class instances manually.

Can it be solved or we have to wait for the dotty version?

BTW, here is an example that may possibly demonstrate 'a type class that has to be recursively called to church-encode an HList-like object':

class SingletonSpike extends AnyFunSpec {

  import shapeless.syntax.singleton._

  object SingletonBroker extends SingletonProductArgs {

    def applyProduct[H <: HList](v: H)(implicit withKeys: ZipWithKeys[H, H]): withKeys.Out = {
    }
  }

  it("should works") {

    val v1 = SingletonBroker("a", 1) // <------------------------------------------------------works
    val v1 = SingletonBroker.applyProduct("a".narrow :: HNil) //<-----------------------breaks!
  }

  it("should not work") {

    val x = 1
//    val v1 = SingletonBroker("a", x)
  }
}

I don't know why calling SingletonBroker.apply directly can work, but here you go. There are too many mysteries that I won't figure out

Fixed by #1242