Question

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?

Was it helpful?

Solution 3

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.

OTHER TIPS

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)

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top