What is the community guidelines on when you should pattern match a function vs when you should write its own? Eg:
Style 1
Extract something to match on and pass all the data down:
def perform_action(%{action: type} = data), do: perform_action(type, data)
def perform_action(:save, data), do: :ok
def perform_action(:delete, data), do: :ok
def perform_action(:revert, data), do: :ok
def perform_action(_, data), do: :error
Pros
- Easier to organize like with like?
- Allows flexibility in what
data
should contain at each handler- Including error handling (checking for required keys, etc)
Cons
- Only one
@doc
block allowed - All functions are public (may not really be an issue)
Style 2
Get type and then call specific:
def perform_action(%{action: type} = data) do
case type do
:save -> perform_save(data)
:delete -> perform_delete(data)
:revert -> perform_delete(data)
_ -> :error
end
end
def perform_save(data), do: :ok
def perform_delete(data), do: :ok
def perform_revert(data), do: :ok
Pros
- Can have per-function
@doc
blocks - Still flexible in what “downstream” functions accept and error handling is local to them
- Probably easier to write “sub match” function headers for each action (eg:
perform_save(%{sneaky: true} = data)
) - Specific functions can be private if needed
- Lists all supported operations in one place?
Cons
- First case could get very large?
Style 3
Extract specific data from top and pass down to functions:
def perform_action(%{action: type} = data) do
case type do
:save -> perform_save(data.id, data.content)
:delete -> perform_delete(data.id)
:revert -> perform_delete(data.id, data.date)
_ -> :error
end
end
def perform_save(id, content), do: :ok
def perform_delete(id), do: :ok
def perform_delete(id, date), do: :ok
Pros
-
@doc
per function - More explicit interfaces, very obvious that save needs
id
+content
.
Cons
- Puts “must have key” checks in first function, which might bloat,
- Though it could match on the type before dispatching to the specific
function (but maybe thats just #1 with more steps…
- Though it could match on the type before dispatching to the specific
From this extremely scientific research, it seems that #2 is maybe the best? Mostly because you can have per-function docs (though if they’re private this doesn’t actually count, @docp
when?) It also seems really common with OTP to just have one named function and match on literally everything too but this is probably more a side effect of needing a generic interface - not intentionally blessed?
I am sure the most appropriate answer is, “well it depends.”, but discarding that, what does the community like to write?