Dealing with private and public function default conflicts

This is one of my favorite patterns, the _guarded suffix. You do all your guard checks in your public function, and you delegate to a private one and call it recursively if needed, without checking for a guard again and increasing performance.

This is the current implementation for Keyword.update/4

  @spec update(t, key, default :: value, (existing_value :: value -> new_value :: value)) :: t
  def update(keywords, key, default, fun)
      when is_list(keywords) and is_atom(key) and is_function(fun, 1) do
    update_guarded(keywords, key, default, fun)
  end

  defp update_guarded([{key, value} | keywords], key, _default, fun) do
    [{key, fun.(value)} | delete(keywords, key)]
  end

  defp update_guarded([{_, _} = pair | keywords], key, default, fun) do
    [pair | update_guarded(keywords, key, default, fun)]
  end

  defp update_guarded([], key, default, _fun) do
    [{key, default}]
  end