Why does this reduce work with this kind of syntax?

Hi everyone,

Would someone have a moment or two to explain what kind of sorcery is happening here?
Why does this reduce work with this kind of syntax?

  def list_users(args) do
    args
    |> Enum.reduce(User, fn
      {:order, order}, query ->
        query |> order_by({^order, :email})
      {:filter, filter}, query ->
        query |> filter_with(filter)
      {:client_filter, client_filter}, query ->
        query |> filter_with(client_filter)
    end)
    |> Repo.all
  end

If I join these lines it looks like this:

… {:order, order}, query -> query |> order_by({^order, :email}) See this here! {:filter, filter}, query -> …

Is there another, more sane way of writing this?

Cheers!

1 Like

It’s an anonymous function with multiple bodies. Just like named functions can have multiple bodies:

def foo([head | rest]), do: ...
def foo([]), do: nil

So can anonymous functions have multiple bodies:

fn
  [head | rest] ->
    ...
  [] ->
    nil
end
2 Likes

But of course it is! It all makes sense now, thanks!

You do not need to have them on separate lines, using a semicolon (;) you can join them on a single line. I’d not suggest to do so very often, its not very good for readability :wink:

iex(1)> f = fn [] -> :empty; [_|_] -> :non_empty end
#Function<6.99386804/1 in :erl_eval.expr/5>
iex(2)> f.([])
:empty
iex(3)> f.([1,2,3])
:non_empty
iex(4)> f.(1)
** (FunctionClauseError) no function clause matching in :erl_eval."-inside-an-interpreted-fun-"/1

    The following arguments were given to :erl_eval."-inside-an-interpreted-fun-"/1:

        # 1
        1

1 Like

More profound knowledge! Didn’t know any of this before. Thank you sir, excellent stuff!