Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do String literals conform to Scala Singleton

Tags:

scala

Wisely or not, I'm writing a method that I'd like to accept only Scala singletons, i.e. objects implemented via "object" rather than constructed instances of a class or trait. It should accept Scala singletons of any type, so "MySingleton.type" won't do.

I came upon the very strange construct "scala.Singleton", which is not documented in the api docs, but seems to do the trick:

scala> def check( obj : Singleton ) = obj
check: (obj: Singleton)Singleton

scala> check( Predef )
res0: Singleton = scala.Predef$@4d3e9963

scala> check ( new java.lang.Object() )
<console>:9: error: type mismatch;
 found   : java.lang.Object
 required: Singleton
              check ( new java.lang.Object() )

scala> check( Map )
res3: Singleton = scala.collection.immutable.Map$@6808aa2d

scala> check( Map.empty[Any,Any] )
<console>:9: error: type mismatch;
 found   : scala.collection.immutable.Map[Any,Any]
 required: Singleton
              check( Map.empty[Any,Any] )

However, rather inexplicably (to me), String literals are accepted as Singletons while explicitly constructed Strings are not:

scala> check( "foo" )
res7: Singleton = foo

scala> check( new String("foo") )
<console>:9: error: type mismatch;
 found   : java.lang.String
 required: Singleton
              check( new String("foo") )

Why do String literals conform to Singleton? Am I misunderstanding what the Singleton type is supposed to specify?

like image 503
Steve Waldman Avatar asked Jul 21 '12 18:07

Steve Waldman


1 Answers

Firstly, what is a singleton type? If you take the view that a type is a set of values, a singleton type is a set with exactly one element.

Most commonly, a top level object can inhabit such a set.

scala> object X
defined module X

scala> X: X.type
res41: X.type = X$@131d1cb

scala> res41: Singleton
res42: Singleton = X$@131d1cb

More generally, and stable value can form a singleton type.

scala> object X { val y: String = "boo" }
defined module X

scala> X.y: X.y.type
res44: X.y.type = boo

scala> res44: Singleton
res45: Singleton = boo

If y is a def or a var, it no longer qualifies, as the value might not be the same over time, so the compiler can't guarantee that the singleton type classifies one-and-only-one value.

scala> object X { def y: String = "boo" }
defined module X

scala> X.y: X.y.type
<console>:12: error: stable identifier required, but X.y found.
              X.y: X.y.type
                     ^

scala> object X { var y: String = "boo" }
defined module X

scala> X.y: X.y.type
<console>:12: error: stable identifier required, but X.y found.
              X.y: X.y.type
                     ^

One more limitation: AnyVals can't form singleton types, because the language specification specifically restricts them to AnyRef.

Paul Phillips has been curating a branch which allows you to express a singleton type for literals.

val xs: Stream[0.type](0)
val ys: Stream[0.type](0, 1) // does not compile
val x = xs.head // inferred type is 0.type, we statically know that this can only be 0!
like image 179
retronym Avatar answered Sep 21 '22 06:09

retronym