Scala: using Nothing for singleton instances of polymorphic types
Given a polymorphic trait like
trait Transform[T] { def apply( t: T ) : T }
one might like to implement various sp开发者_高级运维ecialized instances, such as
case class Add[Double] extends Transform[Double] { def apply( t: Double ) ... }
case class Append[String] extends Transform[String] { def apply( t: String ) ... }
etc. Now a frequently desired transform is also the identity transform. Instead of specializing identity for each type T, it appears preferable to use just one singleton instance for all types T. My question is: what is the best way to accomplish this in Scala?
Here is what I found so far: looking at how List[T] implements List.empty[T] and Nil, I tried using Nothing as the type T. This seems to make sense, since Nothing is a subtype of every other type:
object Identity extends Transform[Nothing] {
def apply( t: Nothing ) = t
}
This seems to work. However, wherever I then want use this instance as-is, like here:
val array = Array[Transform[String]]( Transform.Identity )
I get the compiler error "type mismatch; found: Identity.type, required: Transform[String]". In order to use it, I have to explicitly cast it:
... Identity.asInstanceOf[Transform[String]]
I am not sure this is the best or even the 'proper' way to do it. Thanks for any advice.
As @Kim Stebel points out your Transform[T]
is invariant in T
(and has to be because T
occurs in both co- and contra- variant positions in def apply(t : T) : T)
so Transform[Nothing]
is not a subtype of Transform[String]
and can't be made to be.
If your main concern is the instance creation on each call of Kim's def Id[A]
then your best model is the definition of conforms
in in Predef,
private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]
ie. use a polymorphic method, returning a singleton value cast to the appropriate type. This is one of occasions where erasure is a win.
Applied to your situation we would have,
object SingletonId extends Transform[Any] { def apply(t : Any) = t }
def Id[A] = SingletonId.asInstanceOf[Transform[A]]
Sample REPL session,
scala> Id("foo")
res0: java.lang.String = foo
scala> Id(23)
res1: Int = 23
Since the type parameter T
in Transform[T]
is invariant, Transform[Nothing]
is not a subtype of Transform[String]
, thus the compiler complains about it. But using Nothing
here doesn't make sense anyway, since there can never be an instance of Nothing
. So how would you pass one to the apply
method? You would need to cast yet again. The only option I can see is this:
scala> def Id[A] = new Transform[A] { override def apply(t:A) = t }
Id: [A]=> java.lang.Object with Transform[A]
scala> Id(4)
res0: Int = 4
scala> Id("")
res1: java.lang.String = ""
精彩评论