Using Tuples in map, flatmap,… partial functions

2020-02-14 08:28发布

问题:

If I do:

val l = Seq(("un", ""), ("deux", "hehe"), ("trois", "lol"))
l map { t => t._1 + t._2 }

It's ok.

If I do:

val l = Seq(("un", ""), ("deux", "hehe"), ("trois", "lol"))
l map { case (b, n) => b + n }

It's ok too.

But if I do:

val l = Seq(("un", ""), ("deux", "hehe"), ("trois", "lol"))
l map { (b, n) => b + n }

It will not work.
Why should I use "case" keyword to use named tuples?

回答1:

The error message with 2.11 is more explanatory:

scala> l map { (b, n) => b + n }
<console>:9: error: missing parameter type
Note: The expected type requires a one-argument function accepting a 2-Tuple.
      Consider a pattern matching anonymous function, `{ case (b, n) =>  ... }`
              l map { (b, n) => b + n }
                       ^
<console>:9: error: missing parameter type
              l map { (b, n) => b + n }
                          ^

For an apply, you get "auto-tupling":

scala> def f(p: (Int, Int)) = p._1 + p._2
f: (p: (Int, Int))Int

scala> f(1,2)
res0: Int = 3

where you supplied two args instead of one.

But you don't get auto-untupling.

People have always wanted it to work that way.



回答2:

This situation can be understand with the types of inner function.

First, the type syntax of parameter function for the map function is as follows.

Tuple2[Int,Int] => B //Function1[Tuple2[Int, Int], B]

The first parameter function is expand to this.

(t:(Int,Int)) => t._1 + t._2 // type : Tuple2[Int,Int] => Int

This is ok. Then the second function.

(t:(Int, Int)) => t match {
  case (a:Int, b:Int) => a + b
}

This is also ok. In the failure scenario,

(a:Int, b:Int) => a + b 

Lets check the types of the function

(Int, Int) => Int // Function2[Int, Int, Int]

So the parameter function type is wrong.

As a solution, you can convert multiple arity functions to tuple mode and backward with the helper functions in Function object. You can do following.

val l = Seq(("un", ""), ("deux", "hehe"), ("trois", "lol"))
l map(Function.tupled((b, n) => b + n ))

Please refer Function API for further information.



回答3:

The type of a function argument passed to map function applied to a sequence is inferred by the type of elements in the sequence. In particular,

scenario 1: l map { t => t._1 + t._2 } is same as l map { t: ((String, String)): (String) => t._1 + t._2 } but shorter, which is possible because of type inference. Scala compiler automatically inferred the type of the argument to be (String, String) => String

scenario 2: you can also write in longer form

l map { t => t match {
    case(b, n) => b + n
  }
}

scenario 3: a function of wrong type is passed to map, which is similar to

def f1 (a: String, b: String) = a + b

def f2 (t: (String, String)) = t match { case (a, b) => a + b }

l map f1 // won't work

l map f2