Ihre combine
Methode gibt, was ein "dependent method type" genannt, das bedeutet nur, dass deren Rückgabetyp auf einer seiner Argumente hängt - In diesem Fall als pfadabhängiger Typ, der l
in seinem Pfad enthält.
In vielen Fällen wird der Compiler statisch etwas über den abhängigen Rückgabetyp wissen, aber in Ihrem Beispiel nicht. Ich werde versuchen, warum in einem zweiten zu erklären, aber zunächst die nachfolgend aufgeführten einfacheres Beispiel betrachten:
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
Hier die abgeleitete Art von res0
ist String
, da statisch der Compiler weiß, dass die A
des foo
Argument ist String
. Wir können nicht eine der folgenden schreiben, aber:
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
^
Da hier der Compiler nicht weiß, statisch, dass foo.A
String
ist.
Hier ist ein komplexeres Beispiel:
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
}
}
Jetzt wir wissen, dass es nicht möglich ist, ein Baz
mit verschiedenen Typen für A
und B
, aber der Compiler nicht, zu schaffen, so wird es nicht Akzeptieren Sie Folgendes:
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
^
Dies ist genau das, was Sie sehen. Wenn wir uns den Code in shapeless.ops.hlist
ansehen, können wir uns davon überzeugen, dass die LeftFolder
, die wir hier erstellen, den gleichen Typ für In
und Out
haben wird, aber der Compiler kann nicht (wird nicht -Es ist eine Designentscheidung) Folgen Sie uns in dieser Argumentation, was bedeutet, dass wir l.Out
nicht als ein Tupel ohne weitere Beweise behandeln werden.
Glück, dass die Beweise sind ziemlich einfach, dank LeftFolder.Aux
zu schaffen, die für LeftFolder
mit dem Out
Typ Elemente als ein vierten Typ Parameter nur ein Alias ist:
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
(Sie auch die Art Mitglied Syntax verwenden könnten mit plain old LeftFolder
in l
‚s Art, aber das würde diese Signatur auch chaotischer machen.)
der columns.foldLeft(...)(...)
Teil l.Out
noch zurück, aber jetzt ist der Compiler weiß, statisch, daß das ein Tupel o f Saiten.
Sie haben ein komplexes Konzept auf leicht verständliche Weise erklärt. Glückwunsch!! –
Sehr nette Antwort Travis :) Ich wünschte, die Dokumente wären mehr wie das – ahjohannessen
Was ist, wenn 'foldLeft()' ein Tupel zurückgibt, das ein HList enthält ?. Es kompiliert, aber wenn ich versuche, es zu verwenden, beschwert sich der Compiler über implicits. Wenn nötig, kann ich eine andere Frage stellen. –