Exploring with, the Elixir special form
2016-05-27 by Marcos Almonacid
This is my short exploration of
with, the new Elixir special form introduced in
If we check the documentation, it says that
with is used to combine matching clauses. And if all the clauses match, the
do block is executed, returning its result. Otherwise the chain is aborted and a non-matched value is returned:
The documentation also says: variables bound inside
with won't leak, and also it allows "bare expressions":
This looks cool. But when should we use it? There are some situations where using
with is a good idea. I'm going to mention 2 of them.
Replacing nested case statements
Let's say we have a function to setup a socket that listens on a specific port, accept a connection on this socket, and wait for incoming packets. We could have something like:
server/2 has 2 nested case's. We can rewrite it using
This new implementation is shorter and more readable.
with can also be used to validate data before doing something with this data.
Let's say we want to create a new user in our database by storing it in our database; but in order to do so, we have to run some validations over the data.
If we use
with, we could write something like:
The code looks pretty simple.
We run every validation before calling
persist/1. If one of the validations returns something different than
:ok, for example,
}, the chain will be aborted and
create/1 will return the error tuple.
(We can implement
create/1 in a few different ways. Using
with is just one more)
What about the 'Let it crash' culture?
with is a nice special form, our code looks simple and readable when we use it. But I'm an Erlang developer, so if something doesn't return the result that I'm expecting, the process running that code must die and its supervisor might restart it (depending on the restart strategy).
We could say that
MatchError crashes. So that, using
with is kind of using
try/catch statements. Well, I think that's not really true. Because whatever
with returns is going to be an expected result, regardless if it's a successful response or an error response. For instance, if we call our
create/1 function and it returns
}, we might want to do something with that error (log it, send a notification, etc) before finishing the process.
This new special form is a good tool to write simple code when it's used in the right situation. As any other tool, trying to use it everywhere would be a mistake.
It doesn't hide
MatchError crashes, it simply has different possible returns. So it's not breaking the 'Let it crash' rule. For example, we know that our
create/1 function returns
}, if we only want to accept
} we simply match the result to it:
And as a downside, I would say that I'm starting to get a little "scared" about seeing new special forms, because they provide new ways to do something that we are already doing. And having multiple ways to do the same thing makes me remember Ruby and its "infinite" alternatives to write the same logic, which sometimes makes me feel that I'm not able to choose the right one. Anyway, Elixir doesn't have that problem (yet). So, all in all, I can say I'm enjoying this new special form.Go back to the blog