Elixir is far too generous. I realize how cossetted I am when I go to use function clauses in JavaScript. Because you can’t do that.
I personally detest if
statements, along with case
— perhaps to a fault. Function clauses redeem me.
Why?
Because there is lawtable by which I abide:
Let the complexity of our calculations accord with the data upon which they operate.
And what might I mean?
I mean that, ideally, our code should not introduce additional complexity. If, for example, our data stores a value that will never be nil
, our code need not account for a case in which the value is nil
.
Hence, I love function clauses: more effectively than other control structures, they delegate handling by virtue of data values.
Example Using Elixir Function Clause
Let’s assume our web-app has a table of results to which a diversity of filters can be applied. We want to enable users to delimit results by age, email address, and username:
# In this case, we _will_ account for `nil`, as it represents a common scenario
# when assembling search queries.
defp filter({_key, nil}, %Ecto.Query{} = query), do: query
defp filter({:age, age}, %Ecto.Query{} = query), do: query |> ...
defp filter({:email, email}, %Ecto.Query{} = query), do: query |> ...
defp filter({:username, username}, %Ecto.Query{} = query), do: query |> ...
@spec apply_filters(keyword({atom(), String.t()}), Ecto.Query.t()) :: Ecto.Query.t()
def apply_filters(filters, %Ecto.Query{} = query) do
Enum.reduce(filters, query, &filter/2)
end
Which can be used like:
iex> apply_filters([age: 18, email: "myname@example.com"], query)
Further Reading
- Elixir: Modules & Functions
- Philip Brown. Multi-clause Functions with Pattern Matching and Guards in Elixir.
Read the Full Series
A Compilation of Jonathan’s Favorite Functions and Patterns in Functional Programming.
We're building an AI-powered Product Operations Cloud, leveraging AI in almost every aspect of the software delivery lifecycle. Want to test drive it with us? Join the ProdOps party at ProdOps.ai.