繁体   English   中英

无形:遍历联产品中的类型

[英]Shapeless: Iterate over the types in a Coproduct

我想做一些非常简单的事情,但是我正在努力进行正确的搜索,或者只是了解我所看到的一些解决方案。

给定一个采用通用类型参数(即Coproduct)的方法;

def apply[T <: Coproduct] = {
  ...
}

如何迭代构成联产品的类型? 具体来说,对于每种作为案例类的类型,我想递归地检查每个字段并使用所有信息构建一个映射。

目前,我正在使用构建器模式来解决此问题,如果对其他用户有用,我将在此处发布该模式。

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)) => ???
  ...
}

...但是,如果您想更精确一些,例如具有依赖于输入的返回类型,或者检查此乘积内的类型以求出隐式,则可以使用编写完全相同的模式匹配表达式一个类型类和几个隐式定义:

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                 => ???
}

或作为“依赖类型的函数”:

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可以影响隐式优先级,因此实际上可以将具有模式匹配的任何递归函数转换为该“依赖类型的函数”模式。 这与Haskell不同,后者只有在match表达式的调用情况证明不重叠的情况下才能编写此类函数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM