why I cannot use FieldType as a type in a val

Viewed 47

I was reading chapter 5.2 Type tagging and phantom types of https://books.underscore.io/shapeless-guide/shapeless-guide.html#fn4

and I tried this:

  val tmp: FieldType["foo", Int] =  "foo" ->> 12

but it gives me error:

type mismatch;
 found   : Int(12)
 required: shapeless.labelled.KeyTag["foo",Int] with Int

see https://scastie.scala-lang.org/f20gxWzcQk21GBv8JDyVog

However, I could pass "foo" ->> 12 val through a parameter type of FieldType[K, V] as:

     def getFieldName[K, V](value: FieldType[K, V])(implicit witness: Witness.Aux[K]): K = witness.value
     def getFieldValue[K, V](value: FieldType[K, V]): V = value

     getFieldName("foo" ->> 12) // gives me foo
     getFieldValue("foo" ->> 12) // gives me 12

Can someone explain why the val tmp: FieldType["foo", Int] is not working? It is not working because I am trying to use a phantom type as a regular type?

Another strange fact is

val tmp: 12 =  "foo" ->> 12
or
val tmp: Int =  "foo" ->> 12

work fine.

I am running this in scala 2.13.8 and shapeless 2.4.0-M1

1 Answers

Indeed, if you don't specify the type of variable it's inferred correctly

val tmp =  "foo" ->> 12
tmp: FieldType["foo", Int]

but if you do then it's a type mismatch error

val tmp: FieldType["foo", Int] =  "foo" ->> 12 // found: Int(12), required: KeyTag["foo",Int] with Int

At the same time, if you use field then the code compiles

val tmp: FieldType["foo", Int] = field["foo"](12)

This seems to be just a type inference issue (and not a Shapeless one) because

val tmp: FieldType["foo", Int] =  "foo" ->>[Int] 12

compiles.

After implicit resolution and macros expansion "foo" ->> 12 desugares into SingletonOps.instance["foo"](Witness.mkWitness["foo"]("foo".asInstanceOf["foo"])).->>(12) i.e.

new SingletonOps {
  type T = "foo"
  val w = new Witness {
    type T = "foo"
    val value = "foo".asInstanceOf["foo"]
  }
  val witness: w.type = w
}.->>(12)

(Scala 2.13.8, Shapeless 2.3.9)

Here is minimized code (plain Scala, independent of Shapeless, I removed Witness, no macros and implicits) demonstrating this type inference issue

type FieldType[K, +V] = V with KeyTag[K, V]
trait KeyTag[K, +V]

def field[K] = new FieldBuilder[K]
class FieldBuilder[K] {
  def apply[V](v : V): FieldType[K, V] = v.asInstanceOf[FieldType[K, V]]
}

trait SingletonOps {
  type T
  def ->>[V](v: V): FieldType[T, V] = field[T](v)
}

val tmp: FieldType["foo", Int] = new SingletonOps {
  type T = "foo"
}.->>/*[Int]*/(12) // found: Int(12), required: KeyTag["foo",Int] with Int

but if we uncomment [Int] then the code compiles.

Related