You got it almost right:
import scalaz._
import scalaz.Scalaz._
trait Obj {
type T // existential type
val x: T
implicit val show: Show[T]
}
implicit val objSow: Show[Obj] = Show.shows[Obj] { (x: Obj) =>
x.show.shows(x.x)
}
object Obj {
/* "constructor" */
def apply[U](_x: U)(implicit _show: Show[U]): Obj = new Obj {
type T = U
val x = _x
val show = _show
}
}
val test: List[Obj] = List(Obj(1), Obj(true), Obj("foo"))
/*
scala> test.shows
res0: String = [1,true,"foo"]
*/
P.S I'd like to use T
and show
in apply
; not U
and _show
. If someone knows how to avoid shadowing, I'll appreciate!
Alternatively you could use forSome
:
import scala.language.existentials
trait ObjE {
val pair: Tuple2[T, Show[T]] forSome { type T }
}
/* And to define Show instance we have to help compiler unify `T` in pair components. */
def showDepPair[T] = Show.shows[Tuple2[T, Show[T]]] { x => x._2.shows(x._1) }
implicit val showObjE = Show.shows[ObjE] { x => showDepPair.shows(x.pair) }
Here we have to use Tuple2
(or other auxillary type) to capture Show
. I like the previous variant more. For me it's easier to wrap a mind around a type member.
Also in Scala "Don Giovanni" forSome
syntax will be eliminated in favour of val pair: ({ type λ[T] = Tuple2[T, Show[T]] })#λ[_] }
, which works already too. I hope there will be some syntax support for type lambdas as well. kind-projector doesn't help in this situation (repeated use of T
). Maybe something like in Typelevel scalac
: val pair: ([T] => Tuple2[T, Show[T])[_])
.
Another foundational change will be:
A single fundamental concept – type members – can give a precise meaning to generics, existential types, wildcards, and higher-kinded types.
So the both forms will be equivalent from the point of view of the compiler (in former we unpack the tuple). I'm not 100% sure what are the differences currently, if there are any.
P.S. The Troubles with Types helped me understand scala's current type system quirks.