我有一种复杂的类型层次结构,但要分解它有两个基本特征:Convertable并且Conversion[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))
Run Code Online (Sandbox Code Playgroud)
}
override def convert(startAutomaton: A): B = {
path.foldLeft(startAutomaton)(combiner)
}
}
Run Code Online (Sandbox Code Playgroud)
这不起作用,因为没有找到隐含的文件夹,所以我猜我必须为编译器提供更多类型信息,但不知道在哪里
你是否需要更多的类型信息,一般来说,如果你有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
}
Run Code Online (Sandbox Code Playgroud)
我们可以定义一个类型类,它见证一个特定HList的类型由一个或多个类型对齐的转换组成:
import shapeless._
trait TypeAligned[L <: HList] extends DepFn1[L] {
type I <: Convertable
type O <: Convertable
type Out = Conversion[I, O]
}
Run Code Online (Sandbox Code Playgroud)
L包含有关管道的所有类型信息,I以及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))
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以编写一个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)
}
Run Code Online (Sandbox Code Playgroud)
你可以像这样使用它:
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)
Run Code Online (Sandbox Code Playgroud)
并且conv将有预期的静态类型(和执行Conversion[AutoA, AutoC]).
| 归档时间: |
|
| 查看次数: |
361 次 |
| 最近记录: |