Credence.Pattern.PreferMapPutNew (credence v0.7.1)

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 if condition is Map.has_key?(map, key) (or a single ! negation)
  • The "key exists" branch returns map unchanged
  • The "key absent" branch calls Map.put(map, key, value)
  • Both branches reference the same map and key
  • key and value are 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
  end

Good

new_map = Map.put_new(map, key, value)