HList#foldLeft() 返回什么? [英] What does HList#foldLeft() return?
问题描述
我正在尝试使用 Shapeless 中的 HList.
这是我第一次尝试:
特质列[T] {val 名称:字符串}case class CV[T](col: Column[T], value: T)对象简历{对象 columnCombinator 扩展 Poly2 {隐式 def 算法[A] = at[(String, String, String), CV[A]] { case ((suffix, separator, sql), cv) ⇒(后缀, 分隔符, if (sql == "") cv.col.name+suffix else sql+separator+cv.col.name+suffix)}}def combine[A <: HList](columns: A, suffix: String, separator: String = " and ")(隐式 l: LeftFolder[A, (String, String, String), columnCombinator.type]): String =columns.foldLeft((suffix, separator, ""))(columnCombinator)._3}
问题是我不知道 foldLeft
在这个例子中返回了什么.
我希望它返回 (String, String, String)
,但编译器告诉我返回 l.Out
.什么是l.Out
?
源代码有点难猜.
网络上没有太多关于此的信息.
我查阅过的一些信息:
您的 combine
方法返回所谓的 依赖方法类型",这只是意味着它的返回类型依赖于它的一个参数——在这种情况下作为路径依赖类型,在其路径中包含 l
.>
在许多情况下,编译器会静态地了解依赖返回类型,但在您的示例中它不知道.稍后我将尝试解释原因,但首先考虑以下更简单的示例:
scala>特质 Foo { 类型 A;定义一个:A }定义特征 Foo标度>def fooA(foo: Foo): foo.A = foo.afooA: (foo: Foo)foo.A标度>fooA(new Foo { type A = String; def a = "I'm a StringFoo" })res0: String = 我是一个 StringFoo
这里res0
的推断类型是String
,因为编译器静态地知道foo
的A
> 参数是 String
.但是,我们不能写以下任何一项:
scala>def fooA(foo: Foo): String = foo.a<console>:12: 错误:类型不匹配;发现:foo.A要求:字符串def fooA(foo: Foo): String = foo.a^标度>def fooA(foo: Foo) = foo.a.substring<console>:12: 错误:值子串不是 foo.A 的成员def fooA(foo: Foo) = foo.a.substring^
因为这里编译器不会静态地知道 foo.A
是 String
.
这是一个更复杂的例子:
密封特性 Baz {A型B型定义 b: B}对象巴兹{def makeBaz[T](t: T): Baz { type A = T;类型 B = T } = 新巴兹 {类型 A = T类型 B = T定义 b = t}}
现在我们知道不可能为A
和B
创建具有不同类型的Baz
,但编译器没有,所以它不会接受以下内容:
scala>def bazB(baz: Baz { type A = String }): String = baz.b<console>:13: 错误:类型不匹配;发现:baz.B要求:字符串def bazB(baz: Baz { type A = String }): String = baz.b^
这正是你所看到的.如果我们查看 shapeless.ops.hlist
中的代码,我们可以说服自己,我们在此处创建的 LeftFolder
将具有与 In<相同的类型/code> 和
Out
,但编译器不能(或者说不会——这是一个设计决定)在这个推理中跟随我们,这意味着它不会让我们将 l.Out
视为没有更多证据的元组.
幸运的是,由于 LeftFolder.Aux
可以很容易地提供证据,它只是具有 Out
类型成员的 LeftFolder
的别名作为第四个类型参数:
def combine[A <: HList](columns: A, suffix: String, separator: String = " and ")(隐式 l:LeftFolder.Aux[一种,(字符串,字符串,字符串),columnCombinator.type,(字符串,字符串,字符串)]): 字符串 =columns.foldLeft((suffix, separator, ""))(columnCombinator)._3
(您也可以在 l
的类型中使用带有普通旧 LeftFolder
的类型成员语法,但这会使此签名更加混乱.)
columns.foldLeft(...)(...)
部分仍然返回 l.Out
,但现在编译器静态地知道这是一个字符串元组.
I'm trying to play with HList's from Shapeless.
This is my first try:
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
}
The problem is I don't know what foldLeft
does return in this example.
I expect it to return (String, String, String)
, but the compiler tells me that returns l.Out
. What is l.Out
?
The source code is a little complicated to guess it.
There isn't much information in the web about this.
Some information I've consulted:
Your combine
method returns what's called a "dependent method type", which just means that its return type depends on one of its arguments—in this case as a path-dependent type that includes l
in its path.
In many cases the compiler will statically know something about the dependent return type, but in your example it doesn't. I'll try to explain why in a second, but first consider the following simpler example:
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
Here the inferred type of res0
is String
, since the compiler statically knows that the A
of the foo
argument is String
. We can't write either of the following, though:
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
^
Because here the compiler doesn't statically know that foo.A
is String
.
Here's a more complex example:
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
}
}
Now we know that it's not possible to create a Baz
with different types for A
and B
, but the compiler doesn't, so it won't accept the following:
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
^
This is exactly what you're seeing. If we look at the code in shapeless.ops.hlist
, we can convince ourselves that the LeftFolder
we're creating here will have the same type for In
and Out
, but the compiler can't (or rather won't—it's a design decision) follow us in this reasoning, which means it won't let us treat l.Out
as a tuple without more evidence.
Fortunately that evidence is pretty easy to provide thanks to LeftFolder.Aux
, which is just an alias for LeftFolder
with the Out
type member as a fourth type parameter:
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
(You could also use the type member syntax with plain old LeftFolder
in l
's type, but that would make this signature even messier.)
The columns.foldLeft(...)(...)
part still returns l.Out
, but now the compiler statically knows that that's a tuple of strings.
这篇关于HList#foldLeft() 返回什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!