PyWart: More surpises via "implict conversion to boolean" (and other steaming piles!)
steve at pearwood.info
Tue Feb 11 07:30:43 CET 2014
On Mon, 10 Feb 2014 10:45:40 -0800, Rick Johnson wrote:
> ## START CODE ###########################################
> def foo():
> # foo represents a patternless function
Patternless? I have never heard that term before in this context. Do you
mean a parameter-less or argument-less function?
> # or method that returns a Boolean value
> # based on some internal test.
> if 1==1:
> return True
> return False
This always returns True, since 1 always equals 1.
> # The fun begins when two tiny chars are forgotten,
> # however, since the code is legal, python will happily
> # give us the wrong answer.
> if foo: # <- forgot parenthesis!
> print 'implicit conversion to bool bites!'
No it doesn't. It rocks. You have found one tiny little disadvantage,
about the size of a mote of dust floating: if you write functions that
take no arguments (already a code-smell) and forget the brackets
(mistakes will happen...) and have no tests to ensure that both branches
of the `if` are tested (what, are you careless and negligent?), then you
might be bitten by a bug of your own creation.
Compared to that mote, the usefulness and convenience of duck-typing bools
is about the size of Mt Everest.
> # This block will NEVER execute because foo is
> # ALWAYS True!
Correct. And since foo() is also always True, there is no difference.
> It's obvious i did not follow the syntactical rules of Python, i
> understand that,
No you don't understand that. You *did* follow the syntactical rules of
Python. `if foo` is perfectly correct syntax, if it were not, you would
have got a SyntaxError exception at compile-time.
You need to understand the difference between syntax and semantics. This
is invalid English syntax:
"Cat mat on sat the."
This is valid syntax, but semantically wrong:
"The mat sat on the cat."
This is both syntactically and semantically correct:
"The cat sat on the mat."
> however, there are three design flaws here that are
> contributing to this dilemma:
> 1. Implicit conversion to Boolean is evil
It's not implicit conversion. You're not understanding what is going on.
foo is not converted to a bool, foo remains a function object. Rather,
it's duck-typing truthiness, which is no different from any other duck-
typing. If it swims like a bool and quacks like a bool, it might as well
be a bool.
Duck-typing is a design feature, not a flaw.
> 2. Conditionals should never accept parameter-less functions.
How is the conditional supposed to know that its term is a function?
What's so special about parameter-less functions? You can forget to call
functions with any number of parameters, especially if they take default
Let's suppose that you get your way. Which of the following if-tests
should be prohibited, and when should they be prohibited? At compile time?
if random.random() > 0.5:
spam = lambda x=23: x
eggs = 42
spam = 42
eggs = lambda x=23: x
print "spam is a truthy value"
print "eggs is a truthy value"
> If you
> want to check if a callable is True or False, then use "if
Ewww. That's horrible. bool() should only be used to get a canonical bool
object, e.g. for writing to a database or something. It should never be
used just because you are scared of Python's power.
> Any usage of a bare callable in conditionals should
> raise SyntaxError.
Ah, so it should happen at compile-time, not run-time? I'm afraid you're
not as clear about how Python operates as you perhaps think you are.
> 3. Implicit introspection is evil, i prefer all references to a
> callable's names to result in a CALL to that callable, not an
> introspection! Introspection should ALWAYS be explicit!
Again, this is a complete misunderstanding of Python's execution model.
It is true that callables have names, but they are only used for
displaying error messages in tracebacks. Otherwise, callables are no
different from any other object: they can have zero, one or many names
bound to them, and referring to the name gives you access to the object
spam = <any object at all, whether a function or not>
spam # this is a reference to the object
Introspection has nothing to do with this.
> For a long time i thought Python's idea of a returning the value of a
> callable-- who's name is unadorned with "(...)" --was a good idea,
> however, i am now wholly convinced that this design is folly,
Oh well, not everyone has the sense to recognise good design. You should
do more Python programming, and less ranting, you might learn something.
> and the
> reason is two fold:
> 1. Parenthesis should not be required for parameter- less functions.
Of course they should. Firstly, parameter-less functions are a code-
smell, and ought to be discouraged. Secondly, even if you have a good
reason for using one -- for example, random.random -- then the difference
between referring to the object and calling the object should be clear.
With Python's correct design, we have:
spam # always, without exception, refers to the object
spam() # always, without exception, calls the object
With your suggested design, we would have:
spam # sometimes refers to the object, sometimes calls the object
spam() # always calls the object
Ruby makes this mistake, and is a lessor language for it.
> I realize this is a bit more complicated in languages like Python
> where attributes are exposed to the public, but still, not reason
> enough to require such onerous typing.
What does the presence or absence of attributes have to do with this?
> 2. Implicit introspection is evil. I would prefer an explicit method
> attached to all callables over a sugar for "callable.call". We
> should never consume syntactical sugars UNLESS they can greatly
> reduce the density of code (like math operators for instance!)
I do not understand what you are trying to say here.
More information about the Python-list