Pasting Josh’s comment on the linked issue for convenience:
The behaviour of Set#map
is quite strange on its own, but especially as an implementation of the Iterable
interface, I think the difference in behaviour between Set’s map and that of other Iterables makes it very questionable what is the use-case of Iterable#map
at all? If you have an Iterable[T]
and you call map
on it, you actually do not know if it is deduplicating or not. I cannot think of a situation in which I would like to map each element of an Iterable[T]
to something, but then I don’t actually care if the output is deduplicated or not.
def foo(it: Iterable[Int]): Iterable[Int] = it.map(_ / 3)
foo(List(1,2)) // List(0, 0)
foo(Set(1, 2)) // Set(0)
The situation is even more bizarre when you look at Map, which can completely change its behaviour based on if you know it is a Map at compile time:
scala> Map(1 -> 1, 2 -> 1).map(_.swap)
res14: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> (Map(1 -> 1, 2 -> 1): Iterable[(Int, Int)]).map(_.swap)
res15: Iterable[(Int, Int)] = List((1,1), (1,2))
I think in an ideal world, and maybe something we can shoot for long term, is a situation where Sets and Maps return Iterable
s from their map methods, and Seq’s continue as they do today, refining their map
output to return Seq
:
scala> Map(1 -> 1, 2 -> 1).map(_.swap)
res14: Iterable[(Int, Int)] = List((1,1), (1,2))
scala> (Map(1 -> 1, 2 -> 1): Iterable[(Int, Int)]).map(_.swap)
res15: Iterable[(Int, Int)] = List((1,1), (1,2))
scala> Set(1,2).map(_ / 3)
res18: scala.collection.immutable.Iterable[Int] = List(0, 0)
scala> Seq(1,2).map(_ / 3)
res19: Seq[Int] = List(0, 0)
I think the use case of mapping Set
=> Set
and mapping Map
=> Map
is such a specific/niche transformation/use-case, that it wouldn’t be so inconvenient to just in that case do set.view.map(...).toSet
and map.view.map(...).toMap
, and leave their implementations Iterable#map
to be consistent and predictable with other Iterables.