OlivierBlanvillain
OlivierBlanvillain

Reputation: 7768

What's up with immutable.Map.map?

How does immutable.Map.map work? It looks like there is something wrong with the documentation:

def map[B](f: (A) ⇒ B): Map[B]

[use case] Builds a new collection by applying a function to all elements of this immutable map.

Full Signature

def map[B, That](f: ((A, B)) ⇒ B)(implicit bf: CanBuildFrom[Map[A, B], B, That]): That

Upvotes: 1

Views: 82

Answers (1)

Sascha Kolberg
Sascha Kolberg

Reputation: 7152

The understandable confusion stems from the fact that map is not implemented in Map, but in TraversableLike. However, the function documentation in inherited by sub classes.

TraversableLike takes two type parameters TraversableLike[+A, +Repr] and the map function has the signature TraversableLike.map[B](f: (A) ⇒ B): Traversable[B]. In the api docs of Map the documentation for that is inherited and partly adjusted Traversable[B] by Map[B], but B from TraversableLike is not resolved to (A, B) form Map.

(Might be a bug in Scaladoc, that probably won't be fixed, as there could be an erasure problem. But that's just guessing on my side.)

You can check what is actually implemented in Map if you configure visibility of members right above the members documentation.

EDIT:

Now to your core question:

If the documentation would give us, what we could read intuitively

and if we simplify it a bit for readability

and if we than further use a bit more of natural language instead of acronyms, the signature of map for a Map[A, B] could look like:

map[ResultItem, ResultCollection](f: (A,B) => ResultItem)(implicit bf: CanBuildFrom[Map[A, B], ResultItem, ResultCollection]): ResultCollection

So, basically you apply a function to each key-value-pair of the Map that transforms a key-value-pair of type (A,B) into a value of type ResultType.

As you can build almost any kind of collection from a map (or any other collection), this result type does not have to be another tuple. And ResultCollection does not have to be another Map. E.g.:

Map("1" -> 1, "2" -> 2).map((keyValuePair: (String, Int)) => keyValuePair._2)

or short

Map("1" -> 1, "2" -> 2).map(_._2)

has List(1, 2) as result, List[Int] as ResultCollection, Int as ResultItem.

This is possible because of the implicit CanBuildFrom parameter that adds an implicit builder that takes the result of the map function and appends it to its builder-result. In most cases CanBuildFrom is inferred by the compiler. However, there are cases when it will not be able to infer the proper result collection.

In such cases you have to give the compiler more information:

val test2: Vector[Int] = Map("1" -> 1, "2" -> 2).map(_._2)(collection.breakOut)
val test3: Set[Int] = Map("1" -> 1, "2" -> 2).map(_._2)(collection.breakOut)

For more information on breakOut and CanBuildFrom I'd recommend this answer

Upvotes: 4

Related Questions