Reputation: 572
you can do the following in ruby:
l = [[1, 2], [3, 4], [5, 6]]
m = l.map {|(a, b)| a+b}
but you can not do the following in scala:
val a = List((1, 2), (3, 4), (5, 6))
a.map((f, s) => f + s)
<console>:9: error: wrong number of parameters; expected = 1
a.map((f, s) => f + s)
instead you have to do this:
a.map { case (f, s) => f + s }
I find this rather wordy, since scala defines a "tuple" type, I was expecting it to also provide the syntactic sugar on top of it to match implicitly like above. Is there some deep reason why this kind of matching is not supported? Is there a more elegant way of doing this?
Upvotes: 3
Views: 247
Reputation: 10711
If you have more time, you can even do the following with implicits:
val _1 = { t: { def _1: Int } => t._1 }
val _2 = { t: { def _2: Int } => t._2 }
implicit class HighLevelPlus[A](t: A => Int) {
def +(other: A => Int) = { a: A => t(a) + other(a) }
def *(other: A => Int) = { a: A => t(a) * other(a) }
}
val a = List((1, 2), (3, 4), (5, 6))
a map _1 + _2
Another possibility with monads and the keyword for
and yield
:
val a = List((1, 2), (3, 4), (5, 6))
for((i, j) <- a) yield i + j
but this one might not be the solution you prefer.
Upvotes: 4
Reputation: 52691
The reason is that the syntax you are trying to use already has a meaning. It is used when the higher order function expects a two-argument function. For example, with reduce
or fold
:
List(1,2,3).reduce((a,b) => a+b)
The cleaner way can be achieved by defining your own implicit method:
import scala.collection.generic.CanBuildFrom
import scala.collection.GenTraversableLike
implicit class EnrichedWithMapt2[A, B, Repr](val
self: GenTraversableLike[(A, B), Repr]) extends AnyVal {
def mapt[R, That](f: (A, B) => R)(implicit bf: CanBuildFrom[Repr, R, That]) = {
self.map(x => f(x._1, x._2))
}
}
Then you can do:
val a = List((1, 2), (3, 4), (5, 6))
a.mapt((f, s) => f + s) // List(3, 7, 11)
There are some other tricks you can do, like using tupled
, but they doesn't really help you with the situation you described:
val g = (f: Int, s: Int) => f + s
a.map(g.tupled)
Or just
a.map(((f: Int, s: Int) => f + s).tupled)
Upvotes: 5
Reputation: 3294
you can use _1
, _2
properties of Tuple
for desired effect
scala>val a = List((1, 2), (3, 4), (5, 6))
a: List[(Int, Int)] = List((1,2), (3,4), (5,6))
scala>a.map(x => x._1 + x._2)
res2: List[Int] = List(3, 7, 11)
Upvotes: 0
Reputation: 33093
Is there some deep reason why this kind of matching is not supported?
Yes, because in Scala, the syntax
(f, s) =>
means an anonymous function that takes 2 arguments.
Is there a more elegant way of doing this?
(Somewhat tongue-in-cheek answer.) Use Haskell, where \(f, s) ->
actually means a function that takes a tuple as an argument.
Upvotes: 2