我正在学习Scala中的类型级编程,我很好奇是否可以使用类型级编程来表示树或层次结构。
简单的案例是多层树
A_
|
B_
|C
|D
|
E
如何表示这样的结构?
答案 0 :(得分:14)
有很多方法可以在Scala中表示异构树,其中一个最简单的就是这样:
type MyTreeShape[A, B, C, D, E] = (A, (B, (C, D), E))
这有一些限制(尽管你不能将元组作为叶子的值,因为我们在表示中使用了元组)。对于本答案的其余部分,我将使用涉及Shapeless的HList
的稍微复杂的表示:
import shapeless._
type MyTreeShape[A, B, C, D, E] =
A ::
(B ::
(C :: HNil) ::
(D :: HNil) ::
HNil) ::
(E :: HNil) ::
HNil
这里的树是HList
,其头部是值,其尾部是子树的HList
。
如果我们想对这些树进行有用的通用操作,我们需要一些类型类。我将在Shapeless的FlattenTree
包中的类型类模型上写一个快速深度优先ops.hlist
作为示例。其他类型的大小,深度等可以类似地实现。
这是类型类和便于使用的便捷方法:
trait FlattenTree[T <: HList] extends DepFn1[T] { type Out <: HList }
def flattenTree[T <: HList](t: T)(implicit f: FlattenTree[T]): f.Out = f(t)
现在我们将放入伴随对象的实例:
object FlattenTree {
type Aux[T <: HList, Out0 <: HList] = FlattenTree[T] { type Out = Out0 }
implicit def flattenTree[H, T <: HList](implicit
tf: FlattenForest[T]
): Aux[H :: T, H :: tf.Out] = new FlattenTree[H :: T] {
type Out = H :: tf.Out
def apply(t: H :: T): H :: tf.Out = t.head :: tf(t.tail)
}
}
请注意,这需要一个帮助器类型类FlattenForest
:
trait FlattenForest[F <: HList] extends DepFn1[F] { type Out <: HList }
object FlattenForest {
type Aux[F <: HList, Out0 <: HList] = FlattenForest[F] { type Out = Out0 }
implicit val hnilFlattenForest: Aux[HNil, HNil] = new FlattenForest[HNil] {
type Out = HNil
def apply(f: HNil): HNil = HNil
}
implicit def hconsFlattenForest[
H <: HList,
OutH <: HList,
T <: HList,
OutT <: HList
](implicit
hf: FlattenTree.Aux[H, OutH],
tf: Aux[T, OutT],
pp: ops.hlist.Prepend[OutH, OutT]
): Aux[H :: T, pp.Out] = new FlattenForest[H :: T] {
type Out = pp.Out
def apply(f: H :: T): pp.Out = pp(hf(f.head), tf(f.tail))
}
}
现在我们可以像这样使用它:
val myTree: MyTreeShape[String, Int, Char, Symbol, Double] =
"foo" :: (10 :: HList('a') :: HList('z) :: HNil) :: HList(0.0) :: HNil
val flattened = flattenTree(myTree)
让我们看一下静态类型是否合适:
flattened: String :: Int :: Char :: Symbol :: Double :: HNil
这正是我们想要的。
你可以在没有Shapeless的情况下做到这一切,但它会涉及到令人难以置信的样板量。