我正在尝试使用来自Shapeless的HList.
这是我的第一次尝试:
trait Column[T] {
val name: String
}
case class CV[T](col: Column[T], value: T)
object CV {
object columnCombinator extends Poly2 {
implicit def algo[A] = at[(String, String, String), CV[A]] { case ((suffix, separator, sql), cv) ?
(suffix, separator, if (sql == "") cv.col.name+suffix else sql+separator+cv.col.name+suffix)
}
}
def combine[A <: HList](columns: A, suffix: String, separator: String = " and ")
(implicit l: LeftFolder[A, (String, String, String), columnCombinator.type]): String =
columns.foldLeft((suffix, separator, ""))(columnCombinator)._3
}
Run Code Online (Sandbox Code Playgroud)
问题是我不知道foldLeft这个例子中返回了什么.
我希望它返回(String, String, String),但编译器告诉我返回l.Out.什么是l.Out?
猜测源代码有点复杂.
网上没有太多关于此的信息.
我咨询过的一些信息:
Tra*_*own 11
你的combine方法返回所谓的"依赖方法类型",这意味着它的返回类型取决于它的一个参数 - 在这种情况下是一个包含l在其路径中的路径依赖类型.
在许多情况下,编译器将静态地知道有关依赖返回类型的内容,但在您的示例中却没有.我将尝试在一秒钟内解释原因,但首先考虑以下更简单的例子:
scala> trait Foo { type A; def a: A }
defined trait Foo
scala> def fooA(foo: Foo): foo.A = foo.a
fooA: (foo: Foo)foo.A
scala> fooA(new Foo { type A = String; def a = "I'm a StringFoo" })
res0: String = I'm a StringFoo
Run Code Online (Sandbox Code Playgroud)
这里推断出的类型的res0是String,因为编译器静态地知道该A在的foo说法是String.但是,我们不能写下列任何一种:
scala> def fooA(foo: Foo): String = foo.a
<console>:12: error: type mismatch;
found : foo.A
required: String
def fooA(foo: Foo): String = foo.a
^
scala> def fooA(foo: Foo) = foo.a.substring
<console>:12: error: value substring is not a member of foo.A
def fooA(foo: Foo) = foo.a.substring
^
Run Code Online (Sandbox Code Playgroud)
因为这里的编译器并不是静态知道foo.A的String.
这是一个更复杂的例子:
sealed trait Baz {
type A
type B
def b: B
}
object Baz {
def makeBaz[T](t: T): Baz { type A = T; type B = T } = new Baz {
type A = T
type B = T
def b = t
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们知道,这是不可能的创建Baz不同类型的A和B,但是编译器没有,所以不会接受如下:
scala> def bazB(baz: Baz { type A = String }): String = baz.b
<console>:13: error: type mismatch;
found : baz.B
required: String
def bazB(baz: Baz { type A = String }): String = baz.b
^
Run Code Online (Sandbox Code Playgroud)
这正是你所看到的.如果我们查看代码shapeless.ops.hlist,我们可以说服自己,LeftFolder我们在这里创建的将具有相同的类型In和Out,但编译器不能(或者更确切地说,不是 - 这是一个设计决策)在这个推理中跟随我们,这意味着它不会让我们l.Out在没有更多证据的情况下视为元组.
幸运的证据是很容易提供感谢LeftFolder.Aux,这仅仅是一个别名LeftFolder与Out类型成员作为第四类型参数:
def combine[A <: HList](columns: A, suffix: String, separator: String = " and ")(
implicit l: LeftFolder.Aux[
A,
(String, String, String),
columnCombinator.type,
(String, String, String)
]
): String =
columns.foldLeft((suffix, separator, ""))(columnCombinator)._3
Run Code Online (Sandbox Code Playgroud)
(你也可以使用与普通的旧类型成员的语法LeftFolder中l的类型,但是这将让这个签名甚至混乱.)
该columns.foldLeft(...)(...)部分仍然返回l.Out,但现在编译器静态地知道这是一个字符串元组.