Question

Given a polymorphic trait like

 trait Transform[T] { def apply( t: T ) : T }

one might like to implement various specialized 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.

Was it helpful?

Solution

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

OTHER TIPS

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 = ""
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top