Maybe Map.replace should lazily evaluate the new value, or introduce Map.replace_lazy?

43 views
Skip to first unread message

Ary Borenszweig

unread,
Oct 3, 2017, 8:51:53 AM10/3/17
to elixir-lang-core
Hi!

I have a Map and I want to replace the value associated to a key with something that depends on that value, so that can only work if that value does exist in the Map. For example:

~~~
map = %{1 => 2}
key = 1
Map.replace(map, key, map[key] + 2) # %{1 => 4}
~~~

Now, `key` is not known to me but I'm sure that if it's present in the `map` I will be able to perform the operation on the value (so `map[key]` will always be a number, in this example). If `key` is not present then I will get an error:

~~~
map = %{1 => 2}
key = 3
Map.replace(map, key, map[key] + 2) # (ArithmeticError)
~~~

So I'm thinking maybe there should be Map.replace_lazy (there's already Map.get_lazy, Map.pop_lazy and Map.put_new_lazy). I also checked other Map functions that have a `fun` as a last argument and none allow to me to the above. There's `Map.update(map, key, initial, fun)` but I don't want an initial value, I just want to update the value if the key exists.

Another alternative it to let Map.replace be a macro so that the value can be unquoted but only if the key is present.

Tallak Tveide

unread,
Oct 3, 2017, 2:00:43 PM10/3/17
to elixir-lang-core

Ary Borenszweig

unread,
Oct 3, 2017, 2:40:27 PM10/3/17
to elixir-lang-core
I saw it, but it's different from what I want. There are two versions:
- get_and_update(map, key, fun): fun is called with the current value under key in map (or nil if key is not present in map) -> I don't want that nil, it will break my code
- get_and_update!(map, key, fun): raises if there's no key

I want something like this:

~~~
Map.put_lazy(map, key, fun value -> value + 1 end)
~~~

Here, the function is only invoked if key is present in map. `get_and_update` would invoke that function with `nil`.



On Tuesday, October 3, 2017 at 3:00:43 PM UTC-3, Tallak Tveide wrote:
https://hexdocs.pm/elixir/Map.html#get_and_update!/3

Greg Vaughn

unread,
Oct 3, 2017, 3:21:54 PM10/3/17
to elixir-l...@googlegroups.com
Is it so bad to write a simple helper function?

def update_if_exists(map, key, fun) do
case Map.has_key?(map, key) do
false -> map
true -> Map.update(map, key, nil, fun)
end
end

-Greg Vaughn
> --
> You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/1164ddd8-de7d-4e0d-b140-63db6b6b6646%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Martin Svalin

unread,
Oct 3, 2017, 3:39:16 PM10/3/17
to elixir-l...@googlegroups.com
I'm unclear what you want the proposed function to return when the key isn't present. The map, untouched? I think that behaviour would be a surprise to some. I like the explicitness of `Map.get_and_update/3`. 

`Map.get_and_update(map, key, fn
  nil -> map
  n -> n + 1
end)`

-- 
- Martin

--

Norbert Melzer

unread,
Oct 3, 2017, 3:57:05 PM10/3/17
to elixir-l...@googlegroups.com
For me the requirement does sound much more like `Map.update/4`…


```
Map.update(map, key, 1, &(&1 + 1))
```

But if this is really correct behaviour, we can't know, since the OP only said what happens when `key` is not in the map, but not what he would have expected instead…

Tallak Tveide

unread,
Oct 4, 2017, 1:57:47 PM10/4/17
to elixir-lang-core
I agree the function is missing. The name shoul be different than your suggestion, maybe ‘update_existing(...)’?

Still the alternatives, though not as efficient perhaps, should give not big practical problems. So adding this might be considered margin

Perhaps even the get_and_update is the most optimized solution?

Tallak
Reply all
Reply to author
Forward
0 new messages