Translate/encode Haskell's `data Obj = forall a. (Show a) => Obj a` in Scala

限于喜欢 提交于 2019-12-04 18:16:45
phadej

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]
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!