Tipo de inferencia en funciones anónimas con enrich-my-bibrary
-
26-10-2019 - |
Pregunta
Digamos que tengo un método que convierte una (función en dos elementos) en un (función en dos secuencias):
def seqed[T](f: (T,T) => T): (Seq[T], Seq[T]) => Seq[T] = (_,_).zipped map f
En palabras, la función resultante toma dos secuencias xs
y ys
, y crea una nueva secuencia que consiste en (xs(0) f ys(0), xs(1) f ys(1), ...)
Entonces, por ejemplo, si xss
es Seq(Seq(1,2),Seq(3,4))
y f
es (a: Int, b: Int) => a + b
, podemos invocarlo así:
xss reduceLeft seqed(f) // Seq(4, 6)
o con una función anónima:
xss reduceLeft seqed[Int](_+_)
Esto es bastante bueno; Sería bueno deshacerse del [Int]
Escriba argumento pero no veo cómo (¿alguna idea?).
Para que se sienta un poco más como el tupled
Método, también probé el patrón enriquecano-my-bibrary:
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)
Para una función predefinida esto funciona muy bien, pero es feo con los anónimos
xss reduceLeft f.seqed
xss reduceLeft ((_:Int) + (_:Int)).seqed
¿Hay otra forma en que pueda reformular esto para que los tipos se infieran, y puedo usar sintaxis algo así como:
// pseudocode
xss reduceLeft (_+_).seqed // ... or failing that
xss reduceLeft (_+_).seqed[Int]
? ¿O estoy pidiendo demasiada inferencia de tipo?
Solución 3
La razón por la cual se requiere una anotación tipo en
xss reduceLeft seqed[Int](_+_)
Pero no en
xs zip ys map Function.tupled(_+_)
se debe a la diferencia en los requisitos de tipo entre map
y reduceLeft
.
def reduceLeft [B >: A] (f: (B, A) ⇒ B): B
def map [B] (f: (A) ⇒ B): Seq[B] // simple version!
reduceLeft
esperanza seqed
para devolver el tipo B
dónde B >: Int
. Parece que por lo tanto el tipo preciso para seqed
no se puede conocer, por lo que tenemos que proporcionar la anotación. Más información en esta pregunta.
Una forma de superar esto es volver a implementar reduceLeft
sin el límite inferior.
implicit def withReduceL[T](xs: Seq[T]) = new {
def reduceL(f: (T, T) => T) = xs reduceLeft f
}
Prueba:
scala> Seq(Seq(1,2,3), Seq(2,2,2)) reduceL seqed(_+_)
res1: Seq[Int] = List(3, 4, 5)
El problema ahora es que esto ahora no funciona en subtipos de Seq
(p.ej List
), con o sin el [Int]
parámetro:
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
espera una función de tipo (List[Int], List[Int]) => List[Int]
. Porque Function2
Se define como Function2 [-T1, -T2, +R]
, (Seq[Int], Seq[Int]) => Seq[Int]
no es una sustitución válida.
Otros consejos
No puedes hacerlo como quieres, pero mira Function.tupled
, que es una contraparte para .tupled
Eso resuelve este mismo 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)
Estoy bastante seguro de que tu son Pidiendo demasiado. Tipo de inferencia en Scala va de izquierda a derecha, entonces el tipo de (_+_)
necesita ser resuelto primero antes de considerar el .sedeq
parte. Y no hay suficiente información allí.