gpt4 book ai didi

scala - 折叠具有未知类型的 HList

转载 作者:行者123 更新时间:2023-12-04 14:10:44 27 4
gpt4 key购买 nike

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

现在我要介绍智能转换的概念,它基本上是一个普通转换的列表,它会一个接一个地执行。
因此我介绍了一个 AutoConversion trait,扩展一个Conversion,它有一个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)
}
}

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

最佳答案

您需要更多类型信息是正确的,而且通常情况下,如果您的值是 HList作为静态类型,您可能需要更改方法。使用 HList 基本上无能为力如果您只知道它是 HList (除了在它前面加上值),你通常只会写 HList作为类型约束。

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

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

我会假设这些类型:

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]
}
L包含有关管道的所有类型信息,以及 IO是其端点的类型。

接下来我们需要这个类型类的实例(注意,这必须与上面的 trait 一起定义,以便两者相伴):
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] )。

关于scala - 折叠具有未知类型的 HList,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35127196/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com