使用未知类型折叠HList

时间:2016-02-01 09:40:39

标签: scala shapeless

我有一种复杂的类型层次结构,但为了分解它,有两个基本特征:ConvertableConversion[A <: Convertable, B <: Convertable,例如:有一个转换可以将Mealy-automaton转换为Moore-automaton。 每个Conversion[A,B]都有一个convert(automaton: A) : B方法。

现在我想介绍智能转换的概念,它基本上是一个普通转换列表,将一个接一个地执行。 因此,我引入了AutoConversion特征,扩展了具有val path : HList参数的转化,以表示转化链,并应实施convert方法,以便AutoConversions只需要提供要采取的实际转化列表。 我认为您可以使用fold path来实现这一点,所以这是我的第一次尝试:

package de.uni_luebeck.isp.conversions

import shapeless._
import shapeless.ops.hlist.LeftFolder

trait AutoConversion[A <: Convertable, B <: Convertable] extends Conversion[A, B] {
  val path: HList

  object combiner extends Poly {
      implicit def doSmth[C <: Convertable, D <: Convertable] = 
         use((conv : Conversion[C, D] , automaton : C) => conv.convert(automaton))

}

  override def convert(startAutomaton: A): B = {
    path.foldLeft(startAutomaton)(combiner)
  }
}

这不会起作用,因为找不到隐含的文件夹,所以我猜测我必须在某处为编译器提供更多类型信息,但不知道在哪里

1 个答案:

答案 0 :(得分:9)

您是否正确需要更多类型信息,而且一般情况下,如果您的值为HList作为静态类型,则您可能需要更改方法。如果您只知道它是HList(除了前置值之外),那么HList基本上没什么可以做的,而且您通常只会永远写HList作为类型约束。

在您的情况下,您所描述的是一种类型对齐的序列。在您采用这种方法之前,我建议您确实确实需要这样做。关于函数(以及类似函数的类型,例如Conversion)的一个好处是它们构成:你有一个A => B和一个B => C,你将它们组成{{1}并且可以永远忘记A => C。你会得到一个漂亮干净的黑盒子,这通常就是你想要的。

但在某些情况下,能够以能够反映管道部分的方式组合类似函数的东西会很有用。我会假设这是其中一种情况,但你应该为自己确认一下。如果不是,那么你很幸运,因为它的到来有点混乱。

我会假设这些类型:

B

我们可以定义一个类型类,证明特定trait Convertable trait Conversion[A <: Convertable, B <: Convertable] { def convert(a: A): B } 由一个或多个类型对齐的转换组成:

HList

import shapeless._ trait TypeAligned[L <: HList] extends DepFn1[L] { type I <: Convertable type O <: Convertable type Out = Conversion[I, O] } 包含有关管道的所有类型信息,LI是其端点的类型。

接下来我们需要这个类型类的实例(请注意,这必须与上面的特征一起定义,以便两个人同伴):

O

现在您可以编写object TypeAligned { type Aux[L <: HList, A <: Convertable, B <: Convertable] = TypeAligned[L] { type I = A type O = B } implicit def firstTypeAligned[ A <: Convertable, B <: Convertable ]: TypeAligned.Aux[Conversion[A, B] :: HNil, A, B] = new TypeAligned[Conversion[A, B] :: HNil] { type I = A type O = B def apply(l: Conversion[A, B] :: HNil): Conversion[A, B] = l.head } implicit def composedTypeAligned[ A <: Convertable, B <: Convertable, C <: Convertable, T <: HList ](implicit tta: TypeAligned.Aux[T, B, C] ): TypeAligned.Aux[Conversion[A, B] :: T, A, C] = new TypeAligned[Conversion[A, B] :: T] { type I = A type O = C def apply(l: Conversion[A, B] :: T): Conversion[A, C] = new Conversion[A, C] { def convert(a: A): C = tta(l.tail).convert(l.head.convert(a)) } } } 版本,跟踪有关管道的所有类型信息:

AutoConversion

你可以像这样使用它:

class AutoConversion[L <: HList, A <: Convertable, B <: Convertable](
  path: L
)(implicit ta: TypeAligned.Aux[L, A, B]) extends Conversion[A, B] {
  def convert(a: A): B = ta(path).convert(a)
}

case class AutoA(i: Int) extends Convertable case class AutoB(s: String) extends Convertable case class AutoC(c: Char) extends Convertable val ab: Conversion[AutoA, AutoB] = new Conversion[AutoA, AutoB] { def convert(a: AutoA): AutoB = AutoB(a.i.toString) } val bc: Conversion[AutoB, AutoC] = new Conversion[AutoB, AutoC] { def convert(b: AutoB): AutoC = AutoC(b.s.lift(3).getOrElse('-')) } val conv = new AutoConversion(ab :: bc :: HNil) 将具有预期的静态类型(并实现conv)。