使用Shapeless将案例类层次结构转换为scalaz.Tree

时间:2016-01-08 19:55:16

标签: scala scalaz 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)

这样做最合适的无形功能/示例是什么?

1 个答案:

答案 0 :(得分:2)

首先是免责声明:如果您计划在实际代码中使用此功能,我认为这几乎肯定是一个坏主意。尝试使用Shapeless来做你直接做的事情而不是通过这种类型不安全的表示更好。但这是一个有趣的问题,所以就这样了。

(哦,还有另一个免责声明:这个实现不在头顶,可能有更好的方法来实现这一点。)

首先是帮助类型类(请注意,下面三个部分中的所有代码都需要一起定义 - 如果您在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(_)))
  }
}

另一个助手类型:

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)
    }
  }
}

我们真正关心的类型类:

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,其中所有叶子都有一个成员或一个或多个递归成员。