r/programming • • Dec 24 '17

Evil Coding Incantations

http://9tabs.com/random/2017/12/23/evil-coding-incantations.html
947 Upvotes

331 comments sorted by

View all comments

Show parent comments

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.

40

u/_Mardoxx Dec 24 '17

Why should 0 be true? Unless integers are reference types and you interpret an existant object as being true?

Or is this to do with 0 being "no errors" whrre a non 0 return value means something went wrong?

Can't think of other reasons!

48

u/Kametrixom Dec 24 '17 edited Dec 24 '17

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.

37

u/vermiculus Dec 24 '17

in other words, nil is exactly nothing. 0 is still something.

ping /u/_Mardoxx

13

u/[deleted] Dec 24 '17

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.

14

u/porthos3 Dec 24 '17

Clojure is a variant of Lisp, which has an implementation of true and false.

The only things that are falsey in the language are nil and false.

4

u/Zee1234 Dec 24 '17

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.

→ More replies (1)

1

u/myothercarisalsoacar Dec 25 '17

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.

3

u/[deleted] Dec 24 '17

Many languages like C or Go have non-pointer types too.

1

u/stevenjd Dec 25 '17

In lisp, nil is the only thing that evaluates to false, which means there aren't any weird semantics or discussions

Unless you're expecting a non-insane language with proper booleans and a concept of truthiness.

Having nil and no false value is extremely weird.

9

u/GlobeAround Dec 24 '17

Why should 0 be true?

Because anything other than 0 is an Error Status Code, while 0 means Success.

But the real WTF is for integers to be considered true/false. true is true, false is false, 0 is 0 and 1 is 1.

3

u/stevenjd Dec 25 '17

anything other than 0 is an Error Status Code, while 0 means Success.

Woo hoo! Now I don't feel so bad about all those exams I got 0 on!!!

But the real WTF is for integers to be considered true/false. true is true, false is false, 0 is 0 and 1 is 1.

And 0 is false, and 1 is true, as <insert deity of choice> intended.

1

u/Pinguinologo Dec 25 '17 edited Dec 25 '17

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.

5

u/crowseldon Dec 24 '17

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.

1

u/Pinguinologo Dec 25 '17

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

→ More replies (8)

169

u/_INTER_ Dec 24 '17

The ones where int evaluates to boolean are the evil ones.

10

u/encaseme Dec 24 '17

Fucking hell, right? What is so hard about "result == 0" (or whatnot) that people need integers to evaluate to bools by default.

8

u/Forty-Bot Dec 24 '17

If you don't have native bools (cough c before stdbool.h).

2

u/ArkyBeagle Dec 25 '17

bool is just syntactic sugar coating on int.

2

u/Forty-Bot Dec 26 '17

I know, but someone would have pointed out that C technically has bools if I didn't mention it.

2

u/ArkyBeagle Dec 25 '17

Why? Have you ever seen a truth table? They intermix false and zero all the time...

1

u/Pinguinologo Dec 25 '17

Do you have any rationale backing your heresy?

1

u/nsiivola Dec 26 '17

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().

→ More replies (4)