
I am a horse in the land of booleans - tosh
https://iloveponies.github.io/120-hour-epic-sax-marathon/I-am-a-horse-in-the-land-of-booleans.html
======
jefftk

        Because Java’s if does not return a value,
        you cannot say:
    
          return if (x < 0) "-" else "+";
    

This would have been a nice place to make an analogy to the question mark
operator, since a java programmer would probably be familiar with it and it
allows you to write:

    
    
        return if (x < 0) "-" else "+";
    

as

    
    
        return x < 0 ? "-" : "+";

~~~
felipemnoa
>>java programmer would probably be familiar with it

And also C, C++, and Objective-C programmers. I'm sure there are more
languages that use that operator that I have not mentioned here.

~~~
scardine
I have not touched PHP in the last 10 years but if memory serves it also has
the ternary operator. Ok, lets try it:

    
    
        $ php -a
        Interactive mode enabled
        php > echo 1 == 1 ? "foo" : "bar";
        foo
        php > echo 1 == 2 ? "foo" : "bar";
        bar
    

Looks like javascript has it too:

    
    
        1 == 1 ? "foo" : "bar"
        "foo"
        1 == 2 ? "foo" : "bar"
        "bar"

~~~
amenghra
Be careful, php’s ternary operator precedence is different from (most) other
languages’ (consult binding direction).

The use of parenthesis is highly encouraged...

------
dmix
> if does not have a return value in a language like Java. In other words, it
> is not an expression, but a statement. Because everything in Clojure is an
> expression, there is no equivalent construct to Java’s if in it.

Anyone know the language design rationale behind the way Java does it? It
seems much easier to make everything an expression. Ive always disliked that
part of Javascript and being forced to use ternary operators to do a single
line return statement for a conditional. Same with case statements.

~~~
zwkrt
If your control structures are expressions then they can lead to code that the
maintainers of the language might not want to promote. Remember that language
design is not just about being as flexible as possible. If it were we would
have stopped with Lisp. Languages are also designed with readability,
portability, and simplicity in mind.

Imagine if you could write this (nonsense) code in Java:

bool a = (while (b) { if (c) { b=d;} else { b=e; }});

While it is efficient, some may balk at how “implicit” it is.

~~~
ACow_Adonis
Of course I'd say this as a lisper, but as long as the return values of
everything is intuitive and well defined, I don't see an inherent problem with
such form.

Indeed, if anything, if the intention is to communicate that the Boolean value
is the result of some process that runs through a while loop, then explicitly
saying that in the assignment seems to me to almost be the best way to do it
:\

ymmv.

Now deeply nested while/assignment/ifs, that's a bad-pattern, but I'm not
inherently convinced that's an expression/statement problem.

------
zshrdlu
Clojure seems to have replaced the "p"[0] suffix in predicates with "?", the
former being an old Lisp convention. Interesting choice, I'm mildly miffed
they didn't go with the old convention.

[0]
[http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec...](http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/fun_zerop.html)

~~~
greggyb
Question mark is a Scheme tradition. Clojure is a new lisp, drawing
inspiration from many sources.

One of Rich Hickey's goals, stated in many ways and in many of his
presentations, was to design a modern lisp not bound by design decisions in
old ones. That's why Clojure is not built on or directly based on any specific
lisp.

He has made a big point about moving on from lisp-isms such as `car` and
`cdr`, which are based on tradition and specific archaic hardware
implementation, to modern and within-language-consistent things like `first`
and `rest`, which are both self-descriptive and implemented for anything that
conforms to the seq protocol, as opposed to `car` and `cdr`, which are tied to
cons cells.

I do not recall anything specific that he's said about about the choice of a
question mark vs a "p", but I can imagine a similar argument that a question
mark is more self-descriptive than "p". "p" only makes sense if you know lisp
traditions.

Edit: typo

~~~
Smithalicious
"p" makes sense if you know that it stands for "predicate". It's not nearly as
arcane as car and cdr.

Also can we finally settle on a name for these things now? If car and cdr are
too arcane we can do away with them (though I like being able to do caddadadr)
but why do we need "first" and "rest" rather than the already established
"head" and "tail"? I particularly dislike "rest" since it's a relative term,
i.e. in normal speech saying that you're going to do something with "the rest"
of the list means something different depending on how much of the list you
already used.

~~~
puredanger
In Clojure, first and rest apply to sequences, which are a logical list
abstraction. They apply to lists, but they can also be used on sequential
views of indexed vectors, maps, sets, database result sets, files in a dir,
lines in a file, and an open world of "things that can be seen in some order".
head and tail I suspect are much more tied to linked lists and data structure
than the more plain first and rest.

~~~
lonelappde
If you are using first and rest, then you are treating whatever sequence you
have as a linked list, so you've already lost the plot.

~~~
greggyb
You _can_ treat any collection or data structure that implements the sequence
protocol as a lazy sequence with

    
    
        (seq collection)
    

But you don't _have_ to. If you choose to, you're not tied to a representation
of mutable cons cells. There are good reasons to want to iterate over values
in a map or a set, while still wanting the lookup characteristics of those
collections, rather than a linked list.

This is hardly a controversial stance. Python, for example, supports iteration
over many of its data structures and it is recommended to implement the dunder
methods for iteration on any class that might reasonably be the basis of an
iteration.

All clojure does is agree that iteration over a lazy sequence is a valuable
concept. It implements this as an abstract protocol, rather than as a concrete
implementation on linked lists.

------
asplake
I’m used to 0 and [] being falsy. Upsides and downsides to Clojure’s choice
here?

~~~
SEMW
The trouble is that `if(value)` or `value && ..` ends up getting used as an
idiomatic shorthand for "if value is present" even in languages (such as
javascript and python) where 0 is falsey. Because most of the time it works,
so people do it. And then inevitably get bugs when the value happens to be 0.

This can get increasingly hard to reason about the more datatypes you add
which interpret 0-ish values as falsey -- as there's always the temptation to
do, for 'consistency' with integer truthiness, e.g.
[https://lwn.net/Articles/590299/](https://lwn.net/Articles/590299/) about the
python behaviour of dates being falsey in the first second after midnight.

If `null` and `false` are the only falsey values (ala clojure, ruby, elixir
etc), that's a rule that's really easy to internalise and reason about, so you
never have to worry about things like whether your data type might be falsey
in the first second after midnight (and also `if(values)` is actually a
correct idiom).

~~~
raverbashing
I disagree with this rationale (not with your specific explanation, with the
rationale)

You can make your types be interpreted as True/False (in Python at lest) as
you want. The issue here is that a date should never be "false".

> that's a rule that's really easy to internalise and reason about

Python's rule is simple, it's JS that came up with confusing rules.

The issue here are types that are inconsistently false (and I disagree with
the Python module on that)

~~~
mikeash
"The following values are considered false:

* None

* False

* zero of any numeric type, for example, 0, 0L, 0.0, 0j.

* any empty sequence, for example, '', (), [].

* any empty mapping, for example, {}.

* instances of user-defined classes, if the class defines a __nonzero__() or __len__() method, when that method returns the integer zero or bool value False."

This really does not strike me as being simple.

I personally don't find these shortcuts to be worthwhile. The best approach is
that boolean is a separate type, true is true, false is false, and any attempt
to use any other type as the predicate of a control flow statement is a
(preferably compile-time) error. Writing `if x != 0` is not a large burden.

~~~
raverbashing
> I personally don't find these shortcuts to be worthwhile

They are, because they are the difference between driving with the parking
brake on or not. Other languages don't have this so that's why people might
not see the value in it first.

> Writing `if x != 0` is not a large burden

If you're comparing what can only be an int value, then it's not a burden.

When your variable can assume multiple values, explicitly comparing it with
multiple possibilities _is_ a burden:

Examples:

\- Your variable is optional and/or is a sequence that might be empty

\- You're using .get() in a dictionary

One of the signs someone is unfamiliar with Python is doing multiple
comparisons when only `if x:` would suffice

~~~
mikeash
I've worked in a lot of different languages, from ones that work the way I
prefer all the way to Python-style languages where anything is a predicate and
there are a bunch of semi-arbitrary rules about what qualifies as "false."
This isn't a lack of familiarity talking.

Regarding your examples, having an optional sequence is probably not the
correct move anyway. Is there actually a semantic difference between no
sequence and an empty sequence? If not, the variable should be non-optional.
If so, then glossing over those differences by writing `if x` to implicitly
check both conditions is unclear. Not sure what you're referring to with get()
in a dictionary.

I understand that good Python style is considered to be one where you take
advantage of the language's notion of truthiness, but I disagree with its
whole approach.

~~~
raverbashing
> Is there actually a semantic difference between no sequence and an empty
> sequence?

It really depends on your function, but you might want to have the function
behave differently if given an empty list rather than no list. One very common
case for doing that is when you want to facilitate unit testing of a function.

> Not sure what you're referring to with get() in a dictionary.

A very common case, in which you don't care if the element is not present or
it is a "False" element.

~~~
mikeash
Behaving differently on an empty list than on no list is a huge smell. That's
really weird and unexpected behavior. Doing it for testing is even weirder.

If you're fetching an element from a dictionary and want not-present to be the
same as some false value, fetch with a default value (in Python, pass a second
parameter to get()) that you want to see when there's nothing present. Or just
extract the value and check for nil or empty separately, nothing wrong with
being explicit.

