简体   繁体   English

使用未知类型折叠HList

[英]Fold over HList with unknown Types

I have kind of a complex type hierarchy, but to break it down there are two base traits: Convertable and Conversion[A <: Convertable, B <: Convertable , eg there is a Conversion which can convert a Mealy-automaton to a Moore-automaton. 我有一种复杂的类型层次结构,但要分解它有两个基本特征: Convertable Conversion[A <: Convertable, B <: ConvertableConversion[A <: Convertable, B <: Convertable可转换,例如,有一个转换可以将Mealy自动机转换为摩尔 -自动机。 Every Conversion[A,B] has a convert(automaton: A) : B method. 每个Conversion[A,B]都有一个convert(automaton: A) : B方法。

Now I want to introduce the concept of smart Conversions , which are basically a List of normal Conversions, which will be performed one after another. 现在我想介绍智能转换的概念,它基本上是一个普通转换列表,将一个接一个地执行。 Therefore I have introduced an AutoConversion trait, extending a Conversion, which has a val path : HList parameter, to represent the chain of conversions, and should implement the convert method, so that AutoConversions just have to provide the list of actual Conversions to take. 因此,我引入了一个AutoConversion特性,扩展了一个转换,它具有一个val path : HList参数,用于表示转换链,并且应该实现convert方法,以便AutoConversions只需提供要采取的实际转换列表。 I think you could implement this with a fold over the path , so here is my first try: 我认为你可以通过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)
  }
}

This won't work, because no implicit Folder can be found, so I'm guessing I have to provide more Type information for the Compiler somewhere, but don't know where 这不起作用,因为没有找到隐含的文件夹,所以我猜我必须为编译器提供更多类型信息,但不知道在哪里

You're right about needing more type information, and in general if you have a value with HList as a static type, it's likely you'll need to change your approach. 你是否需要更多的类型信息,一般来说,如果你有一个HList作为静态类型的值,你可能需要改变你的方法。 There's essentially nothing you can do with an HList if all you know is that it's an HList (besides prepend values to it), and you'll usually only ever write HList as a type constraint. 如果您只知道HList是一个HList (除了为它添加值之外),那么HList基本上没什么可以做的,而且您通常只会将HList写为类型约束。

In your case what you're describing is a kind of type-aligned sequence. 在你的情况下,你所描述的是一种类型对齐的序列。 Before you move forward with this approach, I'd suggest being really sure that you actually need to. 在你采用这种方法之前,我建议你真的确定你真的需要。 One of the nice things about functions (and function-like types like your Conversion ) is that they compose: you have an A => B and a B => C and you compose them into an A => C and can forget about B forever. 关于函数(以及类似函数的类型,如Conversion )的一个好处是它们组成:你有一个A => B和一个B => C并且你将它们组成A => C并且可以忘记B永远。 You get a nice clean black box, which is generally exactly what you want. 你得到一个漂亮干净的黑盒子,这通常正是你想要的。

In some cases, though, it can be useful to be able to compose function-like things in such a way that you can reflect on the pieces of the pipeline. 但是,在某些情况下,能够以能够反映管道部分的方式组合类似函数的东西会很有用。 I'm going to assume that this is one of those cases, but you should confirm that for yourself. 我会假设这是其中一种情况,但你应该为自己确认一下。 If it's not, you're in luck, because what's coming is kind of messy. 如果不是,那么你很幸运,因为即将发生的事情有点混乱。

I'll assume these types: 我会假设这些类型:

trait Convertable

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

We can define a type class that witnesses that a specific HList is composed of one or more conversions whose types line up: 我们可以定义一个类型类,它见证特定的HList由一个或多个类型HList转换组成:

import shapeless._

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

L contains all of the type information about the pipeline, and I and O are the types of its endpoints. L包含有关管道的所有类型信息, IO是其端点的类型。

Next we need instances for this type class (note that this must be defined together with the trait above for the two to be companioned): 接下来我们需要这个类型类的实例(请注意,这必须与上面的特征一起定义,以便两个人同伴):

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

And now you can write a version of your AutoConversion that keeps track of all of the type information about the pipeline: 现在,您可以编写一个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)
}

And you can use it like this: 你可以像这样使用它:

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)

And conv will have the expected static type (and implement Conversion[AutoA, AutoC] ). 并且conv将具有预期的静态类型(并实现Conversion[AutoA, AutoC] )。

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

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