Credence.Pattern.PreferMapPutNew
(credence v0.7.0)
Copy Markdown
Performance and idiomatic code rule: detects an if/unless guard
using Map.has_key?/2 that only calls Map.put/3 when the key is
absent, and suggests Map.put_new/3 instead.
Map.put_new/3 is implemented natively and expresses the intent
directly — "put this key-value pair only if the key is missing."
Only flags when:
- The
ifcondition isMap.has_key?(map, key)(or a single!negation) - The "key exists" branch returns
mapunchanged - The "key absent" branch calls
Map.put(map, key, value) - Both branches reference the same
mapandkey keyandvalueare pure (a bare variable or a scalar literal)
Why the purity restriction
Map.put_new/3 always evaluates its value argument, whereas the
if/else form evaluates value only when the key is absent. If value
could raise or has a side effect, the rewrite would change behaviour when the
key is present — so the rule fires only when value (and key, which the
if form evaluates twice) is a variable or scalar literal that cannot raise
or observe a side effect. Expressions like Map.put(map, key, expensive())
are intentionally left alone (Map.put_new_lazy/3 is the right tool there).
Bad
new_map =
if Map.has_key?(map, key) do
map
else
Map.put(map, key, value)
end
# negated condition with swapped branches:
new_map =
if !Map.has_key?(map, key) do
Map.put(map, key, value)
else
map
endGood
new_map = Map.put_new(map, key, value)