Why in the cond we must use true as the final condition? Why can't we use _?

For example the following,

iex(16)> cond do
...(16)>  2 + 2 == 5 ->
...(16)>   "Not true"
...(16)>  2 * 2 == 3 ->
...(16)>   "nor this"
...(16)>  _ -> 
...(16)>   "will match"
...(16)> end
** (CompileError) iex:21: invalid use of _ inside "cond". If you want the last clause to always
 match, you probably meant to use: true ->

Beacause _ is only valid in a match and cond is only expanding to a bunch of nested ifs.

In other words you’re not matching something, but are checking which statement is true.

Thank you @NobbZ and @DevotionGeo!

This is the reason I love Elixir community. Two responses in less than 10 minutes.

Hehe, you were lucky to catch me during my morning routine and first coffee :wink:

I understand that you are only referring to a mental model - but as you know in Elixir a literal if is actually implemented a case expression, while cond is it’s own special form (so if taken literally this could be confusing).

Essentially the first expression that evaluates to a truthy value “wins” and consequently the expression to the right of that -> is evaluated and becomes the value of the cond expression. In a sense the cond expression is enclosing a sequence of condition -> expression constructs.

Why can’t we use _?

cond by design

Raises an error if all conditions evaluate to nil or false .

so

it may be necessary to add a final always-truthy condition (anything non- false and non- nil )

_ would imply that any value is OK.

In the end it makes the design of cond simpler
(in the true sense of the word - if ... else if ... else .. end is more familiar).

true -> expression

is no different from the other

condition -> expression

before it - i.e. there is no need for a special else part inside cond.

cond do
  x > 0 -> :true
  x < 0 -> :false
  _ -> :wrong
end

Conceptually compiles to:

case [] do
  _ when x > 0 -> :true
  _ when x < 0 -> :false
  _ when _ -> :wrong
end

You can’t have a _ in a guard, and cond is like guards (except you can call any function, linearly run, etc… etc… unlike cases). _ is used for matching, and nothing is being matched.

However, you don’t need to use true, you just need to use anything that is not the atoms :false or :nil, so you can use :else -> etc... or :blah -> etc... or 42 -> etc... or whatever. :slight_smile:

Conceptually

Elixir Guards:

As far as I’m aware those restrictions do not apply to cond conditions.

(except you can call any function, linearly run, etc… etc… unlike case s).

It’s one of those things that took me by surprise with Erlang’s if expression because it’s based on guard expressions which severely restricts the type of conditions you can use.

Yep, I like Erlang’s if as it is simple and easy to reason about without any weird and surprising speed costs. I think cond compiles down to a case tree or something like that in core erlang? I need to check again…

I think this is worth reiterating:

For example, Clojure’s cond works in similar fashion, although the convention there is to use :else instead of true, e.g.,:

(let [x 11]
  (cond
    (< x 2)  "x is less than 2"
    (< x 10) "x is less than 10"
    :else  "x is greater than or equal to 10"))

And :else also works in Elixir:

iex(6)> x = 11
11
iex(7)> cond do
...(7)>   x < 2  -> "x is less than 2"
...(7)>   x < 10 -> "x is less than 10"
...(7)>   :else  -> "x is greater than or equal to 10"
...(7)> end
"x is greater than or equal to 10"

true, :else, "default", etc. are all truthy and therefore serve the purpose.

Personally, I kinda like :else -> or :default -> but the Elixir community appears to have settled on true -> so I’ll use that.

Good point!
I like :else more than true.

If you want to get really fancy with it and make sure that nobody else will understand your code any more, you could even use :_ :stuck_out_tongue_winking_eye:. don’t do this please