Shapeless:迭代联积中的类型
Shapeless: Iterate over the types in a Coproduct
我想做一些非常简单的事情,但我正在努力制定正确的搜索或只是理解我见过的一些解决方案。
给定一个采用泛型类型参数的方法,该参数是一个 Coproduct;
def apply[T <: Coproduct] = {
...
}
如何迭代构成联积的类型?具体来说,对于属于案例 class 的每种类型,我想递归检查每个字段并构建包含所有信息的地图。
目前我正在使用构建器模式来解决这个问题,我将在此处 post 以防它对其他人有用;
class ThingMaker[Entities <: Coproduct] private {
def doThings(item: Entities): Set[Fact] = {
...
}
def register[A <: Product with Serializable]: ThingMaker[A :+: Entities] = {
// useful work can be done here on a per type basis
new ThingMaker[A :+: Entities]
}
}
object ThingMaker {
def register[A <: Product with Serializable]: ThingMaker[A :+: CNil] = {
// useful work can be done here on a per type basis
new ThingMaker[A :+: CNil]
}
}
如果您只想检查值,您可以像对任何其他值一样简单地对联积进行模式匹配...
def apply[T <: Coproduct](co: T): Any = co match {
case Inl(MyCaseClass(a, b, c)) => ???
...
}
...但是如果你想比这更精确,例如有一个依赖于输入的 return 类型,或者检查这个余产品中的类型以召唤隐式,那么你可以使用类型 class 和几个隐式定义来编写 完全相同的模式匹配表达式 :
trait MyFunction[T <: Coproduct] {
type Out
def apply(co: T): Out
}
object MyFunction {
// case Inl(MyCaseClass(a, b, c)) =>
implicit val case1 = new MyFunction[Inl[MyCaseClass]] {
type Out = Nothing
def apply(co: Inl[MyCaseClass]): Out = ???
}
// ...
}
一般来说,当您想要对所有类型的余积进行迭代时,您将始终遵循相同的尾递归结构。作为函数:
def iterate[T <: Coproduct](co: T): Any = co match {
case Inr(head: Any) => println(v)
case Inl(tail: Coproduct) => iterate(tail)
case CNil => ???
}
或作为 "dependently typed function":
trait Iterate[T <: Coproduct]
object Iterate {
implicit def caseCNil = new Iterate[CNil] {...}
implicit def caseCCons[H, T <: Coproduct](implicit rec: Iterate[T]) =
new Iterate[H :+: T] {...}
}
例如,您可以使用加法 ClassTag
隐式获得联积中每种类型的名称:
trait Iterate[T <: Coproduct] { def types: List[String] }
object Iterate {
implicit def caseCNil = new Iterate[CNil] {
def types: List[String] = Nil
}
implicit def caseCCons[H, T <: Coproduct]
(implicit
rec: Iterate[T],
ct: reflect.ClassTag[H]
) =
new Iterate[H :+: T] {
def types: List[String] = ct.runtimeClass.getName :: rec.types
}
}
implicitly[Iterate[Int :+: String :+: CNil]].types // List(int, java.lang.String)
由于 Scala 允许您影响隐式优先级的方式,实际上可以将任何具有模式匹配的递归函数转换为此 "dependently typed function" 模式。这与 Haskell 不同,后者只有在匹配表达式的调用情况可证明不重叠时才能编写此类函数。
我想做一些非常简单的事情,但我正在努力制定正确的搜索或只是理解我见过的一些解决方案。
给定一个采用泛型类型参数的方法,该参数是一个 Coproduct;
def apply[T <: Coproduct] = {
...
}
如何迭代构成联积的类型?具体来说,对于属于案例 class 的每种类型,我想递归检查每个字段并构建包含所有信息的地图。
目前我正在使用构建器模式来解决这个问题,我将在此处 post 以防它对其他人有用;
class ThingMaker[Entities <: Coproduct] private {
def doThings(item: Entities): Set[Fact] = {
...
}
def register[A <: Product with Serializable]: ThingMaker[A :+: Entities] = {
// useful work can be done here on a per type basis
new ThingMaker[A :+: Entities]
}
}
object ThingMaker {
def register[A <: Product with Serializable]: ThingMaker[A :+: CNil] = {
// useful work can be done here on a per type basis
new ThingMaker[A :+: CNil]
}
}
如果您只想检查值,您可以像对任何其他值一样简单地对联积进行模式匹配...
def apply[T <: Coproduct](co: T): Any = co match {
case Inl(MyCaseClass(a, b, c)) => ???
...
}
...但是如果你想比这更精确,例如有一个依赖于输入的 return 类型,或者检查这个余产品中的类型以召唤隐式,那么你可以使用类型 class 和几个隐式定义来编写 完全相同的模式匹配表达式 :
trait MyFunction[T <: Coproduct] {
type Out
def apply(co: T): Out
}
object MyFunction {
// case Inl(MyCaseClass(a, b, c)) =>
implicit val case1 = new MyFunction[Inl[MyCaseClass]] {
type Out = Nothing
def apply(co: Inl[MyCaseClass]): Out = ???
}
// ...
}
一般来说,当您想要对所有类型的余积进行迭代时,您将始终遵循相同的尾递归结构。作为函数:
def iterate[T <: Coproduct](co: T): Any = co match {
case Inr(head: Any) => println(v)
case Inl(tail: Coproduct) => iterate(tail)
case CNil => ???
}
或作为 "dependently typed function":
trait Iterate[T <: Coproduct]
object Iterate {
implicit def caseCNil = new Iterate[CNil] {...}
implicit def caseCCons[H, T <: Coproduct](implicit rec: Iterate[T]) =
new Iterate[H :+: T] {...}
}
例如,您可以使用加法 ClassTag
隐式获得联积中每种类型的名称:
trait Iterate[T <: Coproduct] { def types: List[String] }
object Iterate {
implicit def caseCNil = new Iterate[CNil] {
def types: List[String] = Nil
}
implicit def caseCCons[H, T <: Coproduct]
(implicit
rec: Iterate[T],
ct: reflect.ClassTag[H]
) =
new Iterate[H :+: T] {
def types: List[String] = ct.runtimeClass.getName :: rec.types
}
}
implicitly[Iterate[Int :+: String :+: CNil]].types // List(int, java.lang.String)
由于 Scala 允许您影响隐式优先级的方式,实际上可以将任何具有模式匹配的递归函数转换为此 "dependently typed function" 模式。这与 Haskell 不同,后者只有在匹配表达式的调用情况可证明不重叠时才能编写此类函数。