我正在尝试使用来自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
}
问题是我不知道{1}}在此示例中返回了什么。
我希望它返回foldLeft
,但编译器告诉我返回(String, String, String)
。什么是l.Out
?
猜测源代码有点复杂。
网上没有太多关于此事的信息。
我咨询过的一些信息:
答案 0 :(得分:11)
你的combine
方法返回称为"dependent method type"的方法,这意味着它的返回类型取决于它的一个参数 - 在这种情况下是一个包含{的路径依赖类型{1}}在其路径中。
在许多情况下,编译器将静态地知道有关依赖返回类型的内容,但在您的示例中它并不是。我会尝试在一秒钟内解释原因,但首先要考虑以下更简单的例子:
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
类型为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
^
是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
}
}
和Baz
创建具有不同类型的A
,但编译器没有#&# 39; t,所以它不会接受以下内容:
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
^
中的代码,我们可以说服自己,我们在此处创建的shapeless.ops.hlist
将具有LeftFolder
和In
相同的类型,但编译器不能(或者更确切地说赢得 -it&#39;设计决策)在这个推理中跟随我们,这意味着它不会让我们对待{{ 1}}作为没有更多证据的元组。
幸运的是,由于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
的类型中使用普通旧版LeftFolder
的类型成员语法,但这会使此签名更加混乱。)
l
部分仍然返回columns.foldLeft(...)(...)
,但现在编译器静态地知道那是一个字符串元组。
答案 1 :(得分:0)
在阅读了Travis的完整答案之后,这里有一点他的解决方案:
type CombineTuple = (String, String, String)
def combine[A <: HList](columns: A, suffix: String, separator: String = " and ")(
implicit l: LeftFolder[
A,
CombineTuple,
columnCombinator.type
]
): String =
columns.foldLeft((suffix, separator, ""))(columnCombinator).asInstanceof[CombineTuple]._3
通过这种方式,隐式签名更短,因为在许多调用此签名的方法中都需要它。
更新:正如Travis在评论中所解释的那样,最好使用LeftFolder.Aux
。