I am a horse in the land of booleans
iloveponies.github.io
iloveponies.github.io
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 ? "-" : "+";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.
$ 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"The use of parenthesis is highly encouraged...
Sometimes it's called the "conditional operator" but my experience has been that "question mark operator" is what the most people will understand.
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.
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.
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.
Because C did it, because Algol did it, because Fortran did it, because that is how assembly code works. Expressions require code to be compiled to something that uses a temporary value stack (or equivalent, like what some continuation-passing style compilers do by allocating values from garbage-collected memory), which, along with subroutines, was an exotic technology well into the 1960s.
I remember the first time I saw 'constexpr' in C++ I was excited, until I realized it's really conststatement.
The elaborate syntactical decorations of most languages drive away from this, unfortunately.
[0] http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec...
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
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.
This allows for unified abstract views on things normally distinguished from each other by words, syntax, conventions and mind. This allows stuff like pattern matching generic functional code like the mighty monad with it's transformer.
Learning functional programming incl. higher kinded types (dependent types and such) isn't easy for someone used to e.g. Java,C,C++,Python,Go, and others I can't judge of the top of my head.
If you ask yourself why said functional is beneficial, remember the popularity and typical confusion of the Monad posts/articles. You might not quite understand / "get" it, but they keep coming up and the authors seem intelligent. So you (might) just have a case of the Blub Paradox[0]. You need to understand it to grok it's usefulness/desirability.
(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.
We are in agreement here. It seems to me, though, that it is an easy case to make that a question mark makes sense if you know English. Surely this is a convention less dependent upon specific knowledge.
Either way, Clojure is its own language and is not beholden to the tradition of any other language. What is most important is that it chooses a convention and implements it consistently. It is not meant to be Common Lisp nor is it meant to be Scheme, nor Dylan, nor Shen, nor Femtolisp, nor any other implementation. It is meant to be Clojure and what that means is defined by Rich Hickey.
Regarding`first`, `rest`, `head`, and `tail`, I don't think the argument based on English usage is the strongest. `head` and `tail` are established in some other programming languages, almost always as operations upon a link list. Those two words in English refer to body parts on some animals, hardly an obvious analog to generic collections. `first` and `rest` are not unheard of in other programming languages and in English do have a natural connection with the idea of a collection. Again, Clojure is meant only to be what Rich Hickey designed it to be, not a faithful reproduction of any other programming language.
Like Common Lisp, which has `first`...`tenth` and `rest`.
(if (closed handle) ...)
(when (static widget) ...)
The p or -p or ? should only be used in situations when we can't avoid naming the predicate after a noun, like stringp for "is it a string".(That JS coerces the empty string to false is also bad.)
Personally I'd prefer that not even null/undefined coerced to false, or could otherwise be used in a boolean comparison, as for me they still signify different meanings and even in type-safe languages can lead to subtle bugs. I really dislike coercion having principally worked with JavaScript/TypeScript.
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/ 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).
https://github.com/psf/requests/blob/master/requests/models....
https://stackoverflow.com/questions/48347290/why-does-reques...
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)
I think Perl was there first: in addition to 0 and "" being falsy in Perl, "0" is also falsy. Then there is this nifty Perl value "0 but true": if you stick it into a condition, it will evaluate to true, but if you do math with it, it behaves like the number zero.
* 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.
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
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.
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.
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.
Most code doesn't need that and the truthiness nature of various data types is helpful in writing clear code.
Suppose a fantasy language has an ergonomic syntax to check existence. It returns a boolean. Additionally, implicit conversions to boolean for objects in the language is randomized for each runtime.
Isn't such a weirdo language still preferable to implicit conversions, even when compared to a language where "null" and "false" are the only falsey values? Because even in those languages the new user must internalize the simple rule and learn from context how it works and why it can be used with impunity. Whereas with my weirdo language the syntax explicitly conveys to all classes of user what is happening in the code.
Furthermore, I'd bet that even in your preferred languages there are less readable idioms that ninjas can leverage in the implicit conversions. In my weirdo language the randomized conversions would thwart the ninjas and keep the entire class of users free from their unreadable tyranny.
What "exists" means depends on the language; it can be as loose as checking if the variable has ever been set (PHP), but generally has nothing to do with truthiness/falsiness status.
Instead of
if x = 5 and y = 7 then goto 10
use if (x = 5) * (y = 7) then goto 10
Good to know when you're 11 years old typing in some program from a magazine meant for another computer that had AND and OR.In languages with more elaborate type systems, you usually have the pointer and number representation separated.
0 has no special meaning in regards to boolean logic but it has the special meaning of the identity/zero element in an additive group. Likewise, it makes sense to have "[1] + [] => [1]" in your programming language.
But making zero elements evaluate to falsy is problematic. "" would be falsy as well then.
In Python 0, [], and "" are all falsy. In my experience it's only been useful - why do you consider it problematic?
* Does it exist (true) or not (false)?
* Does it have a defined value (true) or not (false)?
* Is it non-zero (true) or not non-zero (false)?
* Does it have non-zero length (true) or not non-zero length (false)?
I think using zero as a magic number is less consistent than not using any magic numbers at all. If you want to check the length, or whether something equals zero, just check for that, not for whether it's "truthy".
def f_to_c(deg_f=None):
if deg_f:
return (deg_f - 32) * 5 / 9
else:
raise TypeError('Invalid value passed in')
The problem is that it works for all numbers, except 0, because 0 is falsy, and will return a TypeError exception.There are lots of different solutions to this issue, but I wouldn't write the pedantic if statements.
I would just do this:
def f_to_c(deg_f):
return (deg_f - 32) * 5 / 9
And let python raise the TypeError.Right?
def my_calc(x, scale = None):
if not scale:
scale = 1
return (x + 4) * scale
where the (x + 4) bit stands for some interesting calculation whose result is being scaled: this code makes it unclear whether or not scale == 0 is intentionally conflated with scale == None or whether this is a programming error. It would be better to do this, if this is intentional: def my_calc(x, scale = None):
if scale == 0 or scale is None:
scale = 1
return (x + 4) * scale
Or, if it's unintentional, one should have written: def my_calc(x, scale = None):
if scale is None:
scale = 1
return (x + 4) * scaleI get that you might want to know if it was supplied or if it is the default, and common lisp has a way to do that. It is very niche to need, thought.
That all said, I think this is just suffering the fate if anything with examples. I accept there are places where zero as false can be annoying.
Generally speaking we're creating contrived examples because in all fairness, I've only seen this once I can remember in the last 5 years of writing business logic in python. Usually language purists would prefer us to be explicit in our languages. And type safety is the best thing since sliced bread.
But effectively that's why python has taken off. Types do matter, but for most things they don't Lots of good software has been written using python.
I do think it would've been better to have 0, specifically, not be falsy - unlike the other cases, 0 is not 'the absence of any actual data' but rather a special case of the data. I assume Python stuck with tradition on this one mostly because it is so tightly tied to C and UNIX-style scripting that changing it would've been confusing for most early users.
I think this is one of the symptoms of the changing priority of simplicity in Python language design, at some point the design decision about 0/1 for simplicity became dated enough to change it despite being a big change and requiring backwards compatibility hacks with 0/1.
In many Lisps, nil is the empty list literal, but interestingly not in Clojure, where it just maps to JVM null, and vectors are the most commonly used data type anyway. However, it is idiomatic for APIs to accept (but not return!) nil as if it were an empty sequence or collection, unlike in most non-Lisp languages.
There are really only two reasons I can think of for not wanting default initialization.
1. performance
2. to catch mistakes on the edges of your system.
A good example of the second is converting incoming data to enumerations. If you're not explicitly checking for it, you can accidentally default initialize to a valid enumeration. This is why I always set the first enumeration to 1 for any language that allows me to. If someone makes that mistake, the missing data causes an error rather than silently doing the wrong thing.
But I can't really think of any reason outside of those two cases where you wouldn't want default initialization.
And my point was that one of the reasons 0 is chosen as false because it's consistent with respect to default initialization of booleans. I realize that isn't the original reason, but today it's a good reason for having the behavior.
false = 0
true = [1] (i.e. the equivalence class of all n > 0)
&& = +
|| = *
spelling it out: || / + (or):
0+0 = 0
0+[1] = [1]+0 = [1]
[1]+[1] = [1]
&& / * (and):
0*0 = 0
0*[1] = [1]*0 = 0
[1]*[1] = [1]
where [1] stands for "any nonzero number". (please let me know if i missed anything!)so natural numbers (quotiented by an equivalence relation) seem to work as a model for boolean arithmetic. and idk if we need more mathematical justification than "it works fine"
EDIT: changed "integers" to "natural numbers", because
5 + -5 = 0
so the `[1]+[1] = [1]` property doesn't holdNeither does the multiplication `[1]*[1] = [1]`
x || y = x + y - xyWhen you have a type system and boolean types, equating 0 and false is not a "math thing," it is more of a "mathematical illiteracy" thing.
[] being truthy is the same as Ruby, but somewhat of a gotcha as an empty vector [] does not have the same truthiness as an empty list '() aka nil.
Generally the more special values, that are treated as false, you have in a language - the more complex and confusing your programs become. Just my opinion.
The major advantage of empty collections being falsy is that you can kill two birds with one stone - it enables you to reduce your use of nil/null/None dramatically, since empty collections will fail a very idiomatic "if items" check just as well as null. But at the same time, you don't have to depend on users (library or otherwise) being careful to provide an empty list rather than null when they mean 'no value' - if they pass a null then your code will take the exact same shortcut with the exact same simple, idiomatic test. Whereas the alternative is needing to do both the null check AND the empty check any place you cannot trust the users of your function/library/whatever to pass real empty collections rather than nulls, which not only becomes tedious but is quite easy for new programmers to fail to do consistently.
In my own experience, the number of cases where an empty collection implies that data is effectively 'missing' is far greater than the number of cases where an empty list being provided would be semantically different from a null - in both cases "there is nothing there". And being freed to then create and pass around empty lists means that my own code is no longer littered with the necessary guards against Nullness in cases where I am the consumer of my own functions and want to be able to write simple list comprehensions without any guards.
In Clojure this is somewhat less of an issue because, as others have mentioned, it's idiomatic to pass `nil` around in the place of an empty collection, and so it's _generally_ safe to do so where in another language like Python or Javascript the function you're calling might not expect it. But in my opinion there was no real advantage to establishing this convention when it could just as easily have been the other way around, and there would therefore be somewhat less opportunity for inconsistent or inexperienced programmers causing NullPointerExceptions.