Introduce a new type (‘logical’) with the desired properties?
This is the way. (Create a new type.)
Are there actual examples of people hurting themselves with ~True ? The only things I could come up with regarding Python boolean weirdness are discussions of things like True and []
returning [].
Honestly, I don’t see it as the responsibility of the language designer from protecting people who do weird stuff from having weird things happen. I say this as a recovering Java/C++ programmer who has tried to && and ! Python booleans more than once.
Deprecating the clever usage ~ boolean by people who understands what it does will just annoy them and benefit very few people.
It may be good for the long term, but in the short to mid term what is supposed to be recommended migration plan for the existing code base with variables/arguments already typed as bool
? Are they all supposed to be typed as bool | logical
and have a forced runtime conversion to either bool
or logical
depending on whether they are inolved in integer-based arithmetic? That sounds like a lot of work.
Anything bool would implicitly convert to logical.
Converting logical to bool would generate a type checker warning, unless explicitly cast using bool().
That wouldn’t really solve any problem, though. People using bool
like an int
will continue to use bool
. Now we have two Boolean types (one of them with a non-standard name and as-yet-unsuggested non-standard names that are synonymous with True
and False
), with no feasible path towards eventually replacing bool
with logical
.
You can’t have two bool types because only one of them can be the one that is actually returned by all the operations that create bools like 2 > 1
etc.
I’m sure this is how bool ended up being a subclass of int in the first place. Someone would have done e.g.:
print "%d thing%s" % (num, "s" * (num != 1))
Then one day Python added bool
and all of a sudden num != 1
was going to evaluate to True
instead of 1
. The only way to keep that code working was if True
supported the same arithmetic as 1
.
Yes, but having >
et al returning logical
instead of bool
is as much a breaking change as simply making bool
not valid for arithmetic in the first place.
Regardless of whether it is a good idea or not, I don’t see why bool
and logical
couldn’t coexist if coercion rules were put in place for whatever behavior is desired, e.g.,
2 > 1
→bool
logical(2 > 1)
→logical
True or False
→bool
logical(True) or False
→logical
I don’t think logical
actually solves the “problem” here[1] but I do agree that putting logical(...)
round bool values where you want “real logical behaviour” is no more or less unreasonable than expecting people to put int(...)
round bool values where you want “real int behaviour”. And unlike int(...)
, logical(...)
is backward compatible and can be implemented entirely in user code.
to be explicit, I don’t even think there is a problem to solve… ↩︎
I still don’t understand, why is it a problem (that bool
is an int
)? The only valid reason stated was that more users want to use it in this way:.
And this could easily be solved by a new type. So, is there any other reason for this problem, that isn’t solved by logical
?
Depending on your definition of “problem”, the current behavior may be surprising and inconvenient for some users. MATLAB’s logical
has behavior similar to that of Python’s bool
, and I had to caution students about this ‘feature’ on many occasions.
I’m fine with bool as is, but addressing the is there a road question:
There are two realms of interest for typing – run time and static checker time. For reasons already discussed, primarily breaking existing code for dubious benefit, I don’t see changing the run time type / behavior of bools.
But just as Any exists for static checking, a logical type could be added for type checkers to flag when bools are treated in integer ways.
x: logical = True
y = x and []
could issue a type warning, while
y = x and logical([])
or y = x and bool([])
would not. (y would be “False”)
Another approach would be to have some magic keyword could change the behavior, although I think this would have to be done on a per-file or per-module basis.
from __strict__ import logical
could make 1 < 2 return the new I am not an integer logical type.
(This seems similar how str | None
can be used in Python 3.9 with from future import annotations)
Just to help people understand this, first of all I gave at five “valid” reasons:
Every programming language draws a line somewhere between what happens implicitly and what must be done explicitly. For example, in Javascript, you can do things like:
"hello" + 123
whereas in Python, you must do:
"hello" + str(123)
and similarly for things like concatenation and string joining.
The benefit of implicit behavior is that code is less wordy. This makes code appear simpler and more elegant.
On the other hand, the benefit of explicit requirements is that errors function as guard rails preventing subtle bugs. Because there’s less implicit behavior, reading code requires keeping fewer possibilities in mind.
My guess is that the kinds of people that prefer implicit behavior are those that write shorter scripts because it’s easier to keep everything about such a script in mind. You just want to write the script as quickly as possible. The benefits of implicit behavior outweigh the disadvantages.
On the hand, people who work on large applications struggle to keep everything in mind. It’s too easy for subtle bugs to creep in. These are the kinds of people that would rather the code be slightly more wordy for the benefit of peace of mind. These are also the kinds of people that tend to use linters and type checkers because they want all the help they can get.
This desire to narrow the Boolean interface is similar to the desire by some people to make strings non-sequences (after all, strings don’t even respect the sequence interface, and distinguishing strings from other sequences is very common).
And Python was always fairly strictly typed, despite being dynamically typed. Stricter than many statically typed languages. Which is why I think that moving towards a bool
type that doesn’t derive from int
is worthwhile. Some of the hacks in this thread that prefer “cleverness” over readability notwithstanding.
One thing worth noting is that a logically distinct bool
type can still be treated as defining __index__
.
That means many “needs an integer-or-integer-like-value” operations (such as subscripting or sequence repetition) would still be considered valid, while still ruling out the operations that genuinely only make sense for true numbers.
(I suspect if the __index__
protocol had existed back when bool
was added, they may have just implemented that protocol rather than inheriting from int
)
This has been a discussion in the typing side of things, wanting to have “strict” types where the default ones are wider than they appear (float, bool, str, etc). I don’t think bool can be changed because it will break too much code but a new type does sound appetizing (even if it’s for typing alone)