Scala Function .tupled vs f.tupled

I have the following Scala code:

def f(x: Int, y: Int): Option[String] = x*y match { case 0 => None case n => Some(n.toString) } val data = List((0, 1), (1, 0), (2, 3), (4, -1)) data flatMap {case (x, y) => f(x, y)} 

But the last line is too verbose, so I tried all this and none of them compiles.

data flatMap f

data flatMap f.tupled

data flatMap Function.tupled(f)

data flatMap {f _}

data flatMap (f _).tupled

data flatMap f(_)

What am I doing wrong? The only thing that works is this:

(data map Function.tupled(f)).flatten

I thought that the map followed by flatten can always be replaced with flatMap , but although the line above compiles, it is not:

data flatMap Function.tupled(f)

+5
source share
2 answers

You can use flatMap when returning Options , because the implicit conversion from Option to Iterable an implicit option2Iterable . The flatMap method on your List[(Int, Int)] expects a function from (Int, Int) to GenTraversableOnce[Int] . The compiler has trouble determining that implicit conversion is a viable option here. You can help the compiler by explicitly specifying your general parameters:

 import Function._ data.flatMap[String, Iterable[String]](tupled(f)) //Or data flatMap tupled[Int, Int, Iterable[String]](f) 

Other formulations of the same idea may also allow the compiler to select the correct types and implicits, even without explicit generics:

 data flatMap (tupled(f _)(_)) data.flatMap (f.tupled(f _)(_)) 

Finally, you can also play with collect along with unlift here, which can be a good way to express this logic:

 data collect unlift((f _).tupled) data collect unlift(tupled(f)) 

Function.unlift accepts a method that returns Option and turns it into a PartialFunction , which does not match the return of the original None function. collect accepts a partial function and collects the values ​​of the partial function, if defined for each element.

+2
source

For a more useful answer above, if you use collect , you can take another step and rewrite the function f as a partial function:

 val f: PartialFunction[(Int, Int), String] = { case (x, y) if x*y != 0 => (x*y).toString } 

Then you can process your data this way:

 data collect f 

In general, any function that returns a parameter can be rewritten as a partial function. In some cases, this is more accurate since you have fewer case expressions and no need to wrap the return values ​​in Some() .

0
source

All Articles