Tuple as argument vs multiple arity

As a beginner Scala programmer I often get confused by argument destructuring. Can someone comment on the best way write a unary lambda function for which I want to destructure the argument. For example. If I want an argument which is a list of lists. ((a:Int, b:Int) (c:Int, d:Int)). That is, a function of one argument, but for which I want to access the restructured components into four variables. Whenever I do this I end up having to fill my code with type declarations which usually exceed the interesting code in the function. Or I often end up writing a helper function, convincing the compiler to be happy with that, then using the function name as an argument to map/flatMap/fold etc…

Here is what I’d like to write, but it is unfortunately :frowning: not correct Scala syntax.
obj.map(((a:Int, b:Int) (c:Int, d:Int))=>(a+b,c+d))

(I may have asked the same question on other forums, but I’m re-asking it here, for searchability reasons and for the benefit of future generations)

You have to use the match case syntax when you want to destructure the argument of a function.
If I understood your example correctly:

scala> val obj =  List(((1,2),(3,4)))
obj: List[((Int, Int), (Int, Int))] = List(((1,2),(3,4)))

scala> obj.map{ case ((a, b), (c, d)) => (a+b, c+d) }
res2: List[(Int, Int)] = List((3,7))

Mind the case and the { }. Scala requires curly braces when writing function literals with case.
That’s actually how you write Partial Functions.

1 Like

Thanks, the case syntax seems indeed to help.
Here is an image from the online Coursera Scala course. Is this a syntax error? The slide follows the case by “:” rather than “=>”. Is it an alternative syntax? Or does it have different semantics?

As far as I can see it does look like a typo.

(By the way I think mapValues is lazy, so it’s not strictly the same as calling map.)
Edit: oh wait, this is Spark. Then ignore what I said about mapValues.

1 Like

case is always followed by =>. That’s a typo in slide.

Note that case is just a pattern match. If your argument does not match the “List of 2 pair tuples” pattern, you must fall through to default case where you say you did not recognize the arg structure.

More on the topic of destructuring, I’ve noticed that the following is syntactically correct.
val (prefix2, suffix2) = s2.span(common)
How does this syntax generalize? Does it only work for tuples? It indeed seems to work for tuples of tuples.
val((a,b),(c,d)) = (s1.span(common),s2.span(common))
And it seems to work for Lists of tuples
val List((a,b),(c,d)) = List(s1.span(common),s2.span(common))

This seems to make the common idiom of foo match { case …} easier.

Indeed destructuring like val (a, b) = someTuple or using a pattern matching is actually the same mechanism. The general rule is: you can do this with any value for which there is an Extractor objects available. And you can combine them: since there is an extractor for List and one for Tuple2 you can write:

val l = List((1, 'a'), (2, 'b'), (3, 'c'))
val List((x0, y0), (x1, y1), (x2, y2)) = l

// Lists also have a `::` extractor to get the head and the tail
val head::tail = l
val (num, letter)::tail = l

l match {
  case (num, letter)::tail =>
    println(num)
    println(letter)
  case _ => println("Woops! The list is empty")
}

Note that scala gives you a extractor for free for case classes which means you can benefit from this for your own classes without any effort. Cool right? ^^

1 Like

I have lots of code I need to refactor now. I’ve been using the idiom

val l = List((1, 'a'), (2, 'b'), (3, 'c'))
...
L match { case List((x0, y0), (x1, y1), (x2, y2)) => { ... }}

And every time I typed that I was thinking, there must be an easier way. Yes Cool.