Why is zero not falsy in Clojure? (2015)
stackoverflow.com
stackoverflow.com
My own thoughts is that what values should be falsey in a language depends on what your language is supposed to manipulate. C is a language designed for mucking about with machine data: bits, numbers, and pointers. And those are the data types that can be sensibly used in a boolean context in C. The Lisp family is designed for processing lists (it's in the name), and lists are the only type that some Lisps let be falsey (although many don't even allow that). It seems sensible to me that a fundamentally object-oriented language would treat a null object as falsey, or the empty string in a language for text processing. I dislike it when multi-paradigm languages have many falsey values.
You're pushing 'back-ported' a bit much. It's just a blatant equivalence that 0 is a conceptual false, when a zero caused a particular behaviour if used in an if/while/etc.
'false' as a concept has always been in C afaik whether it had that name (via #DEFINE) or not.
For example, in Swift
let f: Float = 1.0
let d: Double = f
is invalid code. You have to write let f: Float = 1.0
let d: Double = Double(f)
In such languages, true is true, false is false, and all other values are neither true nor false.I think that, certainly for booleans, that’s the sanest choice.
In any case, the next thread down is making the same point, and specifically calling out languages that do exactly this and get it right: Clojure, Lua, Ruby, and others.
I very much disagree.
Every type should have a false value.
Note that False is a bool while None/Null are pointers/references and Nil is Lisp's empty list. (Python's empty list is [] and it also has empty tuples.) Why should those types and no others have a false?
If a type is not a boolean already, the only way to get a boolean out of it is by using a comparison function.
But better yet, `if` should only accept a boolean expression. Passing a string to `if` should be a type error.
The purpose of type checking is to help people write correct programs.
It's unclear that requiring "if {string}.isempty() ..." does that better than "if {string} ..." in a strongly-typed language.
The entire concept of "falsey" makes my teeth ache.
Non-boolean expressions in a boolean context is asking for a runtime error.
And yet, it doesn't, in strongly-typed languages that define which values are "false" and "true" for types such as strings, sets, dictionaries, and yes, even numbers.
How many checked luggage items do you have?
nil means the user hasn't answered the question
0 means they saw the question and don't have any checked luggage.
To my mind this is an incredibly boneheaded mistake in a modern language. Common Lisp can be excused as it is old and has an even older ancestry. nil has been false in common lisp and its ancestors since forever, but this is an accident of history, and nobody claims it to be a good idea, as it is a source of many bugs. And indeed some lisp functions have to have additional gizmos added to them to work around it.
Scheme fixed this with real true and false constants. And then clojure, um, decided to combine the worst of both worlds, by making TWO things be false.
Citation needed.
I worked with two languages that have exactly this same behavior (Clojure and Ruby), and both of them this is not the source of any bug that I can remember [1].
Also, I would argue that Clojure as a language is much more ergonomic thanks to `nil` being `falsey`, so this is all about the trade-offs between safety and ergonomics. Considering that, IMO, Clojure without nil being falsey would lose much more than Clojure being a more safe language, I really don't seem much problem.
[1]: well, there is one, where one of our internal tooling had this function called `assoc-if` that it would assoc a key to a map if truthy, however this also meant that when the key was of boolean type and false we would also not assoc this; but we eventually introduced `assoc-some` that does what you expect.
Lua gets this right, `nil` is the Ground State of Being, you get it back in lieu of a value any time you try to find one.
This requires also having a `false` because logical conditions have to be streamed sometimes and you can't transport a key with a nil value by definition.
But an empty list/table being 'false' is convenient right until it isn't. It's a kludge.
I know of no other JVM-based lisp system which makes this "tradeoff". Indeed Kawa, which is rather faster than Clojure in my experience, does not.
Which leads to this link: https://groups.google.com/g/clojure/c/OnagUrQZ1NE/m/Uwm8fvak...
Where Rich comments on the thinking behind this.
Also see https://clojure.org/reference/lisps , which compares Clojure with other Lisp dialects on this and other points
Caveats and boundaries: It is typically clear whether something can return a nil or accepts a nil value one versus when it doesn't. A good example would be string methods and other Java interop, where you will make sure you don't pass in a nil value, you are crossing language boundaries here so to speak. Another boundary of nil usage would be sequences, which will not evaluate to falsy, even if empty, so you explicitly would call seq or empty? on them.
The bottom line is that you are very aware of nil values and their falsiness in Clojure and that you program with them rather than against them.
Yup. It took a decade of ES updates to overcome these issues with Javascript. The falsy zero in JS has probably created more bugs in aggregate than any other language feature in history.
"Values are falsey if they are `nil` or `false`, otherwise they are truthy".
It's hard for me to map this rule to 'disaster', either in principle or in practice. It means that `if number then` does what you expect, if you expect that maybe number exists, maybe it doesn't.
It’s a bit optimistic that we’d ever have most languages working under the same rules though, no matter how sane it is and how dubious the value of falseyness (and the terseness you can get from it) is.
What's the absolute minimum level of mental imposition on an if statement? I'd say it's "this must take a boolean", but that implies compile-time types, and I'm not here to litigate the mere existence of dynamic languages today.
I would argue that the rule I sketched above is both the clearest and simplest rule which is compatible with a dynamic language. It imposes the least amount of mental burden in understanding or writing code.
if user = db.query(id)
user.update(...)
end
and user = db.query(id) || raise "UserNotFound" user = db.query(id) || raise("UserNotFound")
Or use the `or` keyword: user = db.query(id) or raise "UserNotFound"Instead of changing the boolean rules, they should definitely address the different precedence rules for boolean operators : )
in dynamic languages, wanting to take advantage of brevity tricks with number zero or empty list or empty string being falsy can seem like a good idea, but gets confusing fast- different languages are going to have different falsy values, and you can easily get caught off guard when legitimately wanting to accept an 'empty' value
common lisp has NIL as its definition of the end of a list, which ends up working out fine since lists are so deeply embedded in the language design that it's hard to forget that they're there (any other kind of object is 'truthy', including any size of regular array)
scheme has a separate #f that is taken as the only falsy value, which is probably also fine
python and javascript are a mess, with different kinds of 'empty' or 'nothing' values being falsy whether you want them to be or not, which is the kind of helpfulness we don't need- where evaluating in boolean context means "if this value is present but not 'empty', in a way that is dependent on the objects type, and may change over time"
Pattern matching in Elixir is so powerful, why would you even bother?
Count yourself lucky
Edit: Thinking about it, this is probably a frame-shift from static to dynamic languages, because in the latter any value can be nil/null at any time. Whereas in C, C++, and even Java etc, unboxed numbers are always numbers. So coming from those you may not be used to thinking about the case where you have a nullable number and mean to check for null and accidentally also check for 0. But in dynamic languages like Clojure, it's a common situation to have a nullable number.
Ruby also does this, nil and false are the only falsey values.
Funny enough, NULL::bool = false in postgres is NULL - because of course it is.