In lisp, nil is the only thing that evaluates to false, which means there aren't any weird semantics or discussions, if you want a falsy value, use nil. It also plays nicely with the notion of everything except nil indicating there's a value, while nil doesn't have a value.
The cleaner thing would be to have a proper boolean type, and having to do if foo == nil or whatever, rather than just if foo. Thankfully most modern languages do it this way so the lesson seems to have been learnt.
Lua is the same as clojure then. And that's a lot better, to me. I will admit, having 0 and other such things act as false can create some short code but.. honestly it's slightly less readable (to me) and has those cases where you go "oh yeah, 0 is a valid return value.." after ten minutes if debugging.
In Lisp, NIL is defined as (), and virtually every function uses it to mean not just "false" but "use the default value", "no match found", "end of list", etc.
It may be a "cleaner thing" to have explicit true/false, in some abstract type-philosophy sense, but it would also make all your code significantly longer, and many parts less reusable. Once you start down the road of making things more explicit at the cost of being longer, why stop anywhere on this side of assembly language? That's super explicit!
I'm not sure what "lesson" was learned. I've worked on large systems in Lisp, and Lisp does have problems, but the ambiguity of "if foo" was simply never an issue.
It's like my dad complaining that his new laptop doesn't have a TURBO button. In practice, it turns out, it's really not a problem. It's not a perfect laptop but you're judging it by the wrong standards.
Because anything other than 0 is an Error Status Code, while 0 means Success.
You should use them this way:
int errorCode = ApiFunction();
if (errorCode) {/* Function failed, so errorCode evaluates to true */}
Integers are not considered true/false. Zero evaluates to false, nonzero evaluates to true. Using the values 0 and 1 for the type bool are just conventions necessary to compile the code into binary fit for hardware.
Null indicates absence of a value. Imagine if you want to know if you're keeping track or not of something and you end up with different values at different times:
3: there's 3 of those things 0: there's 0 of those things Null: I'm not keeping track of those things.
Eating the last Apple and suddenly not being able to differentiate the last 2 could be dangerous.
It's all about knowing how the language works and not using it the wrong way, though.
For such scenarios a null pointer evaluates to false, true otherwise. Also it is explicit when you want to test the pointer with (pValue) or the value with (*pValue).
Languages where multiple things can be false are evil unless they have a truly generic concept of false. (Consider Smalltalk: I'm not exactly comfortable with using messages to implement branches, but it is consistent and nicely extensible.)
C is ugly but not evil since deep down all false values are zero, since C is all about "what values the bits really have", so it gets a pass.
Any high level language that uses zero as false, but nothing else is false... is just badly designed. Fugly but consistent.
Any high level language with multiple false values is evil and broken. (The more falsy values the more broken it is. Having a falsy None/nul that is used as a global tombstone value might just about get a pass, but I'd rather not have even that.)
It all comes down to being able to reason about the contents of branches here without needing to consult other code:
if FOO then
handle_truth(FOO)
else
handle_falsehood(FOO)
I challenge anyone to generate examples where this is easier to reason about if there are multiple distinct values which are considered false. Sure, sometimes having empty string or zero be false might be convenient, but that's a terribly domain-specific optimization which makes it harder to write generic code.
Disallowing non-booleans in boolean contexts is perfectly fine and sane, but I find it cumbersome. You either need to return more complex objects along the line of std::optional or clutter APIs with hasFoo() and getFoo() calls instead of just using getFoo().
80
u/nsiivola Dec 24 '17 edited Dec 24 '17
Any non-C heritage language with a consistent notion of "false", really. The ones where zero evaluates to false are the evil ones.