使用 Shapeless 将案例 class 层次结构转换为 scalaz.Tree
Converting a case class hierarchy to scalaz.Tree with Shapeless
考虑以下层次结构:
sealed trait Ex
case class Lit(value: Int) extends Ex
case class Var(name: Char) extends Ex
case class Add(a: Ex, b: Ex) extends Ex
我想将 val ex = Add(Lit(0),Add(Lit(1),Var('a')))
等表达式转换为 scalaz.Tree[Either[Class[Any],Any]]]
,在本例中将给出:
-\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(0)
|
`- -\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(1)
|
`- -\/(class Var)
|
`- \/-(a)
最适合从哪个 Shapeless functionality/example 入手?
首先免责声明:如果您打算在实际代码中使用它,我认为这几乎肯定是个坏主意。最好尝试使用 Shapeless 来直接做你想做的事情,而不是通过这个 type-unsafe 表示。但这是一个有趣的问题,所以开始吧。
(哦,还有一个免责声明:这个实现是最重要的,可能有更好的方法来完成这个。)
首先是一个辅助类型 class(请注意,下面三个部分中的所有代码都需要一起定义——如果你在 REPL 中,你可以使用 :paste
) :
import scalaz.{ Show, Tree, \/ }, scalaz.syntax.either._
import shapeless._, ops.hlist.ToTraversable
trait TreeifyCc[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any]
}
trait LowPriorityTreeifyCc {
implicit def singleMemberTreeifyCc[A, C, R <: HList, X](implicit
gen: Generic.Aux[C, R],
ev: R <:< (X :: HNil)
): TreeifyCc[A, C] = new TreeifyCc[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any] = Tree.Node(
c.getClass.left,
Stream(Tree.Leaf(ev(gen.to(c)).head.right))
)
}
}
object TreeifyCc extends LowPriorityTreeifyCc {
implicit def recursiveTreeifyCc[A, C, R <: HList](implicit
gen: Generic.Aux[C, R],
ts: ToTraversable.Aux[R, Stream, A]
): TreeifyCc[A, C] = new TreeifyCc[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any] =
Tree.Node(c.getClass.left, ts(gen.to(c)).map(tf(_)))
}
}
还有另一个助手类型 class:
trait TreeifyAdt[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any]
}
object TreeifyAdt {
implicit def cnilTreeifyAdt[A]: TreeifyAdt[A, CNil] =
new TreeifyAdt[A, CNil] {
def apply(tf: Treeify[A], c: CNil): Tree[Class[_] \/ Any] =
sys.error("impossible")
}
implicit def cconsAdt[A, H, T <: Coproduct](implicit
cc: TreeifyCc[A, H],
ta: TreeifyAdt[A, T]
): TreeifyAdt[A, H :+: T] = new TreeifyAdt[A, H :+: T] {
def apply(tf: Treeify[A], c: H :+: T): Tree[Class[_] \/ Any] = c match {
case Inl(h) => cc(tf, h)
case Inr(t) => ta(tf, t)
}
}
}
而我们真正关心的类型class:
trait Treeify[A] {
def apply(a: A): Tree[Class[_] \/ Any]
}
object Treeify {
implicit def treeifyAdt[A, R <: Coproduct](implicit
gen: Generic.Aux[A, R],
adt: TreeifyAdt[A, R]
): Treeify[A] = new Treeify[A] {
def apply(a: A): Tree[Class[_] \/ Any] = adt(this, gen.to(a))
}
def toTree[A](a: A)(implicit tf: Treeify[A]): Tree[Class[_] \/ Any] = tf(a)
}
我们可以这样使用它:
scala> val ex: Ex = Add(Lit(0), Add(Lit(1), Var('a')))
ex: Ex = Add(Lit(0),Add(Lit(1),Var(a)))
scala> Treeify.toTree(ex).drawTree(scalaz.Show.showFromToString)
res0: String =
"-\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(0)
|
`- -\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(1)
|
`- -\/(class Var)
|
`- \/-(a)
"
这适用于任何 ADT,其中所有叶子都具有单个成员或一个或多个递归成员。
考虑以下层次结构:
sealed trait Ex
case class Lit(value: Int) extends Ex
case class Var(name: Char) extends Ex
case class Add(a: Ex, b: Ex) extends Ex
我想将 val ex = Add(Lit(0),Add(Lit(1),Var('a')))
等表达式转换为 scalaz.Tree[Either[Class[Any],Any]]]
,在本例中将给出:
-\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(0)
|
`- -\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(1)
|
`- -\/(class Var)
|
`- \/-(a)
最适合从哪个 Shapeless functionality/example 入手?
首先免责声明:如果您打算在实际代码中使用它,我认为这几乎肯定是个坏主意。最好尝试使用 Shapeless 来直接做你想做的事情,而不是通过这个 type-unsafe 表示。但这是一个有趣的问题,所以开始吧。
(哦,还有一个免责声明:这个实现是最重要的,可能有更好的方法来完成这个。)
首先是一个辅助类型 class(请注意,下面三个部分中的所有代码都需要一起定义——如果你在 REPL 中,你可以使用 :paste
) :
import scalaz.{ Show, Tree, \/ }, scalaz.syntax.either._
import shapeless._, ops.hlist.ToTraversable
trait TreeifyCc[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any]
}
trait LowPriorityTreeifyCc {
implicit def singleMemberTreeifyCc[A, C, R <: HList, X](implicit
gen: Generic.Aux[C, R],
ev: R <:< (X :: HNil)
): TreeifyCc[A, C] = new TreeifyCc[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any] = Tree.Node(
c.getClass.left,
Stream(Tree.Leaf(ev(gen.to(c)).head.right))
)
}
}
object TreeifyCc extends LowPriorityTreeifyCc {
implicit def recursiveTreeifyCc[A, C, R <: HList](implicit
gen: Generic.Aux[C, R],
ts: ToTraversable.Aux[R, Stream, A]
): TreeifyCc[A, C] = new TreeifyCc[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any] =
Tree.Node(c.getClass.left, ts(gen.to(c)).map(tf(_)))
}
}
还有另一个助手类型 class:
trait TreeifyAdt[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any]
}
object TreeifyAdt {
implicit def cnilTreeifyAdt[A]: TreeifyAdt[A, CNil] =
new TreeifyAdt[A, CNil] {
def apply(tf: Treeify[A], c: CNil): Tree[Class[_] \/ Any] =
sys.error("impossible")
}
implicit def cconsAdt[A, H, T <: Coproduct](implicit
cc: TreeifyCc[A, H],
ta: TreeifyAdt[A, T]
): TreeifyAdt[A, H :+: T] = new TreeifyAdt[A, H :+: T] {
def apply(tf: Treeify[A], c: H :+: T): Tree[Class[_] \/ Any] = c match {
case Inl(h) => cc(tf, h)
case Inr(t) => ta(tf, t)
}
}
}
而我们真正关心的类型class:
trait Treeify[A] {
def apply(a: A): Tree[Class[_] \/ Any]
}
object Treeify {
implicit def treeifyAdt[A, R <: Coproduct](implicit
gen: Generic.Aux[A, R],
adt: TreeifyAdt[A, R]
): Treeify[A] = new Treeify[A] {
def apply(a: A): Tree[Class[_] \/ Any] = adt(this, gen.to(a))
}
def toTree[A](a: A)(implicit tf: Treeify[A]): Tree[Class[_] \/ Any] = tf(a)
}
我们可以这样使用它:
scala> val ex: Ex = Add(Lit(0), Add(Lit(1), Var('a')))
ex: Ex = Add(Lit(0),Add(Lit(1),Var(a)))
scala> Treeify.toTree(ex).drawTree(scalaz.Show.showFromToString)
res0: String =
"-\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(0)
|
`- -\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(1)
|
`- -\/(class Var)
|
`- \/-(a)
"
这适用于任何 ADT,其中所有叶子都具有单个成员或一个或多个递归成员。