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.

5 Likes

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

4 Likes

Thank you @NobbZ and @DevotionGeo!

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

1 Like

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

3 Likes

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.

5 Likes
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:

1 Like

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"))

https://clojure.org/guides/learn/flow#_cond

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.

8 Likes

Good point!
I like :else more than true.

3 Likes

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

5 Likes