Il tuo metodo combine
restituisce quello che è chiamato un "dependent method type", il che significa semplicemente che il suo tipo di ritorno dipende da uno dei suoi argomenti -in questo caso come tipo dipendente dal percorso che include l
nel suo percorso.
In molti casi il compilatore conoscerà staticamente qualcosa sul tipo di ritorno dipendente, ma nel tuo esempio non lo fa. Cercherò di spiegare perché in un secondo, ma prima si consideri il seguente esempio più semplice:
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
Qui il tipo derivato di res0
è String
, dal momento che il compilatore sa staticamente che il A
dell'argomento foo
è String
. Non possiamo scrivere una delle seguenti, però:
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
^
Perché qui il compilatore non sa staticamente che foo.A
è String
.
Ecco un esempio più complesso:
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
}
}
Ora abbiamo sappiamo che non è possibile creare un Baz
con diversi tipi di A
e B
, ma il compilatore non lo fa, in modo da non accetta quanto segue:
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
^
Questo è esattamente quello che stai vedendo. Se guardiamo il codice in shapeless.ops.hlist
, possiamo convincerci che lo LeftFolder
che stiamo creando qui avrà lo stesso tipo per In
e Out
, ma il compilatore non può (o meglio non sarà -è una decisione di progettazione) seguici in questo ragionamento, il che significa che non ci consentirà di trattare l.Out
come una tupla senza ulteriori prove.
Per fortuna che la prova è abbastanza facile da fornire grazie alla LeftFolder.Aux
, che è solo un alias per LeftFolder
con il membro Out
caratteristiche di un quarto parametro tipo:
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
(Si potrebbe anche usare la sintassi membro tipo con pianura vecchio LeftFolder
nel tipo 's l
, ma che avrebbe fatto questa firma anche Messier.)
la parte columns.foldLeft(...)(...)
restituisce ancora l.Out
, ma ora il compilatore sa staticamente che questa è una tupla o stringhe.
Hai spiegato un concetto complesso in un modo facile da capire. Congratulazioni!! –
Risposta molto piacevole Travis :) Vorrei che i documenti fossero più come questo – ahjohannessen
E se 'foldLeft()' restituisce una tupla che contiene una lista H ?. Compilare, ma quando provo ad usarlo, il compilatore si lamenta degli impliciti. Se necessario, posso creare un'altra domanda. –