Better design pattern for creating a polymorphic API

The best way in Elixir I could see is a mix of your original approach, and behaviours.

  1. Behaviour:
defmodule ExAws.Iam.Consumer do
  @callback consume_response(String.t, String.t) :: any # (xml, action)
end
  1. Implementers:
defmodule User do
  @behaviour ExAws.Iam.Consumer
  def consume_response(xml, action), do: #...
end
  1. Refactor dispatch to use router functions like so:
defp consumer(action) when action in @user_actions, do: User
defp consumer(action) when action in @access_key_actions, do: AccessKey

#...

defp dispatch(xml, action), do: consumer(action).consume_response(xml, action)

Code is not tested. But IMO this brings you close enough to some practical polymorphism while remaining terse enough.

(Another alternative would be to have a big module where you match on all possible combos but I don’t think it would be an improvement over your current approach – except maybe for having less source files.)