你combine
方法返回什麼叫做"dependent method type",這只是意味着它的返回類型取決於它的一個參數 - 在這種情況下,作爲路徑依賴類型,其路徑中包含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
,因爲編譯器靜態地知道,foo
參數的A
是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
}
}
現在我們知道這是不可能的創建具有不同類型A
和B
一個Baz
,但是編譯器沒有,所以不會接受以下內容:
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
和Out
相同的類型,但編譯器不能(或者更確切地說不會-這是一個設計決定)在這個推理中跟隨我們,這意味着它不會讓我們把l.Out
當作一個元組而不需要更多的證據。
幸運的證據是很容易提供拜LeftFolder.Aux
,這僅僅是一個與Out
型成員作爲第四類參數LeftFolder
別名:
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(...)(...)
部分仍返回l.Out
,但現在的編譯器靜態地知道,這是一個元組Ø f字符串。
你已經用簡單易懂的方式解釋了一個複雜的概念。恭喜!! – 2014-10-28 14:33:37
非常好的回答Travis :)我希望文檔更像這樣 – ahjohannessen 2014-10-28 20:38:39
如果'foldLeft()'返回一個包含HList的元組呢?它編譯,但是當我嘗試使用它時,編譯器會抱怨implicits。如有必要,我可以創建另一個問題。 – 2014-10-29 13:01:14