L'inférence de type sur les fonctions anonymes avec Enrich-my-bibliothèque
-
26-10-2019 - |
Question
Say I ai une méthode qui transforme un (fonction sur deux éléments) dans une (fonction de deux séquences):
def seqed[T](f: (T,T) => T): (Seq[T], Seq[T]) => Seq[T] = (_,_).zipped map f
En d'autres termes, la fonction résultante prend deux séquences xs
et ys
, et crée une nouvelle séquence consistant en (xs(0) f ys(0), xs(1) f ys(1), ...)
Ainsi, par exemple, si xss
est Seq(Seq(1,2),Seq(3,4))
et f
est (a: Int, b: Int) => a + b
, nous pouvons invoquons ainsi:
xss reduceLeft seqed(f) // Seq(4, 6)
ou avec une fonction anonyme:
xss reduceLeft seqed[Int](_+_)
Ceci est assez bon; ce serait bien de se débarrasser de l'argument de type [Int]
mais je ne vois pas comment (des idées?).
Pour faire sentir un peu plus comme la méthode tupled
, j'ai essayé aussi le modèle d'Enrich-ma bibliothèque:
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)
Pour une fonction prédéfinie cela fonctionne très bien, mais il est moche avec les anonymes
xss reduceLeft f.seqed
xss reduceLeft ((_:Int) + (_:Int)).seqed
Y at-il une autre façon, je peux reformuler cela pour que les types sont inférées, et je peux utiliser quelque chose comme la syntaxe:
// pseudocode
xss reduceLeft (_+_).seqed // ... or failing that
xss reduceLeft (_+_).seqed[Int]
? Ou que je demande trop d'inférence de type?
La solution 3
La raison pour laquelle une annotation de type est nécessaire
xss reduceLeft seqed[Int](_+_)
mais pas dans
xs zip ys map Function.tupled(_+_)
est due à la différence dans les exigences de type entre map
et reduceLeft
.
def reduceLeft [B >: A] (f: (B, A) ⇒ B): B
def map [B] (f: (A) ⇒ B): Seq[B] // simple version!
reduceLeft
attend seqed
retourner le type B
où B >: Int
. Il semble donc que le type précis de seqed
ne peut pas être connu, donc nous devons fournir l'annotation. Plus d'infos sur cette question.
Une façon de surmonter ce problème est de re-mettre en œuvre reduceLeft
sans la limite inférieure.
implicit def withReduceL[T](xs: Seq[T]) = new {
def reduceL(f: (T, T) => T) = xs reduceLeft f
}
Test:
scala> Seq(Seq(1,2,3), Seq(2,2,2)) reduceL seqed(_+_)
res1: Seq[Int] = List(3, 4, 5)
Le problème est maintenant que cela ne maintenant pas le travail sur les sous-types de Seq
(par exemple List
), avec ou sans le paramètre [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
attend une fonction de type (List[Int], List[Int]) => List[Int]
. Parce que Function2
est défini comme Function2 [-T1, -T2, +R]
, (Seq[Int], Seq[Int]) => Seq[Int]
ne remplace pas valide.
Autres conseils
Vous ne pouvez pas le faire de la façon dont vous voulez, mais regardez Function.tupled
, qui est une contre-partie à .tupled
qui permet de résoudre ce problème même.
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)
Je suis assez sûr que vous sont trop demander. L'inférence de type à Scala va de gauche à droite , de sorte que le type de besoins de (_+_)
à être compris d'abord avant même de considérer la partie .sedeq
. Et il n'y a pas assez d'informations.