On 21/06/2013 19:26, Rick Johnson wrote:
[...]
I didn't ask what alternative methods of handling default
argument binding exist (I can think of several, but none
of them strikes me as preferable to how Python currently
does it). I asked what would happen in /your/ version of
Python. Which of the alternatives that you present would
have been implemented, if you had designed the language?
The apathetic approach. However, you fail to understand that
whilst Python's current implementation is partly apathetic,
is is also benevolent, and malevolent simultaneously. My
approach is purely apathetic. I'll explain later. Stay
tuned.
[...]
So how does the interpreter know whether an arbitrary
object passed as a default value is mutable or not? Not
that it really matters.
Well i'm glad it does not matter to you because it does not
matter to me either. *shrugs*
Am i just to take your word for this? You cannot provide an
example? Here, allow me to "break the ice":
# Literal
py> d = {[1]:2}
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
d = {[1]:2}
TypeError: unhashable type: 'list'
# Symbol
py> lst = [1]
py> d = {lst:2}
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
d = {lst:2}
TypeError: unhashable type: 'list'
Hmm, maybe only certain mutables work? Great, more esoteric
rules! Feel free to enlighten me since i'm not going to
waste one second of my time pursuing the docs just to learn
about ANOTHER unintuitive PyWart i have no use for.
Now, I don't really believe that you think that the user
shouldn't be protected from doing one idiotic thing with
mutable dict keys but should be protected from doing
another idiotic thing with mutable default arguments,
especially as you've already been given a use case for the
latter. So I assume that The Benevolent Approach is not
the approach you would have gone for if you had designed
the language, right? If so then let's ignore it.
You are correct. Finally, we can agree on something.
It seems to me that this is exactly what currently happens.
(is this lazy readers day? I swear i explained this earlier)
And here is where you are wrong. In the current implementation
python functions carry the state of mutable default arguments
between successive calls. That's a flaw. Observe:
py> def foo(arg=[]):
... arg.append(1)
... print(arg)
...
py> foo()
[1]
py> foo()
[1, 1]
py> foo()
[1, 1, 1]
No, no, NO! That's wrong! Subroutines should be stateless.
That means that an empty mutable default argument will
ALWAYS be empty at each call of the subroutine. This is
what should happen:
py> def foo(arg=[]):
... arg.append(1)
... print(arg)
...
py> foo()
[1]
py> foo()
[1]
py> foo()
[1]
Yes, Yes, YES! That is intuitive! That is sane! Now, what if
we pass a reference to a mutable object? What then. Well, let's
see:
py> lst = range(5)
py> lst
[0, 1, 2, 3, 4]
py> def foo(arg=lst):
... arg.append(1)
... print(arg)
...
py> foo()
[0, 1, 2, 3, 4, 1]
py> foo()
[0, 1, 2, 3, 4, 1, 1]
That's fine. Because the object was already created OUTSIDE
the subroutine. So therefore, modifications to the mutable
are not breaking the fundamental of statelessness INSIDE
subroutines. The modification is merely a side effect, and
the subroutine is unconcerned with anything that exists
beyond it's own scope.
IS ALL THIS REGISTERING YET? DO YOU UNDERSTAND?
My question was about how you think the language should
work, not about what your buddies should or shouldn't
enjoy.
My buddies? This design flaw is NOT my brain child. Your
barking up the wrong tree pal.
In terms of how a language actually works, is there
any difference between The Malevolent Approach and The
Apathetic Approach? And is there any difference between
either of them and what Python currently does?
I explained this to MRAB already.
Of course using a mutable default as a cache can be
reproduced by other means, as can another common use case
that I don't think anyone's mentioned yet (defining
functions parametrised by variables whose values aren't
known until runtime). That's hardly an argument against it
- you might as well argue that Python shouldn't have
decorators, or that it shouldn't have for loops because
their behaviour can be reproduced with while loops.
Nice attempt at sleight of hand but your logic is clumsy.
Your trying to argue that my use of a "custom callable state
object" (to avoid the esoteric and unintuitive nature of the
current implementation of Python "mutable function
arguments") is somehow only a mere reproduction of the
function behavior and has no positive benefits, when in
fact, it has a HUGE direct benefit:
* AVOIDING A FLAW IN THE LANGUAGE
It also has quite a few positive side effects:
* CODE ENCAPSULATION
* INTERFACE
* USING THE CORRECT TOOL FOR THE JOB++
* READABILITY
* NO HIDDEN SURPRISES
* LESS BUGGY
How much more justification do you need?
or that it shouldn't have for loops because their
behaviour can be reproduced with while loops.
Yes, iterating a sequence can be achieved using a "while
loop", but "for loops" should not be thrown out because they
offer a specific type of iteration that nicely complements a
while loop. Plus, for loops do not have any strange side
effects (unlike Python functions). They do one thing and
they do damn well! So stop picking on for loops
I want Python functions to also "do one thing and do it
well", and what is that "one thing" you ask, execute
subprograms.
But this is beside the point anyway, until you present an
alternative to Python's current behavior. If you do so
then we can start debating the relative merits of the two
approaches.
The fix is simple. No more "magical Python functions", only
stateless routines. I've explained this ad nauseam already,
and until you provide more specific questions on the matter,
i refuse to reply to any more bombastically general questions.