woohaaha
Why was equal sign used in with statement?
I read an article on the Dockyard blog and I’m wondering why they used an equal sign with a with statement.
The code example is:
defmodule CMS.PageController do
def update(conn, %{"id" => id, "page" => page_params}, current_user) do
with page = CMS.get_page!(id),
:ok <- Authorizer.authorize(:update, page, current_user),
{:ok, page} <- CMS.update_page(page, page_params) do
conn
|> put_flash(:info, "Page Updated")
|> redirect(to: cms_page_path(conn, :show, page)
end
end
end
Why was page = CMS.get_page!(id) used instead of <-?
Most Liked
felix-starman
with is a macro that evaluates a set of expressions and the <- symbol is a hint to that macro that the right-hand side will either match the left, bind any unbound variables, and then continue to the next expression, or it won’t match, and it will follow the else clause matching if present, or just return the unmatched clause. But if you don’t use the <- then it’s just a typical expression, and it will run it, and do the next thing.
Using a = inside a with is usually a hint that either the expression will always pass (e.g. string = "asdf:" <> some_known_value) or that you want it to raise an error if it something is wrong. In this case because the right side of the = is CMS.get_page!/1 and ! is typically used when you want to raise if something isn’t found, I’d expect that get_page!/1 will raise an error if it doesn’t find the page, so we can assume that page = ... will always pass (if it doesn’t raise an error). If it didn’t raise an error and returned {:ok, page} but the author wanted it to raise in that scenario they could change it to {:ok, page} = CMS.get_page(id),
woohaaha
Thank you everyone!!!
@bennelsonweiss @focused @Sebb @felix-starman
Such a nice implementation in the original example

focused
I think it should be:
with {:ok, page} <- CMS.get_page(id),
:ok <- Authorizer.authorize(:update, page, current_user),
{:ok, page} <- CMS.update_page(page, page_params) do
...
or
page = CMS.get_page!(id)
with :ok <- Authorizer.authorize(:update, page, current_user),
{:ok, page} <- CMS.update_page(page, page_params) do
...
The code with page = CMS.get_page!(id) raises in case of error even if it would be outside of with, so it has no reason to be placed inside that with clause (it does not need to be inside that with).
bennelsonweiss
It’s probably not a mistype at all. You don’t need to handle everything in a with, and in their case they’re not handling anything in with.
Since they’re not handling anything in a with everything is falling through, whether it falls through as a throw or as a returned error, and my educated guess is that since we’re looking at a controller they’re:
- Relying on the fallback controller they would no doubt register with
action_fallbackto handle the return value of failing to matchauthorizeorupdate_page - Relying on
Plug.Exception’s coercion ofEcto.NoResultsError(as one would expectget_page!to raise) to turn that error into a 404 response
So they are using = because they aren’t trying to match on the value, they just expect it to work or raise.
It’s contrived, but
with :ok <- validate(params),
changeset = Thing.create_changeset(%Thing{}, params),
{:ok, thing} <- Repo.insert(changeset) do
Logger.info("I created thing #{thing.id}")
{:ok, thing}
end
When I created the changeset, it’s gonna work no matter what, so I use = instead of <- because there’s nothing to match on.
In your original question they did they same because they don’t care what get! returns because either it’ll return what they want or it’ll raise.
bennelsonweiss
The simplest reason is that it lets you define a clear sequence of execution for functions that may fail without needing to nest conditional checks.
In the example you’re fetching a page (page may not exists), then authorizing the current user to interact with that page (user may not be authorized), then updating the page (update may be invalid).
You could nest three ifs, “if page is not found fail, else if user is not authorized fail, else if page cannot be updated fail, else put flash and redirect”, but then all the error handling obscures what you’re actually trying to achieve.
with allows you to structure it as “successfully get the page, then ensure the user is authorized, then update the page, then put flash and redirect”. That way the happy path is clear to see, but you haven’t ignored the fact that things can go wrong because you’re still checking for that, just not handling it right there.
You could just as easily not put it in the with, but there’s a non-technical reason you might want to put it in there.
Since the with is defining the logic for some process, in this case how you update a page, and getting a page is a fundamental part of the process as they’re defining it, then including it in the with puts all the steps of the process at the same level so that it’s clear to understand each stage of the process.
It’s a decision entirely aimed towards trying to present something clearly for the developer, and it might not always be the clearest choice given a team’s conventions.
Everything defined between with and do is available in the do block (but not in the else block, if there is one), and if you reach the do block it means there were no errors before.







