pathikrit
pathikrit

Reputation: 33479

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 of these 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 a map followed by flatten can always be replaced by flatMap, but although the above line compiles, this does not:

data flatMap Function.tupled(f)

Upvotes: 5

Views: 1417

Answers (2)

Tom Westmacott
Tom Westmacott

Reputation: 31

To further the very helpful answer above, if you use collect, you can then go a step further and rewrite your function f as a Partial Function:

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

You can then process your data thus:

data collect f

In general, any function that returns an Option can be re-written as a partial function. In some cases this comes out neater as you have fewer case expressions and don't have to wrap the return values in Some().

Upvotes: 0

Ben Reich
Ben Reich

Reputation: 16324

You can only use flatMap when returning Options because there is an implicit conversion from Option to Iterable by the implicit option2Iterable. The method flatMap on your List[(Int, Int)] expects a function from (Int, Int) to GenTraversableOnce[Int]. The compiler is having trouble identifying that implicit conversion as a viable option here. You can help the compiler along by explicitly specifying your generic 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 might also allow the compiler to pick the correct types and implicits, even without the explicit generics:

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

Finally, you might also want to play with collect together with unlift here, which can be a nice way to express this logic as well:

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

Function.unlift takes a method that returns an Option and turns it into PartialFunction that doesn't match where the original function would return None. collect takes a partial function and collects the values of a the partial function if it is defined at each element.

Upvotes: 2

Related Questions