I've not been able to come up with how to encode Obj
in Scala:
{-# LANGUAGE ExistentialQuantification #-}
data Obj = forall a. (Show a) => Obj a
instance Show Obj where show (Obj a) = "Obj " ++ show a
main = print $ show [Obj "hello", Obj 3, Obj True]
when run, the above produces the following output:
[Obj "hello",Obj 3,Obj True]
In Scala, however, this does not seem to compile:
forSome { type T; implicit val ev: Show[T] }
and neither does this:
forSome { type T : Show[T] }
Is this even possible at the type system level, or do I need to "capture" the type class instance using something like this:
class Obj[T](val x: T)(implicit val: Show[T]) // ...or similar
Any insight would be appreciated!
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.
I've "packaged" Oleg's answer into this generic and (seemingly) reusable structure:
import scala.language.{ higherKinds, implicitConversions }
trait AnyWithTC[TC[_]] { type T; val x: T; implicit val ev: TC[T] }
// don't like the 'implicit' here; suggestions welcome
implicit def AnyWithTC[T, TC[_]](x: T)(implicit ev: TC[T]) = {
type T0 = T; val x0 = x; val ev0 = ev
new AnyWithTC[TC] { type T = T0; val x = x0; val ev = ev0 }
}
then, data Obj = forall a. (Show a) => Obj a
can be implemented like this:
type Obj = AnyWithTC[Show]
implicit val objShow = Show.shows[Obj] { x => "Obj " + x.show.shows(x.x) }
val xs: List[Obj] = List(1, true, "hello")
println(xs.shows) // prints [Obj 1,Obj true, Obj hello]
来源:https://stackoverflow.com/questions/28137713/translate-encode-haskells-data-obj-forall-a-show-a-obj-a-in-scala