Reputation: 51109
Say I have a method that turns a (function on two elements) into a (function on two sequences):
def seqed[T](f: (T,T) => T): (Seq[T], Seq[T]) => Seq[T] = (_,_).zipped map f
In words, the resulting function takes two sequences xs
and ys
, and creates a new sequence consisting of (xs(0) f ys(0), xs(1) f ys(1), ...)
So, for example, if xss
is Seq(Seq(1,2),Seq(3,4))
and f
is (a: Int, b: Int) => a + b
, we can invoke it thus:
xss reduceLeft seqed(f) // Seq(4, 6)
or with an anonymous function:
xss reduceLeft seqed[Int](_+_)
This is pretty good; it would be nice to get rid of the [Int]
type argument but I don't see how (any ideas?).
To make it feel a bit more like the tupled
method, I also tried the enrich-my-library pattern:
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)
For a pre-defined function this works great, but it's ugly with anonymous ones
xss reduceLeft f.seqed
xss reduceLeft ((_:Int) + (_:Int)).seqed
Is there another way I can reformulate this so that the types are inferred, and I can use syntax something like:
// pseudocode
xss reduceLeft (_+_).seqed // ... or failing that
xss reduceLeft (_+_).seqed[Int]
? Or am I asking too much of type inference?
Upvotes: 5
Views: 304
Reputation: 51109
The reason why a type annotation is required in
xss reduceLeft seqed[Int](_+_)
but not in
xs zip ys map Function.tupled(_+_)
is due to the difference in type requirements between map
and reduceLeft
.
def reduceLeft [B >: A] (f: (B, A) ⇒ B): B
def map [B] (f: (A) ⇒ B): Seq[B] // simple version!
reduceLeft
expects seqed
to return type B
where B >: Int
. It seems that therefore the precise type for seqed
cannot be known, so we have to provide the annotation. More info in this question.
One way to overcome this is to re-implement reduceLeft
without the lower bound.
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)
The problem now is that this now doesn't work on subtypes of Seq
(e.g. List
), with or without the [Int]
parameter:
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
expects a function of type (List[Int], List[Int]) => List[Int]
. Because Function2
is defined as Function2 [-T1, -T2, +R]
, (Seq[Int], Seq[Int]) => Seq[Int]
is not a valid substitution.
Upvotes: 0
Reputation: 297185
You can't do it the way you want, but look at Function.tupled
, which is a counter-part to .tupled
that solves this very same problem.
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)
Upvotes: 5
Reputation: 170735
I am pretty sure you are asking too much. Type inference in Scala goes from left to right, so the type of (_+_)
needs to be figured out first before even considering the .sedeq
part. And there isn't enough information there.
Upvotes: 4