您需要更多类型信息是对的,一般来说,如果您有 HList 作为静态类型的值,您可能需要更改方法。如果您只知道 HList(除了为其添加值),那么您基本上对 HList 无能为力,并且您通常只会将 HList 作为类型约束编写。
在您的情况下,您所描述的是一种类型对齐的序列。在您继续使用这种方法之前,我建议您确定您确实需要这样做。函数(以及类似函数的类型,如 Conversion)的优点之一是它们可以组合:你有一个 A => B 和一个 B => C,你将它们组合成一个 A => C 并且可以忘记 @987654329 @ 永远。你会得到一个干净整洁的黑匣子,这通常正是你想要的。
不过,在某些情况下,能够以一种可以反映管道各个部分的方式组合类似函数的事物会很有用。我将假设这是其中一种情况,但您应该自己确认。如果不是,那你很幸运,因为接下来的事情有点混乱。
我会假设这些类型:
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 包含有关管道的所有类型信息,I 和 O 是其端点的类型。
接下来我们需要这个类型类的实例(请注意,这必须与上面的 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])。