繁体   English   中英

使用未知类型折叠HList

[英]Fold over HList with unknown Types

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

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

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)
  }
}

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

你是否需要更多的类型信息,一般来说,如果你有一个HList作为静态类型的值,你可能需要改变你的方法。 如果您只知道HList是一个HList (除了为它添加值之外),那么HList基本上没什么可以做的,而且您通常只会将HList写为类型约束。

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

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

我会假设这些类型:

trait Convertable

trait Conversion[A <: Convertable, B <: Convertable] {
  def convert(a: A): B
}

我们可以定义一个类型类,它见证特定的HList由一个或多个类型HList转换组成:

import shapeless._

trait TypeAligned[L <: HList] extends DepFn1[L] {
  type I <: Convertable
  type O <: Convertable
  type Out = Conversion[I, O]
}

L包含有关管道的所有类型信息, IO是其端点的类型。

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

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将具有预期的静态类型(并实现Conversion[AutoA, AutoC] )。

暂无
暂无

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

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