Let's pretend we are writing some Ruby code, and we need a function that takes two optional arguments, returning "happy path" if either are present, otherwise returning "red herring" if both are nil:
-- CODE line-numbers language-rb --
<!--
def some_func(one, two)
if one || two do
puts one
puts two
return "happy path"
end
"red herring"
end
-->
### Now imagine a coworker who is troubleshooting this function for the first time:
Without the benefit of our description above, they might expect that "red herring" is actually the normal, expected return value of this method. The code technically works, but our intention could be clearer.
Using **early returns**, we can refactor this, so that the happy path is within the normal linear flow of the method, and the special case is the one surrounded by additional logic:
-- CODE line-numbers language-rb --
<!--
def some_func(one, two)
if one.nil? && two.nil?
return "red herring"
end
puts(one)
puts(two)
"happy path"
end
-->
The above function expresses our intent: i.e. "happy path" is the normal intended usage of this function, and "red herring" is some special case, when the conditions of the function are not met.
## There is no return in Elixir!!!
Let's try a naive "early return" implementation in Elixir:
-- CODE line-numbers language-elixir --
<!--
def some_func(one, two) do
if !one || !two do return "red herring" end
IO.puts(one)
IO.puts(two)
"happy path"
end
-->
At this point, the compiler yells at you, telling you that this is not valid Elixir.
## Pattern matching to the rescue
Instead of dealing with the execution conditions inside the function, we can handle them in entirely separate **function clauses**:
-- CODE line-numbers language-elixir --
<!--
def some_func(nil, _), do: "red herring" end
def some_func(_, nil), do: "red herring" end
def some_func(one, two) do
IO.puts(one)
IO.puts(two)
"happy path"
end
-->
## Guards for the win
The above code still contains duplicate definitions when either argument is nil. We can combine these definitions into one using a **guard clause**, which allows more complex checks to be performed, on top of pattern matching:
-- CODE line-numbers language-elixir --
<!--
def some_func(one, two) when is_nil(one) or is_nil(two), do: "red herring" end
def some_func(one, two) do
IO.puts(one)
IO.puts(two)
"happy path"
end
-->
The code snippet above expresses the "happy path" of the function, as well as what specific execution conditions we are pulling out, along with action taken if they match.
## Other paradigms and techniques
When thinking about the different types of values our functions must accept and return, and communicating this intent to other developers, Elixir is full of options:
#### The key question I would ask myself in this situation is:
"What do I want this code to communicate to other developers?"
If I want to communicate that _"supplying two nil values is part of the normal execution flow of this function"_, then I would use `case` or `cond` and return the values inside of a pattern match clause.
On the other hand, if I want to communicate that _"supplying two nil arguments is outside the domain of this function"_, then a function clauses with guards might be just the ticket!
## Elixir resources
Download our free guide to begin implementing feedback loops in your organization.
By filling out this form, you agree to receive marketing emails from Headway.
In this free video series, learn how the best startup growth teams overcome common challenges and make impact.
In this free video series, learn how the best startup growth teams overcome common challenges and make impact.
In this free video series, learn proven tactics that will impact real business growth.
By filling out this form, you agree to receive marketing emails from Headway.
Dive deeper into the MoSCoW process to be more effective with your team.
By filling out this form, you agree to receive marketing emails from Headway.
In this free video series, learn the common mistakes we see and give yourself a greater chance for success.
By filling out this form, you agree to receive marketing emails from Headway.
Everything you need for a killer DIY audit on your product.
See all the ways we can help you grow through design, development, marketing, and more.
Level up your skills and develop a startup mindset.
Stay up to date with the latest content from the Headway team.