user1639848
user1639848

Reputation: 572

why does scala not match implicitly on tuples?

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

Answers (4)

Mika&#235;l Mayer
Mika&#235;l Mayer

Reputation: 10711

More alternatives

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

dhg
dhg

Reputation: 52691

the reason

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)

a solution

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)

other alternatives

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

Konstantin
Konstantin

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

Robin Green
Robin Green

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

Related Questions