在 scala 中,"flatten"产品类型的最简单方法是什么?



我正在研究scala语言和成熟的代数类型系统的兼容性,这是一个有趣的案例:

假设我有一个复杂的CC类型,它是AA和BB的乘积:

case class AA(_1: Int, _2: Int)
case class BB(_3: Int)
case class CC(a: AA, b: BB)

现在我想定义一个类型代数 flatten(.),它可以在其操作数的定义中展平所有内部积类型,例如 flatten(CC) 可以产生以下产品类型定义:

case class FCC(_1: Int, _2: Int, _3: Int)

在 scala 2.12+ 中实现这样的东西有多难?欢迎您使用任何可能的技巧,包括但不限于:宏观,斯卡拉兹,猫,无形等。

非常感谢您的意见!

shapeless

是可能的,但不是微不足道的。这是一个不完整的草图,很可能必须进行调整。

声明所需的接口。我们将把树压平成HList

import shapeless._
trait Flattener[A] {
type Result <: HList
def flatten(input: A): Result
}
object Flattener {
type Aux[T, R] = Flattener[T] { type Result = R }
}
def flatten[A](input: A)(implicit ev: Flattener[A]): ev.Result = {
ev.flatten(input)
}

现在我们有 3 种情况:A要么是非产品类型,要么直接编组到单元素列表中:

implicit def nonProdFlattener[T : NotAProduct] : Flattener.Aux[T, (T :: HNil)] = {
new Flattener[T] {
override type Result = (T :: HNil)
override def flatten(input: T) = HList(input)
}
}

在这里,我们需要证明T本身不是产品。shapeless没有开箱即用的 (AFAIK),但您可以使用此技巧: Scala:强制执行 A 不是 B 的子类型

第二种情况:PT型单元素产品,我们已经知道如何将T转换为R

implicit def oneElementProduct[T, R, P <: Product](implicit ev: Aux[P, T :: HNil], fl: Flattener.Aux[T, R]): Flattener.Aux[T, R] = ???

最后一种情况:P是从H开始,然后是T(这是HList)

implicit def multiElemProduct[P, R1, R2, H, T](implicit ev: Aux[P, H :: T], fl1: Flattener.Aux[H, R1], fl2: Flattener.Aux[T, R2]):
Flattener.Aux[P, R1 :: R2] = ???

然后,如果要转换为预定义的FCC而不是HList则需要Generic(https://www.scala-exercises.org/shapeless/generic) 才能将HList转换为案例类。

最新更新