From the docs,
Raises an error if all conditions evaluate to nil
or false
.
But conceptually a cond
is a more elegant way to write nested if/else
:
assignment =
if first_condition do
first_result
else
if second_condition do
second_result
else
default_result
end
end
assignment =
cond do
first_condition -> first_result
second_condition -> second_result
true -> default result
end
But if
is very much ok without else
:
variable = if condition, do: value
variable
is nil
if condition
is falsy.
But if I want the same with cond
, I must add an ugly and visually polluting true -> nil
in the end
variable =
cond do
condition -> value
true -> nil
end
Since everything is value transformation in Elixir, I love that variable = if condition, do: value
works, and I don’t understand why cond
wouldn’t have the same behavior.
Same goes for case
, though one could argue that in case variable do
, variable
does have a value so it makes sense to want to patterne match it, even with the general case.
Let’s take this from the other side. If you want to raise if a condition doesn’t match you don’t need if
to do so. You can just do true = condition
and it’ll raise if the condition isn’t true. For case
you might have a case where for a system to work correctly one of the given conditions needs to be true. Implicitly adding a true -> nil
case would break this behavior.
Also for if
there’s a fixed number of cases. The condition being true or not a.k.a. the do
block and the else
block. A missing else
block is easy to detect and to fall back to a default behavior. In a case
expression there’s no way to know if existing clauses are exhaustive or not.
3 Likes
…conceptually a cond
is a more elegant way to write nested if/else
Actually cond
is like nested case
because it is compiled to such structure.
Couple of examples, with their erlang representation obtained by Michał Muskała’s decompile
defmodule ClausesCase do
@doc """
The function compiles to erlang like this:
'one?'(_x@1) ->
case _x@1 of
1 ->
<<"one">>;
_ ->
<<"nope">>
end.
"""
def one?(x) do
case x do
1 -> "one"
_ -> "nope"
end
end
end
defmodule ClausesCond do
@doc """
The function compiles to erlang like this:
'one?'(_x@1) ->
case _x@1 == 1 of
true ->
<<"one">>;
false ->
case true of
true ->
<<"nope">>;
false ->
error(cond_clause)
end
end.
"""
def one?(x) do
cond do
x == 1 -> "one"
true -> "nope"
end
end
end
defmodule ClausesIfElse do
@doc """
The function compiles to erlang like this:
'one?'(_x@1) ->
case _x@1 == 1 of
false ->
<<"nope">>;
true ->
<<"one">>
end.
"""
def one?(x) do
if x == 1, do: "one", else: "nope"
end
end
defmodule ClausesIf do
@doc """
The function compiles to erlang like this:
'one?'(_x@1) ->
case _x@1 == 1 of
false ->
nil;
true ->
<<"one">>
end.
"""
def one?(x) do
if x == 1, do: "one"
end
end
To answer the question, if
and cond
are syntactic sugar on top of case
in Elixir, they have different use cases, so they behave slighly differently. Most notably, if
has an implicit else
clause that defaults to nil
.
1 Like
Interesting pattern the true = variable
, never thought to use it.
That said, I’m still unsure of the whole logic behind, but I reckon this might be related to this comment and the difference between “defensive coding” instead of “let it crash”.
For me a “nil
-case” can definitely be under control, it shouldn’t be explicit all-the-time.
But it relatives to deep language philosophy, so we might not find an issue to the question.
Thanks for your replies.