I'd like to use 'or' more often, but it always makes me stop to think "Could this get passed a falsey value?" I've seen a lot of Python bugs because people used "x or 5" when they needed the much uglier "5 if x is None else x".
I'd like to use 'or' more often, but it always makes me stop to think "Could this get passed a falsey value?" I've seen a lot of Python bugs because people used "x or 5" when they needed the much uglier "5 if x is None else x".
Yes. That's how you end up with messes like this one: https://lwn.net/Articles/590299/
In most people's Haskell code, you can only use actual Booleans when you need true or false. (As you might expect from a well-disciplined, strongly typed language.)
But it's actually really easy to add your own version of eg `if` etc that accept nearly anything, and automatically convert it to True or False. You can do this as a normal library for use in anyone's code.
If your language supports a genuine nil and not just a distinguished singleton token (AFAIK only Lua does that consistently, though perhaps “undefined” in non-strict-mode JavaScript also counts), I’m actually leaning towards having operators for dealing with it specifically. Icon does something like that, arguably, although its behaviour (for a “failing” expression that yields no values) is more like a propagating NaN / relational NULL / poison value than a nil.
Nix (though it has a faux null like everybody else, as well as a poisoning bottom typical of lazy languages) has a slick partial solution: the field access operator is optionally ternary, expr.name [or expr], so you can write things like
systems.flakeExposed or systems.supported.hydra
when you want to fall back from one to the other, even though in the fallback case the value of systems.flakeExposed by itself is not nil, it’s an error. (Using or outside of field access is a syntax error, the Boolean operator is spelled ||.)I liked Kotlin's null-handling when I worked in it because there were some tools there to handle nulls in a reasonable manner. I think the `something?.prop` would do null checking on `something` before trying to access `prop` and then you could use `?:` (Elvis operator, haha) to check a null and do something if it was null, like `something ?: executed_if_null` and combining those would effectively deal with nulls in a simple and readable way.
Lua is JavaScript-level stupid, minus the useful ecosystem.
Which is irrelevant, as the author mentioned a specific feature of Lua that's designed better than Python's same feature, didn't say everything is better in Lua.
(Not to mention it's not really true anyway, Lua is fine).
As far as zero and one; if you compare equality on a machine you’re generally doing an xor type operation between the two values and if they’re equal you get zero, so that’s flipped. If you’re comparing two numbers it might even result in a ternary value of LT, EQ, and GT.
I think true and false are high level programming language or mathematical concepts that don’t map directly to the low level testing results. So, I don’t really consider the traditional “true is 1, false is 0” to be anything more than convenient abstractions in programming based on things that some folks did for convenience at the beginning of software development.
1 doesn't mean much regarding true, if every other non-zero integer is also true.
And my point is that since 0 alone is treated as false among integers in those cases, it can be considered as iconic or "standard". But since any other integer is true, 1 is not much of the "canonical" true, but just one of billions of others.
In binary it's 0 and 1 alone, so the comment doesn't apply there.
If you don’t want to test an integer, if it doesn’t meet your needs, don’t use it. There are an unlimited number of other choices for comparisons.
First class bools alone being logic-checked, is a better design.
The problem is sum types. Specifically, languages which "don't have" sum types often secretly do have sum types, absolutely everywhere, but no language facilities to deal with them because they "don't have" sum types. That's what the billion dollar mistake is, it's sum types (thing | NULL) with no language facilities. In Python that's (thing | None) but the same penalty accrues.
Now, PEP 505 introduces a bunch of actual language facilities. They might be a good idea for Python, other languages successfully did this, but I think as a lesson going forward what we should take away is that we do want Sum types and we should stop pretending we (sometimes?) don't, which means new languages need to be designed for them.
Certainly Kotlin, Swift, Rust, etc. do have them. Even Java, I believe, is planning to add them, though I suspect that many people won't necessarily understand why they're useful even when they land.
It's probably helped by functional programming becoming more mainstream and "obvious" sum types needing to be implemented (Optional and Result, mostly).
something['nested']['blah']['bloop']
some.namespace.with['stuff']
safe with None. Your options are using jmespath, your own similar function, or wrap it in a try block. Otherwise the whole syntax is essentially unusable. And because of the quirks of how __getitem__ is evaluated you can't even hack an infinite defaultdict to do the right thing because you can't detect the "last" access to return None so it's only good for when you want to assign to it. something.get('nested', {}).get('blah', {}).get('bloop')
works just fine but is mighty ugly....which is a hint to use something else in this case. dataclass, typed dictionary, deserialize properly, whatever.
Lisp: (or x 5)
Scheme: (if (null? x) 5 x)