Thanks. I feel things get more interesting when you’re dealing with more realistic examples.
I feel that in Elixir at least these cases can be handled with structs:
def server(info, something, %State{
write: write,
queue: %Queue{
origin: origin,
monitorref: monitorref,
reply: reply
}
}) do
do_something_with(origin, monitorref, reply)
end
def server(_, _, _) do
:nothing
end
now there’s no way I can call server
with a queue which is not a %Queue{}
and hit the catchall clause. Again my question, is queue
part of flow-control? In my opinion it is.
But then I can see that rewriting it like this:
def server(info, something, %State{
write: write,
queue: queue
}) do
%Queue{
origin: origin,
monitorref: monitorref,
reply: reply
} = queue
do_something_with(origin, monitorref, reply)
end
It’s telling me more clearly: “I just expect a %State{}
with write
and queue
”. So we made the “flow-control” part less specific and less verbose I guess. I can live with this version of the code.
But then one could say: “hey, being a struct, %State{}
will always have write
and queue
that’s the point of being a struct. So matching those fields is not “flow-control”, it’s variable extraction”. Let’s rewrite:
def server(info, something, %State{} = state) do
%State{queue: queue} = state
%Queue{
origin: origin,
monitorref: monitorref,
reply: reply
} = queue
do_something_with(origin, monitorref, reply)
end
Ok, now I guess the flow control part is reduced to a minimum. But I confess I don’t like this version, I liked the first one the most because I could clearly see what the function was expecting and using just by looking at the head.
All this to say that I find a lot of these observations useful but also subjective, and the distinction between flow-control and something else is a bit arbitrary IMHO. I guess you could argue that stripping the head of matches improves readability, but that’s also subjective: I personally find the first version to be the most readable, if properly formatted as I posted it.