Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return statement in Elixir

I need a function with some kind of a step-by-step logic and I wonder how I can make one. Let's take a log in process on a site as an example, so I need the following logic:

1) Email is present? Yes -> Go on; No -> Return an error

2) Email has at least 5 characters? Yes -> Go on; No -> Return an error

3) Password is present? Yes -> Go on; No - Return an error

And so on ...

And to implement this, I would usually use a return statement so that if the email is not present, I quit executing the function and make it return an error. But I can't find something similar to this in Elixir so I need an advice. The only way I can see now is to use nested conditions but maybe there is a better way?

like image 900
NoDisplayName Avatar asked Aug 27 '15 02:08

NoDisplayName


People also ask

Does elixir have return?

There is no return in Elixir!!!

How do I return something to Elixir?

Elixir has no 'break out' keyword that would be equivalent to the 'return' keyword in other languages. Typically what you would do is re-structure your code so the last statement executed is the return value.


1 Answers

This is an interesting problem because you need to perform multiple checks, exit early, and in the process transform some state (connection). I typically approach this problem as follows:

  • I implement each check as a function which takes state as an input and returns {:ok, new_state} or {:error, reason}.
  • Then, I build a generic function that will invoke a list of check functions, and return either the first encountered {:error, reason} or {:ok, last_returned_state} if all checks succeeded.

Let's see the generic function first:

defp perform_checks(state, []), do: {:ok, state} defp perform_checks(state, [check_fun | remaining_checks]) do   case check_fun.(state) do     {:ok, new_state} -> perform_checks(new_state, remaining_checks)     {:error, _} = error -> error   end end 

Now, we can use it as follows:

perform_checks(conn, [   # validate mail presence   fn(conn) -> if (...), do: {:error, "Invalid mail"}, else: {:ok, new_conn} end,    # validate mail format   fn(conn) -> if (...), do: {:error, "Invalid mail"}, else: {:ok, new_conn} end,    ... ]) |> case do   {:ok, state} -> do_something_with_state(...)   {:error, reason} -> do_something_with_error(...) end 

Or alternatively move all checks to named private functions and then do:

perform_checks(conn, [   &check_mail_presence/1,   &check_mail_format/1,   ... ]) 

You could also look into the elixir-pipes which might help you express this with pipeline.

Finally, in the context of Phoenix/Plug, you could declare your checks as a series of plugs and halt on first error.

like image 155
sasajuric Avatar answered Sep 19 '22 17:09

sasajuric