I was doing some exercises from exercism.io and in some moment a needed to update a Map, I did and finished the exercise. After a while, I found the function Enum.frequencies/1 that could have helped me on the exercise. I decided open Elixir source code to see implementation of Enum.frequencies/1 that is this:
@doc since: "1.10.0"
@spec frequencies(t) :: map
def frequencies(enumerable) do
reduce(enumerable, %{}, fn key, acc ->
case acc do
%{^key => value} -> %{acc | key => value + 1}
%{} -> Map.put(acc, key, 1)
end
end)
end
Then, the question came to me: There’s any difference between map.put/3 and the built in Map update %{map | “key” => “value”}?
I write my version of Enum.frequencies/1 using Enum.reduce/3, and I replaced %{^key => value} -> %{acc | key => value + 1} by %{^x => value} -> Map.put(acc, x, value + 1) and did work.
I would like to know if there is any reason for Elixir source code to use this syntax %{acc | key => value + 1} on Enum.frequencies/1
Hey, nice to see you are checking source code for solving doubts. Yes there’s a difference, when you update a map using the | operator, that key has to exist already. That is the reason on the case, if the key already exists they just increase the counter by updating the map using the | operator, but if the key is not present yet, the added it using the Map.put/3 function.
But if the key already exists, Map.put/3 can also update the value of this existent key, right? Is there some situation that would be not good to have Map.put/3 instead %{map | “key” => “value”}`?
Yes, Map.put/3 can also update the value of an existent key. I would say that the right comparison here is with Map.update!/3 because using the shorthand update will lead to an update like this function does. So the thing that matters is if the key is present or not, you might want to know when trying to update a map if a certain key is present, because if it does not it can lead into an exception raise which you might be expecting, and using Map.put/3 will simply ignore that condition and set the key to the wanted value.
In erlang, Map#{key => Value} means update or insert, and Map#{key := Value} means update only. In elixir the shorthand for upsert is removed to reduce confusion. So this is also a good example to showcase the difference two languages’ philosophy: Erlang favors logic and completeness, Elixir favors ergonomics and sane default.