L'inferenza di tipo sulle funzioni anonime con Enrich-my-biblioteca
-
26-10-2019 - |
Domanda
Say Ho un metodo che trasforma un (funzione su due elementi) in una (funzione due sequenze):
def seqed[T](f: (T,T) => T): (Seq[T], Seq[T]) => Seq[T] = (_,_).zipped map f
In parole, la funzione risultante prende due sequenze xs
e ys
, e crea una nuova sequenza costituito da (xs(0) f ys(0), xs(1) f ys(1), ...)
Così, per esempio, se è xss
Seq(Seq(1,2),Seq(3,4))
e f
è (a: Int, b: Int) => a + b
, possiamo invocare così:
xss reduceLeft seqed(f) // Seq(4, 6)
o con una funzione anonima:
xss reduceLeft seqed[Int](_+_)
Questo è abbastanza buono; sarebbe bello per sbarazzarsi del argomento di tipo [Int]
ma non vedere come (tutte le idee?).
per farlo sentire un po 'più come il metodo tupled
, ho anche provato il modello Enrich-my-library:
class SeqFunction[T](f: (T,T) => T) {
def seqed: (Seq[T], Seq[T]) => Seq[T] = (_,_).zipped map f
}
implicit def seqFunction[T](f: (T,T) => T) = new SeqFunction(f)
Per una funzione di pre-definita questa grande opera, ma è brutto con quelli anonimi
xss reduceLeft f.seqed
xss reduceLeft ((_:Int) + (_:Int)).seqed
C'è un altro modo per riformulare questo in modo che i tipi sono desunti, e posso utilizzare la sintassi qualcosa come:
// pseudocode
xss reduceLeft (_+_).seqed // ... or failing that
xss reduceLeft (_+_).seqed[Int]
? O sto chiedendo troppo di inferenza di tipo?
Soluzione 3
Il motivo per cui un tipo di annotazione è necessario
xss reduceLeft seqed[Int](_+_)
ma non in
xs zip ys map Function.tupled(_+_)
è dovuto alla differenza di requisiti di tipo tra map
e reduceLeft
.
def reduceLeft [B >: A] (f: (B, A) ⇒ B): B
def map [B] (f: (A) ⇒ B): Seq[B] // simple version!
si aspetta reduceLeft
seqed
per tornare tipo B
dove B >: Int
. Sembra che quindi il tipo preciso per seqed
non può essere conosciuto, quindi dobbiamo fornire l'annotazione. Maggiori informazioni nel questa domanda .
Un modo per superare questo è quello di re-implementare reduceLeft
senza il limite inferiore.
implicit def withReduceL[T](xs: Seq[T]) = new {
def reduceL(f: (T, T) => T) = xs reduceLeft f
}
Prova:
scala> Seq(Seq(1,2,3), Seq(2,2,2)) reduceL seqed(_+_)
res1: Seq[Int] = List(3, 4, 5)
Il problema ora è che questo ora non funziona su sottotipi di Seq
(ad esempio List
), con o senza il parametro [Int]
:
scala> Seq(List(1,2,3), List(2,2,2)) reduceL seqed(_+_)
<console>:11: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
Seq(List(1,2,3), List(2,2,2)) reduceL seqed(_+_)
^
reduceL
si aspetta una funzione di tipo (List[Int], List[Int]) => List[Int]
. Poiché Function2
è definito come Function2 [-T1, -T2, +R]
, (Seq[Int], Seq[Int]) => Seq[Int]
non è una sostituzione valida.
Altri suggerimenti
Non è possibile farlo nel modo desiderato, ma guarda Function.tupled
, che è una contro-parte a .tupled
che risolve questo stesso problema.
scala> List(1, 2, 3) zip List(1, 2, 3) map (_ + _).tupled
<console>:8: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
List(1, 2, 3) zip List(1, 2, 3) map (_ + _).tupled
^
<console>:8: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
List(1, 2, 3) zip List(1, 2, 3) map (_ + _).tupled
^
scala> List(1, 2, 3) zip List(1, 2, 3) map Function.tupled(_ + _)
res7: List[Int] = List(2, 4, 6)
Sono abbastanza sicuro che sono chiedere troppo. Inferenza di tipo a Scala va da sinistra a destra , in modo che il tipo di bisogni (_+_)
per essere capito prima anche considerando la parte .sedeq
. E non c'è abbastanza informazioni là.