-1

I'm new to Scala. In my previous post someone had provided a piece of code which I'm failing to understand properly.

type JsonMap = Map[String, Any]

def getMapDiffs(mapBefore: JsonMap, mapAfter: JsonMap) : (JsonMap, JsonMap) = {
  val sameKeys = mapBefore.keySet intersect mapAfter.keySet
  val startAcc = (Map.empty[String, Any], Map.empty[String, Any])
  sameKeys.foldLeft(startAcc){ case (acc @ (deltaBefore, deltaAfter), key) =>
    (mapBefore(key), mapAfter(key)) match {
      // two maps -> add map diff recursively to before diff and after diff
      case (beforeMap: Map[_, _], afterMap: Map[_, _]) =>
        val (deltaB, deltaA) = 
          getMapDiffs(beforeMap.asInstanceOf[JsonMap], afterMap.asInstanceOf[JsonMap])
        (deltaBefore + (key -> deltaB), deltaAfter + (key -> deltaA))
      // values before and after are different
      // add values to before diff and after diff
      case (beforeValue, afterValue) if beforeValue != afterValue =>
        (deltaBefore + (key -> beforeValue), deltaAfter + (key -> afterValue))
      // keep existing diff  
      case _ => acc
    }  
  }
}

Can someone explain to me what case _ => acc is doing? If anyone wants to explain all the code in depth that will work too, since I'm trying to get a handle on Functional Programming.

Cheers!

Community
  • 1
  • 1
krzasteka
  • 427
  • 1
  • 4
  • 14
  • 1
    Are you asking about `_` or `@`? The title says one thing, but the post says another. – Rex Kerr Jul 17 '15 at 19:28
  • @Rex Kerr, that was unclear. I guess my question should've been: How does `case _ => acc` interact with `(acc @ (deltaBefore, deltaAfter)`. I've researched and found a [post](http://stackoverflow.com/questions/2359014/scala-operator) which explains the @ symbol. But I can't make sense of it in this context. – krzasteka Jul 17 '15 at 20:58
  • `(acc @ (deltaBefore, deltaAfter)` matches a tuple of two Maps. The tuple is extracted and its contents are bound to `deltaBefore` and `deltaAfter`. However, in the code you also need the tuple itself to pass on as is to the next iteration. You use the @ to also bind the tuple iteself to a local val. So the case above could also be written as `case acc => val (deltaBefore, deltaAfter) = acc ...`. Update my answer to include some of this. – Sascha Kolberg Jul 18 '15 at 06:46
  • @ Sascha, thank you for clarifying. – krzasteka Jul 18 '15 at 21:03

2 Answers2

5

First to the question, though I don't get what @ in the title is supposed to mean.

The _ is a wildcard in scala. So, in the code above

case _ => acc

matches all cases not matched by the cases before. In the code above that is all values from mapBefore and mapAfter with the same key, that do not differ. Therefore the result of the previous fold cycle is kept, while in the other cases pairs of different values are added.

Now about the rest of the code (I'm not the author, but I can guess):

type JsonMap = Map[String, Any]

Is a type alias defined just for conciseness and convenience.

def getMapDiffs(mapBefore: JsonMap, mapAfter: JsonMap) : (JsonMap, JsonMap) = {
  val sameKeys = mapBefore.keySet intersect mapAfter.keySet
  val startAcc = (Map.empty[String, Any], Map.empty[String, Any])

samekeys is a Set of keys that are defined in both input maps startAcc is the start value for the upcoming foldLeft.

sameKeys.foldLeft(startAcc){ case (acc @ (deltaBefore, deltaAfter), key) =>

foldLeft takes a start value and then traverses through the collection it has been called on. For each item of the collection the result of the last step and the current element of the collection are given as input to the fold function. In the first step the start value replaces the result.

The function call could be rewritten to:

sameKeys.foldLeft(startAcc){ inputTuple: ((JsonMap, JsonMap), String) =>

However, as you would then have to access the tuples content in rather cumbersome and hardly readable way (e.g. inputTuple._1._2 to get the second map of the previous result), developers make use of the fact that we can directly match inputs for fold, map, flatMap and the like functions. Thus

case (acc @ (deltaBefore, deltaAfter), key) =>

to bind the contents of the two tuples to readable local vals and the tuple itself to acc (that's what the @ is for). This way we can conveniently use the two maps as well as the complete result tuple of the last iteration, which is especially useful in the default case (case _ => acc)

(mapBefore(key), mapAfter(key)) match {

retrieves the values from both input maps for the same current key and starts a match.

case (beforeMap: Map[_, _], afterMap: Map[_, _]) =>
    val (deltaB, deltaA) = 
      getMapDiffs(beforeMap.asInstanceOf[JsonMap], afterMap.asInstanceOf[JsonMap])
    (deltaBefore + (key -> deltaB), deltaAfter + (key -> deltaA))

matches values that are themselves Maps and applies the recursive function to get all differences of these maps and add them to the result of this fold step.

case (beforeValue, afterValue) if beforeValue != afterValue =>
    (deltaBefore + (key -> beforeValue), deltaAfter + (key -> afterValue))

matches all other values of both input maps that are NOT equal and adds them to the result of this fold step.

So in the end you should get two Maps with the same keySet and all values that are different in the original two input maps.

Sascha Kolberg
  • 7,092
  • 1
  • 31
  • 37
2

Every pattern matches to case _ =>. It's like else in if-else or default case in java's switch block. In your code if (mapBefore(key), mapAfter(key)) doesn't match

(beforeMap: Map[_, _], afterMap: Map[_, _]) or

(beforeValue, afterValue) if beforeValue != afterValue

it will always match to _ and code after case _ => will be executed.

ka4eli
  • 5,294
  • 3
  • 23
  • 43