Languages I’m familiar with, C/C++, Python, will at least consider 0 falsey. Python goes further to consider any empty collection falsey. I’m curious why in Elixir only false
and nil
as falsey
My take:
I think the best way to answer this is to think from the other direction: Why is anything other than false
considered falsy?
The absolute simplest thing is that only false
is falsy. This is the easiest to understand, but not necessarily the most expressive. Every non-false
value added to the falsy-list makes conditions more difficult to understand, so there should be an argument for each one that outweighs any arguments against, plus the inherent complexity of anything not false
being falsy!
Let’s consider the empty list:
The biggest argument for, in my mind, is that it (arguably) simplifies certain conditionals in code traversing lists. So you may theoretically have:
def process(list) do
if list do
# list is non-empty
else
# list is empty
end
end
But we already have structural pattern matching and multiple function heads, so the above could be written as either:
def process([]) do
# list is empty
end
def process(list) do
# list is non-empty
end
# or
def process(list) do
case list do
[] -> # list is empty
non_empty -> # list is non-empty
end
end
This means that empty-list falsiness does not provide an expressive benefit over existing tools in the language, and shouldn’t be included. The same can be said for 0
, ""
, {}
.
An additional argument against empty-collection falsiness is that, unlike Python, Elixir does not support operator overloading, so user-defined collections could never share the same behavior as basic collections. (And there are even built-in collections like the :queue
module from Erlang that are implemented using composite basic collections, and would not be able to be falsy here.)
To sum up: there is a strong argument that the expressiveness provided by expanded falsiness is already available using other language features, and there are benefits to keeping conditionals simple.
Never been a fan of this.in some programming languages.
It is not that 0
is “falsy” in C, it is the only possible way to express what haskell would call False
.
C++ has kept this solely for compatibility reasons, despite the fact that they could restrict if
and similar to “true” booleans.
Python even has __bool__()
magic method, which allows you to define your own understanding of “truthiness” and “falsyness”, which in my past has caused me hour long debugging sessions.
We went as far, that we introduced coding rules and static analyzing enforcing them, that we always had to explicitley check for a variables type to be boolean before actually testing whether its true.
- We used mypy for typing.
- we forbid to
if foo:
iffoo
was not typedbool
- we forbid to use
bool(x)
for anyx
- for any
x
that could bebool
by spec but was allowed to other types as well, we required ax is bool
check before. - for any other type we required explicit equality checks
As annoying those rules were when we introduced them, after we got used to them and applied this rule without exception, we spends much less time fixing bugs and issues that boiled down to “weird falsyness”.
During studies, I quickly learned, foo != NULL
is so much easier to read than !foo
and the compiler will treat both the same anyway regarding the emitted assembly.
At the same time foo == NULL
vs foo == 0
signals the additional intend of “this is pointer” and “this is number”.
And as C only knows about integers and floats in a variety of sizes and does not know any other type anyway, it is a rather bad example in this regard.
I consider it a pity that there are truthy and falsy values besides false
and true
in elixir and consider it a leftover of the ruby heritage.
Personally I prefer a case
that strictly tests for true
and false
over a sloppy if
. The last 2 times I entered a new team this lead to an initial discussion but no styleguide that prefers mine or their implementation, at the end both are used across the code, depending on who wrote the particular section. But there are at least no edit wars.
The reason we enforce neither has been the same in both teams: No one had a credo rule for one or the other.
Both teams agreed on the potential pain with slipping values. Though in both teams I have been confident (at the time of the discussion) that there would be no way to non actual bools slipping into the condition.
I even prefer and/2
/or/2
to write the conditions rather than &&/2
/||/2
! Because I consider a logged crash more valuable than a missbehaving condition.
Agreed with case, while if
allows you to skip the else clause, this also means that you will inevitably have places where you forget to define the else clause by mistake and have random nil
s going throughout your codebase that are hard to track.
Too bad @dimitarvp’s post was flagged, it was good comedy, but I get it
I actually have the contrarian view. For me the fact that only false
and nil
are falsey is exactly what makes truthiness viable. I otherwise found it incredibly chaotic in other languages. Of course, you have to be judicious about it and expect that everyone involved in a project will be as well, which I suppose is not always possible. I’m used to working in very small teams and pairing a lot so
In any event, I only use truthiness if I’m going to do something with the checked value. For example:
if field = get_field(changeset, :field) do
put_changeset(changeset, :field, "#{field} plus new stuff")
else
changeset
end
But let’s say I wanted to change the value of a field based on the presence of other another field then I would write it like so:
if get_field(changeset, :field) != nil do
put_change(changeset, :other_field, "new value")
else
changeset
end
I dunno, I’ve never personally run into any problems with this. I also just prefer how if
reads sometimes. This:
case foo do
true -> "this"
false -> "that"
end
always felt a bit weird to me and makes me pause to think there might be a missing clause. Although that is my dynamic-language-oriented brain at play there. I do see the merit in blowing up if you get nil
instead of false
so perhaps I’m shaky ground here. But again, it’s one of those things where I’ve just never felt the pain.
I can just say that coming from Erlang I definitely prefer true
to be the only truthy value and false
to be the only falsey value. It is MUCH simpler and consistent in the long run. I’m a KICASS (Keep It Consistent And Simple Stupid) person.