Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Scala is it possible to retrieve the `val` referenced by a singleton type?

I'm trying to get a minimal form of dependent types in Scala. If I have

class A[T <: Int]

val x: Int = 7

I can

val a = new A[x.type]

Now is it possible to recover x from its singleton x.type?

Or, if that's not possible, is it possible to associate a stable identifier with a type somehow, and then extract it?

like image 432
Owen Avatar asked Jan 18 '23 18:01

Owen


2 Answers

No, you can't recover x from x.type because of JVM type erasure. For example, how would this be implemented?

def f[A]: A = ???
f[x.type]

At the JVM bytecode level, there's no way that f can find value x: A given A = x.type because it doesn't have anything to work with: all type parameters are lost at run-time, and anyway, the x value is not available on f's parameter stack.

For the same reason, to get a stable-ID of a type, you'd have to reify it as a Manifest value. But when I tried, I get a strange result,

def f[A : Manifest] = implicitly[Manifest[A]]
val x = "hi"
val y = "ho"
println(f[x.type]) // hi.type
println(f[y.type]) // ho.type
f[x.type] == f[y.type] // true !?

I'm not sure why these two type manifests are equal---they even have different toString representations. Could this be a Scala bug? Update: According to the ScalaDoc, The type-relation operators <:< and =:= should be considered approximations only, as there are numerous aspects of type conformance which are not yet adequately represented in manifests.

To summarize, the reification of type information into run-time values doesn't happen automatically on the JVM. Scala's Manifest is supposed to fill the gap, but I guess it doesn't work with dependent types.

like image 143
Kipton Barros Avatar answered Feb 05 '23 22:02

Kipton Barros


To answer your second question, "associating a stable identifier to a type", one way to do it is to use type classes. Let's say I want to associate a string description to types, I can do it as follows:

trait Tag[A] {
  val desc : String
}

implicit object StringTag extends Tag[String] {
  val desc = "character string"
}

implicit object IntTag extends Tag[Int] {
  val desc = "32-bit integer"
}

Now, to recover such tags, enter the implicit magic:

def printTag[T : Tag] {
  val tag = implicitly[Tag[T]]
  println("Type is described as : " + tag.desc)
}

E.g:

printTag[String] // prints "Type is described as : character string"
printTag[Double] // compile-time error: no implicit value found of type Tag[Double]

You can even generate tags as needed, using implicit functions. For instance:

implicit def liftTagToList[T : Tag] = new Tag[List[T]] {
  val underlying = implicitly[Tag[T]].desc
  val desc = "list of " + underlying + "s"
}

I can now do the following:

// prints "Type is described as : list of character strings"
printTag[List[String]]

and even:

// prints "Type is described as : list of list of character stringss"
printTag[List[List[String]]]

Please forgive the pluralization.

like image 34
Philippe Avatar answered Feb 05 '23 22:02

Philippe