[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Brendan Barnwell

On 2021-10-26 19:47, Chris Angelico wrote:

help(bisect.bisect)


bisect_right(a, x, lo=0, hi=None, *, key=None)
...
 Optional args lo (default 0) and hi (default len(a)) bound the
 slice of a to be searched.

In the docstring, both lo and hi are given useful, meaningful
defaults. In the machine-readable signature, which is also what would
be used for tab completion or other tools, lo gets a very useful
default, but hi gets a default of None.


	How would tab completion work with this new feature?  How could a 
late-bound default be tab-completed?  Usually when I've used 
tab-completion with function arguments it's only completing the argument 
name, not the default value.



For the key argument, None makes a very meaningful default; it means
that no transformation is done. But in the case of hi, the default
really and truly is len(a), but because of a technical limitation,
that can't be written that way.


Here is some code:

def foo():
for a in x:
print(a)
for b in x:
print(b)

other_func(foo)

Due to a technical limitation, I cannot write foo as a lambda 
to pass it inline as an argument to foo.  Is this also a problem?  If 
so, should we develop the ever-whispered-about multiline lambda syntax?


	Yes, there are some kinds of default behaviors that cannot be 
syntactically written inline in the function signature.  I don't see 
that as a huge problem.  The fact that you want to write `len(a)` in 
such a case is not, for me, sufficient justification for all the other 
cans of worms opened by this proposal (as seen in various subthreads on 
this list), to say nothing of the additional burden on learners or on 
readers of code who now must add this to their list of things they have 
to grok when reading code.



Suppose this function were written as:

bisect_right(a, x, lo=None, hi=None, *, key=None)

Do we really need the ability to specify actual function defaults? I
mean, you can just have a single line of code "if lo is None: lo = 0"
and it'd be fine. Why do we need the ability to put "lo=0" in the
signature? I put it to you that the same justification should be valid
for hi.


	I understand what you're saying, but I just don't agree.  For one 
thing, functions are written once and called many times.  Normal default 
arguments provide a major leverage effect: a single default argument 
value can simplify many calls to the function from many call sites, 
because you can (potentially) omit certain arguments at every call. 
Late-bound defaults provide no additional simplicity at the call sites; 
all they do is permit a more terse spelling at the function definition 
site.  Since the definition only has to be written once, a bit more 
verbosity there is not so much of a burden.


	Also, the attempt to squeeze both kinds of defaults into the signature 
breaks the clear and simple rule we have, which is that the signature 
line is completely evaluated right away (i.e., it is part of the 
enclosing scope).  Right now we have a clean separation between the 
function definition and the body, which this proposal will muddy quite 
profoundly.  The problem is exacerbated by the proposed syntaxes, nearly 
all of which I find ugly to varying degrees.  But I think even with the 
best syntax, the underlying problem remains that switching back and 
forth between definition-scope and body-scope within the signature is 
confusing.


	Finally, I think a big problem with the proposal for me is that it 
really only targets what I see as a quite special case, which is 
late-bound expressions that are small enough to readably fit in the 
argument list.  All of the arguments (har har) about readability go out 
the window if people start putting anything complex in there.  Similarly 
if there is any more complex logic required (such as combinations of 
arguments whose defaults are interdependent in nontrivial ways) that 
cannot easily be expressed in separable argument defaults, we're still 
going to have to do it in the function body anyway.  So we are adding an 
entirely new complication to the basic argument syntax just to handle 
(what I see as) a quite narrow range of expressions.


--
Brendan Barnwell
"Do not follow where the path may lead.  Go, instead, where there is no 
path, and leave a trail."

   --author unknown
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/X3OO4KV4GSJ5CV7OCEZRZAAY7Z2T6V55/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Christopher Barker
a bit OT:

  If you want to bisect a slice, then bisect a slice:
>
> result = bisect_right(a[lo:hi], x)
>
> Oh, wait, what if a has billions of elements and creating a slice
> containing a million or two out of the middle is too expensive?


Yup, that's why Iproposed about a year ago on this list that there should
be a way to get a slice view (or slice iterator, AKA islice) easily :-)

You can use itertools.islice for this though -- oops, no you can't:

TypeError Traceback (most recent call last)
 in 
> 1 result = bisect.bisect_right(islice(a, lo, hi), x)

TypeError: object of type 'itertools.islice' has no len()

We really do need a slice view :-)

-CHB

-- 
Christopher Barker, PhD (Chris)

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/F2CITCVKT3E3RHCH7UUFDUURIBQLJI65/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Christopher Barker
On Tue, Oct 26, 2021 at 8:29 PM Ethan Furman  wrote:

> Using the `bisect()` function as a stand-in for the 20+ years worth of
> Python APIs in existence:
>
>  def bisect_right(a, x, lo=0, hi=None, *, key=None):
>  if hi is None:
>  hi = len(a)
>  while lo < hi:
>  ...
>
> That function would be transformed to:
>
>  def bisect_right(a, x, lo=0, @hi=len(a), *, key=None):
>  if hi is None:
>  hi = len(a)
>  while lo < hi:
>


> Notice that the `None` check is still in the body -- why?  Backwards
> compatibility:


Of course. personally, I don't think there's any reason to go in and change
the standard library at all.

This is a feature for new code, new APIs. *maybe* a long time from now,
some stdlib APIs could be updated, but no hurry.

This seems like a lot of effort for a very marginal gain.
>

marginal gain to the stdlib, yes of course.

Though now that you mention it -- there would be some marginal game even to
functions like that, making the function signature a bit more clear.

Interestingly, though, here's bisect's docstring:

In [14]: bisect.bisect?
Docstring:
bisect_right(a, x[, lo[, hi]]) -> index

Return the index where to insert item x in list a, assuming a is sorted.

The return value i is such that all e in a[:i] have e <= x, and all e in
a[i:] have e > x.  So if x already appears in the list, i points just
beyond the rightmost x already there

Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
Type:  builtin_function_or_method

It's not actually documented that None indicates "use the default".

Which, it turns out is because it doesn't :-)

In [24]: bisect.bisect([1,3,4,6,8,9], 5, hi=None)
---
TypeError Traceback (most recent call last)
 in 
> 1 bisect.bisect([1,3,4,6,8,9], 5, hi=None)

TypeError: 'NoneType' object cannot be interpreted as an integer

I guess that's because in C there is a way to define optional other than
using a sentinel? or it's using an undocumented sentinal?

Note: that's python 3.8 -- I can't imagine anything;s changed, but ...

-CHB

-- 
Christopher Barker, PhD (Chris)

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/NUYBMMBIEHLLIJL6M36OKIGWI3E5DIBZ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread 2QdxY4RzWzUUiLuE
On 2021-10-27 at 13:47:31 +1100,
Chris Angelico  wrote:

> On Wed, Oct 27, 2021 at 1:15 PM Brendan Barnwell  
> wrote:
> >
> > On 2021-10-26 17:41, Christopher Barker wrote:
> > > Python used to be such a simple language, not so much anymore :-(
> >
> > I quite agree, and I feel like this is my biggest reason why I don't
> > want this "feature" (or any of another gazillion features that have been
> > suggested and/or accepted here, including the walrus).  The benefit of
> > this PEP simply does not justify the added complexity of the language as
> > a whole.  Using None (or some sentinel) and including code to check for
> > it works fine.  It's not worth it to add a whole new layer of behavior
> > to something as basic as argument-passing just to avoid having to type
> > `if arg is None`.
> >
> 
> >>> help(bisect.bisect)
> 
> bisect_right(a, x, lo=0, hi=None, *, key=None)
> ...
> Optional args lo (default 0) and hi (default len(a)) bound the
> slice of a to be searched.

[...]

> Suppose this function were written as:
> 
> bisect_right(a, x, lo=None, hi=None, *, key=None)
> 
> Do we really need the ability to specify actual function defaults? I
> mean, you can just have a single line of code "if lo is None: lo = 0"
> and it'd be fine. Why do we need the ability to put "lo=0" in the
> signature? I put it to you that the same justification should be valid
> for hi.

I agree.  Why do we need the ability to put "lo=0" in the signature?  If
we have to rewrite parts of bisect_right for late binding anyway, then
why not go all out?  If you want to bisect a slice, then bisect a slice:

result = bisect_right(a[lo:hi], x)

Oh, wait, what if a has billions of elements and creating a slice
containing a million or two out of the middle is too expensive?  Then
provide two functions:

def bisect_right(a, x, key=None):
return bisect_slice_right(a, x, 0, len(a), key)

def bisect_slice_right(a, x, lo, hi, key=None):
"actual guts of bisect function go here"
"don't actually slice a; that might be really expensive"

No extraneous logic to (re)compute default values at all.  Probably
fewer/simpler units tests, too, but that might depend on the programmer
or other organizational considerations.  On the other side, parts of doc
strings may end up being duplicated, and flat is better than nested.

(No, I don't know the backwards compatibility issues that might arise
from re-writing bisect_right in this manner, nor do I know what the
options are to satisfy IDEs and/or users thereof.)

Running with Brandon Barnwell's point, is there a lot of code that gets
a lot simpler (and who gets to define "simpler"?) with late binding
default values?  Can that same code achieve the same outcome by being
refactored the same way as I did bisect_right?  I'm willing to accept
that my revised bisect_right is horrible by some reasonably objective
standard, too.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/JYKWRN6KMW4KOSFFTXAZ3EDFETQKIAEP/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 2:30 PM Ethan Furman  wrote:
>
> On 10/23/21 5:13 PM, Chris Angelico wrote:
>
>  > PEP: 671
>  > Title: Syntax for late-bound function argument defaults
>
> I have a major concern about the utility of this addition -- it was mentioned 
> already (sorry, I don't remember who) and
> I don't think it has yet been addressed.
>
> Using the `bisect()` function as a stand-in for the 20+ years worth of Python 
> APIs in existence:
>
>  def bisect_right(a, x, lo=0, hi=None, *, key=None):
>  if hi is None:
>  hi = len(a)
>  while lo < hi:
>  ...
>
> That function would be transformed to:
>
>  def bisect_right(a, x, lo=0, @hi=len(a), *, key=None):
>  if hi is None:
>  hi = len(a)
>  while lo < hi:
>  ...
>
>
> Notice that the `None` check is still in the body -- why?  Backwards 
> compatibility: there is code out there that
> actually passes in `None` for `hi`, and removing the None-check in the body 
> will suddenly cause TypeErrors.
>
> This seems like a lot of effort for a very marginal gain.
>

The question then becomes: How important is this feature of being able
to pass None as a stand-in for the length of the list? If it's
important, then don't change the style of the code at all. But since
help(bisect) clearly states that the default is len(a), and doesn't
say anything about passing None, so it would be just as valid to deem
such code to be buggy. Obviously that decision has to be made
individually for each use-case, but one thing is clear: newly-defined
APIs will not need to have None-pollution in this way. Also, anything
that uses a dedicated sentinel should be safe to convert; consider:

# Inspired by socket.create_connection, but simplified:
_USE_GLOBAL_DEFAULT = object()
def connect(timeout=_USE_GLOBAL_DEFAULT):
if timeout is _USE_GLOBAL_DEFAULT:
timeout = default_timeout


If any outside caller is reaching into the module and referring to
this underscore-preceded name, it is buggy, and I would have no qualms
in breaking that code. (The inspiration for this can't actually be
converted, since socket.create_connection with the global default just
doesn't call the settimeout() method, but there will be other cases
where the same applies.)

As with every new feature, a decision has to be made as to whether old
code should be changed. This is no different, but I do believe that in
many cases, it will be safe to do so.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/YBJS3EJOLRKS4FSJDGXNVBMHRM6LQQZH/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Ethan Furman

On 10/23/21 5:13 PM, Chris Angelico wrote:

> PEP: 671
> Title: Syntax for late-bound function argument defaults

I have a major concern about the utility of this addition -- it was mentioned already (sorry, I don't remember who) and 
I don't think it has yet been addressed.


Using the `bisect()` function as a stand-in for the 20+ years worth of Python 
APIs in existence:

def bisect_right(a, x, lo=0, hi=None, *, key=None):
if hi is None:
hi = len(a)
while lo < hi:
...

That function would be transformed to:

def bisect_right(a, x, lo=0, @hi=len(a), *, key=None):
if hi is None:
hi = len(a)
while lo < hi:
...


Notice that the `None` check is still in the body -- why?  Backwards compatibility: there is code out there that 
actually passes in `None` for `hi`, and removing the None-check in the body will suddenly cause TypeErrors.


This seems like a lot of effort for a very marginal gain.

--
~Ethan~
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/VGCI7N3NHB4HBPKFBV2DWTJSJBRT3SGV/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 1:52 PM Rob Cliffe via Python-ideas
 wrote:
>
> On 27/10/2021 03:12, Brendan Barnwell wrote:
> > On 2021-10-26 17:41, Christopher Barker wrote:
> >> Python used to be such a simple language, not so much anymore :-(
> >
> > I quite agree, and I feel like this is my biggest reason why I
> > don't want this "feature" (or any of another gazillion features that
> > have been suggested and/or accepted here, including the walrus).  The
> > benefit of this PEP simply does not justify the added complexity of
> > the language as a whole.  Using None (or some sentinel) and including
> > code to check for it works fine.  It's not worth it to add a whole new
> > layer of behavior to something as basic as argument-passing just to
> > avoid having to type `if arg is None`.
> >
> While I disagree with you re this particular feature, I sympathise with
> the general sentiment.  Perhaps the first objective of Python 4 should
> be to get rid of as many features as possible.  Just like when you're
> forced to throw out all your precious old junk (school reports, prizes,
> the present from Aunt Maud, theatre programmes, books you never read,
> clothes you never wear .).
>
> Nah, who am I kidding?  Each feature will have its band of devotees that
> will defend it TO THE DEATH!  Of course, what it should REALLY have are
> all MY favorite features, including some that haven't been added yet.

One truism of language design is that the simpler the language is (and
the easier to explain to a novice), the harder it is to actually use.
For instance, we don't *need* async/await, or generators, or list
comprehensions, or for loops, or any of those other tools for
processing partial data; all we really need is a class with the
appropriate state management. And there are people who genuinely
prefer coding a state machine to writing a generator function. No
problem! You're welcome to. But the language is richer for having
these tools, and we can more easily express our logic using them.

Each feature adds to the complexity of the language, but if they are
made as orthogonal as possible, they generally add linearly. But they
add exponentially to the expressiveness. Which ultimately means that
orthogonality is the greatest feature in language design; it allows
you to comprehend features one by one, and build up your mental
picture of the code in simple ways, while still having the full power
that it offers.

As an example of orthogonality, Python's current argument defaults
don't care whether you're working with integers, strings, lists, or
anything else. They always behave the same way: using one specific
value (object) to be the value given if one is not provided. They're
also completely orthogonal with argument passing styles (positional
and keyword), and which ones are valid for which parameters. And
orthogonal again with type annotations and type comments. All these
features go in the 'def' statement - or 'lambda', which has access to
nearly all the same features (it can't have type comments, but
everything else works) - but you don't have to worry about exponential
complexity, because there's no conflicts between them.

One of my goals here is to ensure that the distinction between
early-bound and late-bound argument defaults is, again, orthogonal
with everything else. You should be able to change "x=None" to "x=>[]"
without having to wonder whether you're stopping yourself from adding
a type annotation in the future. This is why I'm strongly inclined to
syntaxes that adorn the equals sign, rather than those which put
tokens elsewhere (eg "@x=[]"), because it keeps the default part
self-contained.

(It's actually quite fascinating how language design and game design
work in parallel. The challenges in making a game fair, balanced, and
fun are very similar to the challenges in making a language usable,
elegant, and clean. I guess it's because, ultimately, both design
challenges are about the humans who'll use the thing, and humans are
still humans.)

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/5MGUXX6RBADE7VLQ3DFTGIENQ22PVXHW/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 1:52 PM Ricky Teachey  wrote:
>
> If I might paraphrase Agrippa from the book of Acts: "Almost thou persuadest 
> me..." ;) It's a fine answer.
>
> Thanks for the attentiveness to my concern, Chris. Very much appreciated!
>

My pleasure. This has been a fairly productive discussion thread, and
highly informative; thank you for being a part of that! Now, if I
could just figure out what's going on in the grammar...

(Actually, the PEG parser is a lot easier to understand than Python's
older grammar was. But this is my first time messing with it, so I'm
going through all the stages of brand-new discovery.)

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/23ZL6RIKAA75V2OHFYL62CLBGJL4SCFT/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Brendan Barnwell

On 2021-10-26 19:50, Rob Cliffe via Python-ideas wrote:

On 27/10/2021 03:12, Brendan Barnwell wrote:

On 2021-10-26 17:41, Christopher Barker wrote:

Python used to be such a simple language, not so much anymore :-(


I quite agree, and I feel like this is my biggest reason why I
don't want this "feature" (or any of another gazillion features that
have been suggested and/or accepted here, including the walrus).  The
benefit of this PEP simply does not justify the added complexity of
the language as a whole.  Using None (or some sentinel) and including
code to check for it works fine.  It's not worth it to add a whole new
layer of behavior to something as basic as argument-passing just to
avoid having to type `if arg is None`.


While I disagree with you re this particular feature, I sympathise with
the general sentiment.  Perhaps the first objective of Python 4 should
be to get rid of as many features as possible.  Just like when you're
forced to throw out all your precious old junk (school reports, prizes,
the present from Aunt Maud, theatre programmes, books you never read,
clothes you never wear .).

Nah, who am I kidding?  Each feature will have its band of devotees that
will defend it TO THE DEATH!  Of course, what it should REALLY have are
all MY favorite features, including some that haven't been added yet.
Rob Cliffe


	Now you're talking!  100% agree!  Assuming of course that by "MY 
favorite features" you mean, well, MY favorite features. . . :-)


--
Brendan Barnwell
"Do not follow where the path may lead.  Go, instead, where there is no 
path, and leave a trail."

   --author unknown
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/HPUIM75DTUALTSOKHEIJK4SVFNFK3YXL/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Parameter tuple unpacking in the age of positional-only arguments

2021-10-26 Thread Greg Werbin
>From a "hobbyist / programming aesthete" perspective, I think this is a great 
>idea. I think pattern matching in ML-descended languages is wonderfully 
>elegant. Packing and unpacking tuples is already a common syntactic idiom in 
>Python, and now that we have Structural Pattern Matching (SPM), it's going to 
>encourage people to embrace pattern matching even more as an element of 
>Pythonic style. So this is a good fit for the Python of 2022 (what a 
>futuristic-looking year!) and beyond.

I believe that the "no hidden details" problem can be resolved by a combination 
of thoughtful help-string generation and a suitable syntax for type hints.

---

>From a "rank-and-file programmer" perspective, I don't think I want this in 
>Python. The functional languages that depend heavily on pattern matching 
>really embrace it as a core concept at the syntax level. I think I encounter a 
>use case like this maybe once a week at most, and each time I think "heh, 
>that'd be nice" and move on with my life. I want Python code (really all code) 
>to be as visually easy to parse as possible, so I can read it quickly and have 
>the damn syntax get out of my way. I think Python syntax right now has a nice 
>balance between concise elegance and comfortably boring predictability. I 
>wouldn't want to swing the pendulum too far toward the former, because it 
>would make my day job harder.

The benefits would be relatively minor or near-zero for ~90% of users, would be 
moderate at best for the 9% of users who do a lot of tuple-destructuring stuff 
for whatever reason, and would be an amazing victory for the 1% of users who 
really freakin' love unpacking and already overuse it. The cost would be a 
fixed and probably irreversible increase in syntactic complexity, burdening not 
only future code readers, future students, and the future teachers of those 
future students (!!), but also developers of language introspection and static 
analysis tools.

There is also the issue of playing nice with type hints. It's not just a matter 
of hidden details; how do you type-hint a function like `move((x, y))` without 
making a mess of symbols and nesting?

---

Might as well have some fun with the idea! Here are a couple of extensions I've 
thought of over the last few weeks.

1) As-patterns

One possibly interesting/useful extension is some ML-like "as-pattern" syntax 
to obtain the full argument as well as the destructured values:

def move(point :: (x: float, y: float), /) -> tuple[float, float]:
print(point)
return x + 1, y + 1

def move((x: float, y: float) as point, /) -> tuple[float, float]:
print(point)
return x + 1, y + 1

def move(point from (x: float, y: float), /) -> tuple[float, float]:
print(point)
return x + 1, y + 1

def move(point with (x: float, y: float), /) -> tuple[float, float]:
print(point)
return x + 1, y + 1

2) Dict unpacking. BTW I think this should also be part of SPM syntax.

def move({"x": x, "y": y}, /) -> tuple[float, float]:
return x + 1, y + 1

def move({"x": x, "y": y}, /) -> tuple[float, float]:
return x + 1, y + 1

move({"x": -1.5, "y": 1.5})

3) Full-scale match/case SPM syntax.

def move(Point(x=x, y=y), /) -> tuple[float, float]:
return x + 1, y + 1

move(Point(1.5, -1.5))

4) Any/all of the above on the left hand side of assignment statements.

weird = (
5,
{
"points": [Point(1,2), Point(3,4), Point(5,6)],
"other": "this will be discarded"
}
)
n, {"points": [Point(x, y), *other_points] as all_points} = weird
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/JER5XSUEO77GMNTJH72UOIVJMLGM3TSN/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Ricky Teachey
On Tue, Oct 26, 2021 at 10:44 PM Chris Angelico  wrote:

> On Wed, Oct 27, 2021 at 1:13 PM Ricky Teachey  wrote:
> >
> > I'll try to summarize why I still have pause even though after thinking
> about it I still can't really think of a solid example showing that this
> "give me the default" issue is a concrete problem:
> >
> > Until this point, exactly how to provide a default argument value has
> been the Wild West in python, and as Paul Moore said, there's really not a
> way to introspect whether a parameter was "defaulted". The result is that a
> cornucopia of APIs have flourished. By necessity, all these previous APIs
> provided ways to ask for the default through passing some special value,
> and this has felt like "the pythonic way"for a night time. We are all used
> to it (but perhaps only tolerated it in many cases).
> >
> > The proposal blesses a new API with language support, and it will
> suddenly become THE pythonic approach. But this newly blessed, pythonic API
> is a radical departure from years- decades- of coding habits.
> >
>
> That's a very good point, but I'd like to turn it on its head and
> explain the situation from the opposite perspective.
>
> For years - decades - Python has lacked a way to express the concept
> that a default argument is something more than a simple value. Coders
> have used a variety of workarounds. In the future, most or all of
> those workarounds will no longer be necessary, and we will have a
> single obvious way to do things.
>
> Suppose that, up until today, Python had not had support for big
> integers - that the only numbers representable were those that fit
> inside a 32-bit (for compatibility, of course) two's complement
> integer. People who need to work with larger numbers would use a
> variety of tricks: storing digits in strings and performing arithmetic
> manually, or using a tuple of integers to represent a larger number,
> or using floats and accepting a loss of precision. Then Python gains
> native support for big integers. Yes, it would be a radical departure
> from years of workarounds. Yes, some people would continue to use the
> other methods, because there would be enough differences to warrant
> it. And yes, there would be backward-incompatible API changes as edge
> cases get cleaned up. Is it worth it? For integers, I can respond with
> a resounding YES, because we have plenty of evidence that they are
> immensely valuable! With default argument expressions, it's less of an
> obvious must-have, but I do believe that the benefits outweigh the
> costs.
>
> Will the standard library immediately remove all "=None" workaround
> defaults? No. Probably some of them, but not all. Will there be
> breakage as a result of something passing None where it wanted the
> default? Probably - if not in the stdlib, then I am sure it'll happen
> in third-party code. Will future code be more readable as a result?
> Absolutely.
>
> ChrisA
>

If I might paraphrase Agrippa from the book of Acts: "Almost thou
persuadest me..." ;) It's a fine answer.
  
Thanks for the attentiveness to my concern, Chris. Very much appreciated!

---
Ricky.

"I've never met a Kentucky man who wasn't either thinking about going home
or actually going home." - Happy Chandler
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/UCQSCITR6RWUNLFZ73WAFCC2X3HCQO7M/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Rob Cliffe via Python-ideas

On 27/10/2021 03:12, Brendan Barnwell wrote:

On 2021-10-26 17:41, Christopher Barker wrote:

Python used to be such a simple language, not so much anymore :-(


I quite agree, and I feel like this is my biggest reason why I 
don't want this "feature" (or any of another gazillion features that 
have been suggested and/or accepted here, including the walrus).  The 
benefit of this PEP simply does not justify the added complexity of 
the language as a whole.  Using None (or some sentinel) and including 
code to check for it works fine.  It's not worth it to add a whole new 
layer of behavior to something as basic as argument-passing just to 
avoid having to type `if arg is None`.


While I disagree with you re this particular feature, I sympathise with 
the general sentiment.  Perhaps the first objective of Python 4 should 
be to get rid of as many features as possible.  Just like when you're 
forced to throw out all your precious old junk (school reports, prizes, 
the present from Aunt Maud, theatre programmes, books you never read, 
clothes you never wear .).


Nah, who am I kidding?  Each feature will have its band of devotees that 
will defend it TO THE DEATH!  Of course, what it should REALLY have are 
all MY favorite features, including some that haven't been added yet.

Rob Cliffe
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/SLBFP4DP4MKUOMSVWNCOREVMKXA3UIZ6/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 1:15 PM Brendan Barnwell  wrote:
>
> On 2021-10-26 17:41, Christopher Barker wrote:
> > Python used to be such a simple language, not so much anymore :-(
>
> I quite agree, and I feel like this is my biggest reason why I don't
> want this "feature" (or any of another gazillion features that have been
> suggested and/or accepted here, including the walrus).  The benefit of
> this PEP simply does not justify the added complexity of the language as
> a whole.  Using None (or some sentinel) and including code to check for
> it works fine.  It's not worth it to add a whole new layer of behavior
> to something as basic as argument-passing just to avoid having to type
> `if arg is None`.
>

>>> help(bisect.bisect)

bisect_right(a, x, lo=0, hi=None, *, key=None)
...
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.

In the docstring, both lo and hi are given useful, meaningful
defaults. In the machine-readable signature, which is also what would
be used for tab completion or other tools, lo gets a very useful
default, but hi gets a default of None.

For the key argument, None makes a very meaningful default; it means
that no transformation is done. But in the case of hi, the default
really and truly is len(a), but because of a technical limitation,
that can't be written that way.

Suppose this function were written as:

bisect_right(a, x, lo=None, hi=None, *, key=None)

Do we really need the ability to specify actual function defaults? I
mean, you can just have a single line of code "if lo is None: lo = 0"
and it'd be fine. Why do we need the ability to put "lo=0" in the
signature? I put it to you that the same justification should be valid
for hi.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/PC27XLYJVIC4TXHYJN5XLMR2ZQQLMVT5/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 1:13 PM Ricky Teachey  wrote:
>
> I'll try to summarize why I still have pause even though after thinking about 
> it I still can't really think of a solid example showing that this "give me 
> the default" issue is a concrete problem:
>
> Until this point, exactly how to provide a default argument value has been 
> the Wild West in python, and as Paul Moore said, there's really not a way to 
> introspect whether a parameter was "defaulted". The result is that a 
> cornucopia of APIs have flourished. By necessity, all these previous APIs 
> provided ways to ask for the default through passing some special value, and 
> this has felt like "the pythonic way"for a night time. We are all used to it 
> (but perhaps only tolerated it in many cases).
>
> The proposal blesses a new API with language support, and it will suddenly 
> become THE pythonic approach. But this newly blessed, pythonic API is a 
> radical departure from years- decades- of coding habits.
>

That's a very good point, but I'd like to turn it on its head and
explain the situation from the opposite perspective.

For years - decades - Python has lacked a way to express the concept
that a default argument is something more than a simple value. Coders
have used a variety of workarounds. In the future, most or all of
those workarounds will no longer be necessary, and we will have a
single obvious way to do things.

Suppose that, up until today, Python had not had support for big
integers - that the only numbers representable were those that fit
inside a 32-bit (for compatibility, of course) two's complement
integer. People who need to work with larger numbers would use a
variety of tricks: storing digits in strings and performing arithmetic
manually, or using a tuple of integers to represent a larger number,
or using floats and accepting a loss of precision. Then Python gains
native support for big integers. Yes, it would be a radical departure
from years of workarounds. Yes, some people would continue to use the
other methods, because there would be enough differences to warrant
it. And yes, there would be backward-incompatible API changes as edge
cases get cleaned up. Is it worth it? For integers, I can respond with
a resounding YES, because we have plenty of evidence that they are
immensely valuable! With default argument expressions, it's less of an
obvious must-have, but I do believe that the benefits outweigh the
costs.

Will the standard library immediately remove all "=None" workaround
defaults? No. Probably some of them, but not all. Will there be
breakage as a result of something passing None where it wanted the
default? Probably - if not in the stdlib, then I am sure it'll happen
in third-party code. Will future code be more readable as a result?
Absolutely.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/NYYYAXLQXN3N7OH4SD34QXS2BPETJCS7/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Rob Cliffe via Python-ideas



On 27/10/2021 00:26, Christopher Barker wrote:
I'm very confused about the apparent convergence on the token "=>" for 
deferred parameter assignment.


1)  As others have said, it sure feels like the arrow is going the 
wrong way.


But the bigger question I have is with the similarity to lambda:

2) As I understand it, there's a good chance that "=>" will be adopted 
to mean defining an anonymous function, i.e. a new spelling of lambda.


But we can use lambda as default arguments (indeed, that's a common 
idiom), and, presumably, lambda will be able to use deferred defaults. 
So there's a reasonable probability that the same token will be used 
in the same context, meaning two different things, maybe even at the 
same time.


I'm sure the parser will be able to figure it out, but I really don't 
get why folks think this is a good idea for human readability.


Can someone explain?

On the other hand, others have suggested :=, which is could also be 
used as part of the expression, so not a good idea either :-(

There's no necessary clash.  At present you can write
    f(a = (b:=c)):
so you would be able to write
    f(a:= (b:= c)):
Perhaps a tad confusing at first glance, but unambiguous.


BTW: was it intentional that this:

In [8]: def fun(x, y=(z:=3)):
   ...:     print(x,y,z)
   ...:
   ...:

adds z to the function namespace -- sure seems odd to me.

In [9]: fun(2)
2 3 3
Erm, where else should z go?  What would be the point of writing it if 
it didn't?

Rob Cliffe



-CHB

--
Christopher Barker, PhD (Chris)

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython

___
Python-ideas mailing list --python-ideas@python.org
To unsubscribe send an email topython-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived 
athttps://mail.python.org/archives/list/python-ideas@python.org/message/XHXGW2LCCWUF3FLWBMBH2HVKQXBUYJAH/
Code of Conduct:http://python.org/psf/codeofconduct/
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/SL652RVSUB5YYPO7CYLN3F2JCLUNGNML/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Ricky Teachey
On Tue, Oct 26, 2021, 9:54 PM Rob Cliffe via Python-ideas <
python-ideas@python.org> wrote:

>
>
> On 27/10/2021 01:47, Chris Angelico wrote:
> > The idea that a
> > parameter is optional, but doesn't have a value, may itself be worth
> > exploring (maybe some way to say "arg=pass" and then have an operator
> > "if unset arg:" that returns True if it's unbound), but that's for
> > another day.
> >
> > ChrisA
> Indeed.  And it could be useful to know if a parameter was passed a
> value or given the default value.
> Python has very comprehensive introspection abilities, but this is a
> (small) gap.
> Rob Cliffe
>

I'll try to summarize why I still have pause even though after thinking
about it I still can't really think of a solid example showing that this
"give me the default" issue is a concrete problem:

Until this point, exactly how to provide a default argument value has been
the Wild West in python, and as Paul Moore said, there's really not a way
to introspect whether a parameter was "defaulted". The result is that a
cornucopia of APIs have flourished. By necessity, all these previous APIs
provided ways to ask for the default through passing some special value,
and this has felt like "the pythonic way"for a night time. We are all used
to it (but perhaps only tolerated it in many cases).

The proposal blesses a new API with language support, and it will suddenly
become THE pythonic approach. But this newly blessed, pythonic API is a
radical departure from years- decades- of coding habits.

And so even though I like the proposal, I'm just concerned it could be a
little bit more painful than at first glance. So it just seems like some
version of these concerns belongs in the PEP.

Thanks Chris A for putting up with what isn't much more than a hunch (at
least on my part) and I'll say nothing more about it. Carry on.

>
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/OSJKSROAWNBHXRCI33V3HK6OEXV2IRA3/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Brendan Barnwell

On 2021-10-26 17:41, Christopher Barker wrote:

Python used to be such a simple language, not so much anymore :-(


	I quite agree, and I feel like this is my biggest reason why I don't 
want this "feature" (or any of another gazillion features that have been 
suggested and/or accepted here, including the walrus).  The benefit of 
this PEP simply does not justify the added complexity of the language as 
a whole.  Using None (or some sentinel) and including code to check for 
it works fine.  It's not worth it to add a whole new layer of behavior 
to something as basic as argument-passing just to avoid having to type 
`if arg is None`.


--
Brendan Barnwell
"Do not follow where the path may lead.  Go, instead, where there is no 
path, and leave a trail."

   --author unknown
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/SIMXWBX7GL4S2PRUIDBMAARSUPI33FMV/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 12:59 PM Chris Angelico  wrote:
>
> On Wed, Oct 27, 2021 at 12:50 PM Rob Cliffe  wrote:
> >
> >
> >
> > On 27/10/2021 00:50, Chris Angelico wrote:
> > > On Wed, Oct 27, 2021 at 10:38 AM Rob Cliffe  
> > > wrote:
> > >
> > >> There has been support for evaluating all early-bound defaults before
> > >> all late-bound defaults.  I have been persuaded that this is a
> > >> reasonable option.  AFAICS there would be little practical difference
> > >> from straight left-to-right evaluation of defaults, since assigning an
> > >> early-bound default should not have a side effect.  So it could even be
> > >> an implementation choice.
> > > If it's an implementation choice, then it'll mean that code is legal
> > > on some interpreters and not others. Whether that's a major problem or
> > > not I don't know, but generally, when you can't depend on your code
> > > working on all interpreters, it's not best practice (eg
> > > "open(fn).read()" isn't recommended, even if it happens to close the
> > > file promptly in CPython).
> > >
> > > ChrisA
> > I guess I wasn't clear.  I'm not sure if you understood what I meant to
> > say, or not.
> > First I should have said "binding" rather than "evaluating".
> > What I meant was that in cases like
> >  def f(a=earlydefault1, b:= latedefault2, c=earlydefault3,
> > d:=latedefault4):
> > it makes no real difference if the bindings are done in the order
> >  a, c, b, d (early ones before late ones)
> > or
> >  a, b, c, d (strict left-to-right)
> > since binding an early default should have no side effects, so (I
> > thought, wrongly) it could be an implementation detail.
> > Of course there IS a difference: it allows late default values to refer
> > to subsequent early default values, e.g. in the example above
> > `latedefault2' could refer to `c`.  So yes, then that code would be
> > legal on some interpreters and not others, as you said.  If you
> > understood exactly what I meant, I apologise.
>
> Yep, that's precisely the distinction that matters: whether it's legal
> to refer to parameters further to the right. If we consider tuple
> unpacking as an approximate parallel:
>
> def f():
> a = [10,20,30]
> i, a[i] = 1, "Hi"

Premature send, oops...

def f():
a = [10, 20, 30]
i, a[i] = 1, "Hi"
print(a)

It's perfectly valid to refer to something from earlier in the
multiple assignment, because they're assigned left to right. Python
doesn't start to look up the name 'a' until it's finished assigning to
'i'.

Since Python doesn't really have a concept of statics or calculated
constants, we don't really have any parallel, but imagine that we
could do this:

def f():
# Calculate this at function definition time and then save it
# as a constant
const pos = random.randrange(3)
a = [10, 20, 30]
i, a[i] = pos, "Hi"

This is something what I'm describing. The exact value of an
early-bound argument default gets calculated at definition time and
saved; then it gets assigned to its corresponding parameter if one
wasn't given.

(Actually, I'd really like Python to get something like this, as it'd
completely replace the "random=random" optimization - there'd be no
need to pollute the function's signature with something that exists
solely for the optimization. It'd also make some of these kinds of
things a bit easier to explain, since there would be a concept of
def-time evaluation separate from the argument list. But we have what
we have.)

So I think that we did indeed understand one another.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/CZN7C3N2NON3QBECGRRBYNL7FAJNOPAI/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 12:50 PM Rob Cliffe  wrote:
>
>
>
> On 27/10/2021 00:50, Chris Angelico wrote:
> > On Wed, Oct 27, 2021 at 10:38 AM Rob Cliffe  
> > wrote:
> >
> >> There has been support for evaluating all early-bound defaults before
> >> all late-bound defaults.  I have been persuaded that this is a
> >> reasonable option.  AFAICS there would be little practical difference
> >> from straight left-to-right evaluation of defaults, since assigning an
> >> early-bound default should not have a side effect.  So it could even be
> >> an implementation choice.
> > If it's an implementation choice, then it'll mean that code is legal
> > on some interpreters and not others. Whether that's a major problem or
> > not I don't know, but generally, when you can't depend on your code
> > working on all interpreters, it's not best practice (eg
> > "open(fn).read()" isn't recommended, even if it happens to close the
> > file promptly in CPython).
> >
> > ChrisA
> I guess I wasn't clear.  I'm not sure if you understood what I meant to
> say, or not.
> First I should have said "binding" rather than "evaluating".
> What I meant was that in cases like
>  def f(a=earlydefault1, b:= latedefault2, c=earlydefault3,
> d:=latedefault4):
> it makes no real difference if the bindings are done in the order
>  a, c, b, d (early ones before late ones)
> or
>  a, b, c, d (strict left-to-right)
> since binding an early default should have no side effects, so (I
> thought, wrongly) it could be an implementation detail.
> Of course there IS a difference: it allows late default values to refer
> to subsequent early default values, e.g. in the example above
> `latedefault2' could refer to `c`.  So yes, then that code would be
> legal on some interpreters and not others, as you said.  If you
> understood exactly what I meant, I apologise.

Yep, that's precisely the distinction that matters: whether it's legal
to refer to parameters further to the right. If we consider tuple
unpacking as an approximate parallel:

def f():
a = [10,20,30]
i, a[i] = 1, "Hi"
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/PUO7PPRSI7Q237V642OCC3ERTROWFGOV/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Rob Cliffe via Python-ideas



On 27/10/2021 01:47, Chris Angelico wrote:

The idea that a
parameter is optional, but doesn't have a value, may itself be worth
exploring (maybe some way to say "arg=pass" and then have an operator
"if unset arg:" that returns True if it's unbound), but that's for
another day.

ChrisA
Indeed.  And it could be useful to know if a parameter was passed a 
value or given the default value.
Python has very comprehensive introspection abilities, but this is a 
(small) gap.

Rob Cliffe

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/TSUPWXIDS5RLAKZU6DAXXCJ7HEGHQAVM/
Code of Conduct: http://python.org/psf/codeofconduct/


___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/W7R67WSGT73X3VL54U4OY3M3JAC6B3HR/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Rob Cliffe via Python-ideas



On 27/10/2021 00:50, Chris Angelico wrote:

On Wed, Oct 27, 2021 at 10:38 AM Rob Cliffe  wrote:


There has been support for evaluating all early-bound defaults before
all late-bound defaults.  I have been persuaded that this is a
reasonable option.  AFAICS there would be little practical difference
from straight left-to-right evaluation of defaults, since assigning an
early-bound default should not have a side effect.  So it could even be
an implementation choice.

If it's an implementation choice, then it'll mean that code is legal
on some interpreters and not others. Whether that's a major problem or
not I don't know, but generally, when you can't depend on your code
working on all interpreters, it's not best practice (eg
"open(fn).read()" isn't recommended, even if it happens to close the
file promptly in CPython).

ChrisA
I guess I wasn't clear.  I'm not sure if you understood what I meant to 
say, or not.

First I should have said "binding" rather than "evaluating".
What I meant was that in cases like
    def f(a=earlydefault1, b:= latedefault2, c=earlydefault3, 
d:=latedefault4):

it makes no real difference if the bindings are done in the order
    a, c, b, d (early ones before late ones)
or
    a, b, c, d (strict left-to-right)
since binding an early default should have no side effects, so (I 
thought, wrongly) it could be an implementation detail.
Of course there IS a difference: it allows late default values to refer 
to subsequent early default values, e.g. in the example above 
`latedefault2' could refer to `c`.  So yes, then that code would be 
legal on some interpreters and not others, as you said.  If you 
understood exactly what I meant, I apologise.

Rob Cliffe

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/7GKSZEUZG3O66T5CDU3YL67B6QYF2KHZ/
Code of Conduct: http://python.org/psf/codeofconduct/


___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/7HU3PG3SEV4DTNNKMQISKC3FLTJINHYZ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 11:41 AM Christopher Barker  wrote:
> I've been thinking about this from the perspective of a teacher or Python. 
> I"m not looking forward to having one more thing to teach about function 
> definitions -- I struggle enough with cover all of the *args, **kwargs, 
> keyword-only, positional-only options.
>
> Python used to be such a simple language, not so much anymore :-(
>

Yeah. My intention here is that this should be completely orthogonal
to argument types (positional-only, positional-or-keyword,
keyword-only), completely orthogonal to type hints (or, as I'm
discovering as I read through the grammar, type comments), and as much
as possible else. The new way will look very similar to the existing
way of writing defaults, because it's still defining the default.

> That being said, we currently have to teach, fairly early on, the 
> consequences of using a mutable as a default value. And this PEP would make 
> that easier to cover. But I think it's really important to keep the semantics 
> as simple as possible, and left-to-right name binding is they way to do that.
>

Yes. It will now be possible to say "to construct a new list every
time, write it like this" instead of drastically changing the style of
code.

def add_item(thing, target=[]):
print("Oops, that uses a single shared default target")
def add_item(thing, target=>[]):
print("If you don't specify a target, it makes a new list")

> (all this complicated by the fact that there is a LOT of code and advice in 
> the wild about the None idiom, but what can you do?)

And that's not all going away. There will always be some situations
where you can't define the default with an expression. The idea that a
parameter is optional, but doesn't have a value, may itself be worth
exploring (maybe some way to say "arg=pass" and then have an operator
"if unset arg:" that returns True if it's unbound), but that's for
another day.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/TSUPWXIDS5RLAKZU6DAXXCJ7HEGHQAVM/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Christopher Barker
On Tue, Oct 26, 2021 at 5:24 PM Chris Angelico  wrote:

> > How could that be avoided? by definition, early bound is evaluated
> "earlier" than  late-bound :-)
> >
> > This could cause a bit of confusion with "getting" that it's not a
> simple left-to-right rule, but that's the same potential confusion with
> early vs late bound parameters anyway.
>

sorry, got tangled up between "evaluating" and "name binding"


> The question is whether code like this should work:
>
> def f(a=>b + 1, b=2): ...
>
> f()
> f(b=4)
>
> Pure left-to-right assignment would raise UnboundLocalError in both
> cases. Tiered evaluation wouldn't.
>

Nice, simple example. I'm not a newbie, but my students are, and I think
they'd find "tiered" evaluation really confusing.

Are there any other places in Python where assignments aren't done
> left to right, but are done in two distinct phases?
>

I sure can't think of one.

I've been thinking about this from the perspective of a teacher or Python.
I"m not looking forward to having one more thing to teach about function
definitions -- I struggle enough with cover all of the *args, **kwargs,
keyword-only, positional-only options.

Python used to be such a simple language, not so much anymore :-(

That being said, we currently have to teach, fairly early on, the
consequences of using a mutable as a default value. And this PEP would make
that easier to cover. But I think it's really important to keep the
semantics as simple as possible, and left-to-right name binding is they way
to do that.

(all this complicated by the fact that there is a LOT of code and advice in
the wild about the None idiom, but what can you do?)

-CHB


-- 
Christopher Barker, PhD (Chris)

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/C5O7JBYNLCVBQGVGJMIKKVLJ4XFCFNJR/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 11:17 AM Christopher Barker  wrote:
>
> On Tue, Oct 26, 2021 at 4:46 PM Rob Cliffe via Python-ideas 
>  wrote:
>>
>> There has been support for evaluating all early-bound defaults before
>> all late-bound defaults.  I have been persuaded that this is a
>> reasonable option.
>
>
> How could that be avoided? by definition, early bound is evaluated "earlier" 
> than  late-bound :-)
>
> early-bound (i.e. regular) parameters are evaluated at function definition 
> time. But the time we get to the late-bound ones, those are actual values, 
> not expressions.
>
> The interpreter could notice that early bound names are used in late-bound 
> expressions and raise an error, but if not, there'd be no issue with when 
> they were evaluated.
>
> This could cause a bit of confusion with "getting" that it's not a simple 
> left-to-right rule, but that's the same potential confusion with early vs 
> late bound parameters anyway.
>

The question is whether code like this should work:

def f(a=>b + 1, b=2): ...

f()
f(b=4)

Pure left-to-right assignment would raise UnboundLocalError in both
cases. Tiered evaluation wouldn't.

Are there any other places in Python where assignments aren't done
left to right, but are done in two distinct phases?

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/55DR7BD5BHOUW3KF37EYX3PPS2E622OE/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Christopher Barker
On Tue, Oct 26, 2021 at 4:46 PM Rob Cliffe via Python-ideas <
python-ideas@python.org> wrote:

> There has been support for evaluating all early-bound defaults before
> all late-bound defaults.  I have been persuaded that this is a
> reasonable option.


How could that be avoided? by definition, early bound is evaluated
"earlier" than  late-bound :-)

early-bound (i.e. regular) parameters are evaluated at function definition
time. But the time we get to the late-bound ones, those are actual values,
not expressions.

The interpreter could notice that early bound names are used in late-bound
expressions and raise an error, but if not, there'd be no issue with when
they were evaluated.

This could cause a bit of confusion with "getting" that it's not a simple
left-to-right rule, but that's the same potential confusion with early vs
late bound parameters anyway.

-CHB

-- 
Christopher Barker, PhD (Chris)

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/5757MQ33UV2NZDFGVS4P2N2WI6TGHZYC/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 10:40 AM Christopher Barker  wrote:
>
> subclassing and defaults.
>
> There is one use case I'm not sure how to wrap my head around.
>
> Say a class and subclass have different default arguments. And we want to 
> pass the fact that the argument wasn't set along to the superclass. The "use 
> None" convention works fine:
>
> def __init__(self, something=None):
> super().__init__(something)
>
> The superclass could define its own default.
>
> But deferred binding would not work:
>
> def __init__(self, something=an_expression):
> super().__init__(something)
>
> The superclass would get the result of an_expression, and not know that it 
> should use its default.
>
> Is this an actual problem? I'm not sure -- I can't think of when I wouldn't 
> want a subclass to override the default of the superclass, but maybe ? And of 
> course, if you really do need that, then use a sentinel in that case :-) No 
> one's suggesting that every default should be specified explicitly!
>

Hmm. Trying to figure out what this would mean in practice. Let's get
some concrete names for a moment.

class Pizza:
def __init__(self, cheese="mozzarella"):
...
class WeirdPizza(Pizza):
def __init__(self, cheese="brie"): # you monster
...

Pizza("cheddar") # Legal, makes a cheddar pizza
WeirdPizza("cheddar") # Legal, makes a cheddar weirdpizza
Pizza() # Standard pizza
WeirdPizza() # Nonstandard pizza

If you're overriding the default, then you definitely want to use the
subclass's default. So in this simple case, no problem.

Another case is where you're trying to specify that the subclass
should retain the superclass's default. But in that sort of situation,
you probably want to retain ALL unspecified defaults - and that's best
spelled *a,**kw, so that's fine too.

All I can think of is that you want to replace some, but not all, of
the defaults. That'd be a bit clunky, but if your API is complicated
like that, it should probably be made entirely kwonly for safety,
which means you can write code like this:

def __init__(self, *, tomato="ketchup", **kw):
super().__init__(self, tomato=tomato, **kw)

A little clunky, but it lets you change one default, while keeping
others with the same defaults.

It would be nice to have a way to daisy-chain function signatures
(think like functools.wraps but merging the signatures), but that
would be quite a big thing to try to build. Would be an interesting
idea to tackle though.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/QP6NAPECPO42VAGIC7IGKWUKAZHZHA3F/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 10:38 AM Rob Cliffe  wrote:
>
>
>
> On 26/10/2021 18:25, Chris Angelico wrote:
> > On Tue, Oct 26, 2021 at 11:10 PM Steven D'Aprano  
> > wrote:
> >
> >>> Based on the multi-pass assignment model, which you still favour,
> >>> those WOULD be quite inconsistent, and some of them would make little
> >>> sense. It would also mean that there is a distinct semantic difference
> >>> between:
> >>>
> >>> def f1(x=>y + 1, y=2): ...
> >>> def f2(x=>y + 1, y=>2): ...
> >> Sure. They behave differently because they are different.
> >>
> >> These are different too:
> >>
> >>  # Block 1
> >>  y = 2
> >>  x = y + 1
> >>
> >>  # Block 2
> >>  x = y + 1
> >>  y = 2
> > Yes, those ARE different. Those are more equivalent to changing the
> > order of the parameters in the function signature, and I think we all
> > agree that that DOES make a difference. The question is whether these
> > could change meaning if you used a different type of assignment, such
> > as:
> >
> > y := 2
> > x = y + 1
> >
> > Does that suddenly make it legal? I think you'll find that this sort
> > of thing is rather surprising. And that's what we have here: changing
> > from one form of argument default to another changes whether
> > left-to-right applies or not.
> >
> > I don't want that. And based on an experiment with a less-experienced
> > Python programmer (admittedly only a single data point), neither do
> > other people. Left-to-right makes sense; multi-pass does not.
> As I may be the data point in question:

You're not - I sat down with one of my brothers and led him towards
the problem in question, watching his attempts to solve it. Then
showed him what we were discussing, and asked his interpretations of
it.

> One of my posts seems to have
> got lost again, so I reproduce some of it (reworked):
> What I DON'T want to see is allowing something like this being legal:
>  def f(a := b+1, b := e+1, c := a+1, d := 42, e := d+1):
> If no arguments are passed, the interpreter has to work out to evaluate
> first d, then e, then b, then a, then finally c.  If some arguments are
> passed, I guess the same order would work.  But it feels ... messy.  And
> obfuscated.

At no point will the interpreter reorder things. There have only ever
been two (or three) options seriously considered:

1) Assign all parameters from left to right, giving them either a
passed-in value or a default
2) First assign all parameters that were passed in values, and those
with early-bound defaults; then, in a separate left-to-right pass,
assign all late-bound defaults
3) Same as one of the other two, but validated at compilation time and
raising SyntaxError for out-of-order references

> And if this is legal (note: it IS a legitimate use case):
>  def DrawCircle(centre=(0,0), radius := circumference / TWO_PI,
> circumference := radius * TWO_PI):
> the interpreter has to work out whether to evaluate the 2nd or 3rd arg
> first, depending on which is passed.
> AFAICS all this may need multiple passes though the args at runtime.
> Complicated, and inefficient.  *If* it could all be sorted out at
> compile time, my objection would become weaker.

This is not as legit as you might think, since it runs into other
problems. Having codependent arguments is not going to be solved by
this proposal (for instance, what happens if you pass a radius of 3
and a circumference of 12?), so I'm not going to try to support narrow
subsets of this that just happen to "work" (like the case where you
only ever pass one of them).

> There has been support for evaluating all early-bound defaults before
> all late-bound defaults.  I have been persuaded that this is a
> reasonable option.  AFAICS there would be little practical difference
> from straight left-to-right evaluation of defaults, since assigning an
> early-bound default should not have a side effect.  So it could even be
> an implementation choice.

If it's an implementation choice, then it'll mean that code is legal
on some interpreters and not others. Whether that's a major problem or
not I don't know, but generally, when you can't depend on your code
working on all interpreters, it's not best practice (eg
"open(fn).read()" isn't recommended, even if it happens to close the
file promptly in CPython).

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/7GKSZEUZG3O66T5CDU3YL67B6QYF2KHZ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Christopher Barker
subclassing and defaults.

There is one use case I'm not sure how to wrap my head around.

Say a class and subclass have different default arguments. And we want to
pass the fact that the argument wasn't set along to the superclass. The
"use None" convention works fine:

def __init__(self, something=None):
super().__init__(something)

The superclass could define its own default.

But deferred binding would not work:

def __init__(self, something=an_expression):
super().__init__(something)

The superclass would get the result of an_expression, and not know that it
should use its default.

Is this an actual problem? I'm not sure -- I can't think of when I wouldn't
want a subclass to override the default of the superclass, but maybe ? And
of course, if you really do need that, then use a sentinel in that case :-)
No one's suggesting that every default should be specified explicitly!

-CHB


On Tue, Oct 26, 2021 at 4:32 PM Christopher Barker 
wrote:

> Well, you just repeated what I said, and then again asserted:
>
> On Tue, Oct 26, 2021 at 4:21 PM Chris Angelico  wrote:
>
>> The truth is that there is no value that can be a truly universal
>> representation of absence, so it *always* has to be specific to each
>> API.
>>
>
> But I don't see what that's the case -- why would anyone ever need to use
> a MISSING to mean anything else? If you have a different meaning, use a
> different sentinel. Sure, that wouldn't be enforceable, but it sure could
> be considered best practice.
>
> Though i suppose I'm missing something here.
>
> could it be a soft keyword?
>
> Using *a, **kw does allow us to truly omit an argument.
>>
>
> Exactly, I don't really see why this is considered important.
>
> -CHB
>
> --
> Christopher Barker, PhD (Chris)
>
> Python Language Consulting
>   - Teaching
>   - Scientific Software Development
>   - Desktop GUI and Web Development
>   - wxPython, numpy, scipy, Cython
>


-- 
Christopher Barker, PhD (Chris)

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/VQXZD2SJATYR24MAKANCTVR56ZMYHVSG/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Christopher Barker
On Tue, Oct 26, 2021 at 4:42 PM Chris Angelico  wrote:

> Oops, I missed seeing that that's actually an early-bound, so my
> response was on the misinterpretation that the function was written
> thus:
>
> def fun(x, y=>(z:=3)):
>
> In which case it *would* add it to the function's namespace.
>

That's OK -- you pre-answered my next question :-)

-CHB

-- 
Christopher Barker, PhD (Chris)

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/Z46M3NVLCWA53W75SAMMJZKFGKKZYFJ4/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Christopher Barker
On Tue, Oct 26, 2021 at 4:37 PM Carl Meyer  wrote:

> On Tue, Oct 26, 2021 at 5:29 PM Christopher Barker 
> wrote:
> > BTW: was it intentional that this:
> >
> > In [8]: def fun(x, y=(z:=3)):
> >...: print(x,y,z)
> >...:
> >...:
>
> It doesn't. It adds it to the namespace in which the function is
> defined, which is what you'd expect given when function defaults are
> currently evaluated (at function definition time).
>

indeed:

> 1 fun(2)

 in fun(x, y)
  1 def fun(x, y=(z:=3)):
> 2 z += 1
  3 print(x,y,z)
  4
  5

UnboundLocalError: local variable 'z' referenced before assignment

Sorry for the brain fart. Though it does point out the dangers of the
walrus operator ...

-CHB

-- 
Christopher Barker, PhD (Chris)

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/PU57NRCP253YONQKSTCSUQKB472PHLRB/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Rob Cliffe via Python-ideas



On 26/10/2021 18:25, Chris Angelico wrote:

On Tue, Oct 26, 2021 at 11:10 PM Steven D'Aprano  wrote:


Based on the multi-pass assignment model, which you still favour,
those WOULD be quite inconsistent, and some of them would make little
sense. It would also mean that there is a distinct semantic difference
between:

def f1(x=>y + 1, y=2): ...
def f2(x=>y + 1, y=>2): ...

Sure. They behave differently because they are different.

These are different too:

 # Block 1
 y = 2
 x = y + 1

 # Block 2
 x = y + 1
 y = 2

Yes, those ARE different. Those are more equivalent to changing the
order of the parameters in the function signature, and I think we all
agree that that DOES make a difference. The question is whether these
could change meaning if you used a different type of assignment, such
as:

y := 2
x = y + 1

Does that suddenly make it legal? I think you'll find that this sort
of thing is rather surprising. And that's what we have here: changing
from one form of argument default to another changes whether
left-to-right applies or not.

I don't want that. And based on an experiment with a less-experienced
Python programmer (admittedly only a single data point), neither do
other people. Left-to-right makes sense; multi-pass does not.
As I may be the data point in question: One of my posts seems to have 
got lost again, so I reproduce some of it (reworked):

What I DON'T want to see is allowing something like this being legal:
    def f(a := b+1, b := e+1, c := a+1, d := 42, e := d+1):
If no arguments are passed, the interpreter has to work out to evaluate 
first d, then e, then b, then a, then finally c.  If some arguments are 
passed, I guess the same order would work.  But it feels ... messy.  And 
obfuscated.

And if this is legal (note: it IS a legitimate use case):
    def DrawCircle(centre=(0,0), radius := circumference / TWO_PI, 
circumference := radius * TWO_PI):
the interpreter has to work out whether to evaluate the 2nd or 3rd arg 
first, depending on which is passed.
AFAICS all this may need multiple passes though the args at runtime.  
Complicated, and inefficient.  *If* it could all be sorted out at 
compile time, my objection would become weaker.


There has been support for evaluating all early-bound defaults before 
all late-bound defaults.  I have been persuaded that this is a 
reasonable option.  AFAICS there would be little practical difference 
from straight left-to-right evaluation of defaults, since assigning an 
early-bound default should not have a side effect.  So it could even be 
an implementation choice.


Best wishes
Rob

PS Can I echo Guido's plea that people don't derail this PEP by trying 
to shoehorn deferred-evaluation-objects (or whatever you want to call 
them) into it?  As Chris A says, that's a separate idea and should go 
into a separate PEP.  If I need a screwdriver, I buy a screwdriver, not 
an expensive Swiss Army knife.

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/24LBACW5J4JXGTAXF5BLDPGMB3LU2HBI/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 10:37 AM Carl Meyer  wrote:
>
> On Tue, Oct 26, 2021 at 5:29 PM Christopher Barker  
> wrote:
> > BTW: was it intentional that this:
> >
> > In [8]: def fun(x, y=(z:=3)):
> >...: print(x,y,z)
> >...:
> >...:
> >
> > adds z to the function namespace -- sure seems odd to me.
> >
> > In [9]: fun(2)
> > 2 3 3
>
> It doesn't. It adds it to the namespace in which the function is
> defined, which is what you'd expect given when function defaults are
> currently evaluated (at function definition time).
>
> It's just that if `z` is referenced in the function body and isn't a
> local, it gets looked up in the enclosing namespace (either as a
> global, or via closure if the function is nested.)

Oops, I missed seeing that that's actually an early-bound, so my
response was on the misinterpretation that the function was written
thus:

def fun(x, y=>(z:=3)):

In which case it *would* add it to the function's namespace.

As it currently is, yes, that's added to the same namespace that fun is.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/AFOUMINJNFVBRH5ROFT6USLDV6O6P2PV/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Carl Meyer
On Tue, Oct 26, 2021 at 5:29 PM Christopher Barker  wrote:
> BTW: was it intentional that this:
>
> In [8]: def fun(x, y=(z:=3)):
>...: print(x,y,z)
>...:
>...:
>
> adds z to the function namespace -- sure seems odd to me.
>
> In [9]: fun(2)
> 2 3 3

It doesn't. It adds it to the namespace in which the function is
defined, which is what you'd expect given when function defaults are
currently evaluated (at function definition time).

It's just that if `z` is referenced in the function body and isn't a
local, it gets looked up in the enclosing namespace (either as a
global, or via closure if the function is nested.)

Carl
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/STHTTDVN5D6A5LIBR7PTUOXMEQD2GDXT/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 10:27 AM Christopher Barker  wrote:
>
> I'm very confused about the apparent convergence on the token "=>" for 
> deferred parameter assignment.

The current text of the PEP uses that. It's not particularly important
to me *what* symbol is used, so I just use the same one as in the PEP,
for some measure of consistency.

> On the other hand, others have suggested :=, which is could also be used as 
> part of the expression, so not a good idea either :-(

Yes. I originally was positing =: and another viable suggestion is ?=.
There are a few other options listed in the PEP, and I'm happy to hear
arguments for and against.

> BTW: was it intentional that this:
>
> In [8]: def fun(x, y=(z:=3)):
>...: print(x,y,z)
>...:
>...:
>
> adds z to the function namespace -- sure seems odd to me.
>

If it doesn't add it to the function namespace, what does it add it
to? Do parameters get their own namespace? You can reassign your
parameters in the function without a nonlocal declaration, which
strongly suggests that they're in the exact same namespace as
assignments made in the function body; so I don't see any particular
reason to isolate this one example.

Of course, this wouldn't be *good* code, but it should be acceptable
by the interpreter. (For starters, it will only assign z if y is
omitted.)

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/A3FPGWE3AD5GG4R7DR2OEGMU6IZDIQNZ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread David Mertz, Ph.D.
On Tue, Oct 26, 2021, 7:20 PM Chris Angelico

> The truth is that there is no value that can be a truly universal
> representation of absence, so it *always* has to be specific to each API.
>

That's a fact about Python rather than a fact of programming in general.
For example, R has a NULL that is "even more missing" than it's NA or NaN.

For example:

# operation on NULL Vector
v1 <- c(NULL, NULL, NULL)
str(v1)
# NULL

In contrast, a vector of NA would still have 3 elements (or however many).
NA is basically Python None, NULL is like a true missing that cannot be
anything else.

I'm not saying Python should add this, but it is possible to do.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/YFYXRO4FNSWY3CMUZ42RLRQ7NZHCYOHE/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Christopher Barker
Well, you just repeated what I said, and then again asserted:

On Tue, Oct 26, 2021 at 4:21 PM Chris Angelico  wrote:

> The truth is that there is no value that can be a truly universal
> representation of absence, so it *always* has to be specific to each
> API.
>

But I don't see what that's the case -- why would anyone ever need to use a
MISSING to mean anything else? If you have a different meaning, use a
different sentinel. Sure, that wouldn't be enforceable, but it sure could
be considered best practice.

Though i suppose I'm missing something here.

could it be a soft keyword?

Using *a, **kw does allow us to truly omit an argument.
>

Exactly, I don't really see why this is considered important.

-CHB

-- 
Christopher Barker, PhD (Chris)

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/EICFNG5ZHNI6QTOKWM3UFHMD6YAPRBK2/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Christopher Barker
I'm very confused about the apparent convergence on the token "=>" for
deferred parameter assignment.

1)  As others have said, it sure feels like the arrow is going the wrong
way.

But the bigger question I have is with the similarity to lambda:

2) As I understand it, there's a good chance that "=>" will be adopted to
mean defining an anonymous function, i.e. a new spelling of lambda.

But we can use lambda as default arguments (indeed, that's a common idiom),
and, presumably, lambda will be able to use deferred defaults. So there's a
reasonable probability that the same token will be used in the same
context, meaning two different things, maybe even at the same time.

I'm sure the parser will be able to figure it out, but I really don't get
why folks think this is a good idea for human readability.

Can someone explain?

On the other hand, others have suggested :=, which is could also be used as
part of the expression, so not a good idea either :-(

BTW: was it intentional that this:

In [8]: def fun(x, y=(z:=3)):
   ...: print(x,y,z)
   ...:
   ...:

adds z to the function namespace -- sure seems odd to me.

In [9]: fun(2)
2 3 3


-CHB

-- 
Christopher Barker, PhD (Chris)

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/XHXGW2LCCWUF3FLWBMBH2HVKQXBUYJAH/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 10:14 AM Christopher Barker  wrote:
>
> Hmm, it seems our notes crossed paths, sorry.
>
> On Tue, Oct 26, 2021 at 2:12 PM Chris Angelico  wrote:
>>
>> The trouble with sentinel values is that you always need another one.
>> Sooner or later, you're going to need to talk about the _missing
>> object, and you'll need to distinguish it from the absence of an
>> object.
>>
>> If there is a way to say "don't pass this argument", it would have to
>> be some kind of syntactic token.
>
>
> I don't think that's true. even now, folks can misuse None, and True and 
> False in all sorts of ways, but if a singleton is well documented to mean 
> "missing argument" then anyone who uses it some other way gets what they 
> deserve.
>
> The reason None can't be counted on to mean MISSING is that is, in fact, used 
> in other ways already.
>

None is a great sentinel for a lot of cases, but it can't do
everything. If your API documents that None means absent, then sure,
that works fine! But sometimes you can't, because None has other
meaning. If you create a single global sentinel for _missing, sooner
or later, it will become necessary to distinguish it from actual
absence - for instance, when iterating over the builtins and doing
something with each object.

The truth is that there is no value that can be a truly universal
representation of absence, so it *always* has to be specific to each
API.

Using *a, **kw does allow us to truly omit an argument.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/ECOQ26OY3LPILMHSVSYU6ZVVKI5SKYJ2/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Christopher Barker
Hmm, it seems our notes crossed paths, sorry.

On Tue, Oct 26, 2021 at 2:12 PM Chris Angelico  wrote:

> The trouble with sentinel values is that you always need another one.
> Sooner or later, you're going to need to talk about the _missing
> object, and you'll need to distinguish it from the absence of an
> object.
>
> If there is a way to say "don't pass this argument", it would have to
> be some kind of syntactic token.
>

I don't think that's true. even now, folks can misuse None, and True and
False in all sorts of ways, but if a singleton is well documented to mean
"missing argument" then anyone who uses it some other way gets what they
deserve.

The reason None can't be counted on to mean MISSING is that is, in fact,
used in other ways already.

-CHB


-- 
Christopher Barker, PhD (Chris)

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/XMV6TDO62ONR6EKMWWYIV2HVPXDABZZR/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Christopher Barker
On Tue, Oct 26, 2021 at 1:35 PM Ethan Furman  wrote:

>  > to PASS SOMETHING that says "give me the default". With this proposed
> API, we don't
>  > have that; the only want to say "give me the default" is to NOT pass
> something.
>

but that, in fact, is all that Python provides. yes, None indicating "use
the default" is a very common idiom, but it is only an convention, and you
can't be sure it will work with every API.  Not passing a value is the only
way to indicate that, well, you have not passed a value ;-)

I think this ideas is addressing two related issues:

1) we have to write extra boilerplate code to process mutable (or
calculated) default arguments.
2) There is no well defined way to specify "not specified" -- None is a
convention, but it isnot used everywhere, and there are some places it
can't be used, as None is heavily overloaded and is a valid value in some
cases.

When this PEP originally came out I thought that passing None was the way
> to trigger it -- if that's not the case, and there is nothing we can pass
> to trigger it, I am much less interested.
>

See (2) above -- we really should not make None an "official" way to
specify not-set, or use-default.

If we wanted to, we could create a new standard sentinel for that, say
"MISSING", and then allow folks to use that, or omit the argument. But
frankly, I don't see the point. we already have the args tuple and kwargs
dict -- it is not hard to omit items from those.

But in a previous thread, I argued for a handful of standard sentinels so
that we wouldn't have to overload None so much -- so MISSINGdoes have it's
appeal.

-CHB

-- 
Christopher Barker, PhD (Chris)

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/MSU2PLOCJKFORHZWYECJEKH3CWXCBUEV/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Abdur-Rahmaan Janhangeer
Greetings, this use case interests me.

Is anyone interested in coauthoring this with me? Anyone who has

> strong interest in seeing this happen - whether you've been around the
> Python lists for years, or you're new and interested in getting
> involved for the first time, or anywhere in between!
>


Did you find a co-author yet? If not i apply.

Yours,

Abdur-Rahmaan Janhangeer

>
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/TAKOKKIZIKOA27JY6H4QTP5JROY6HENT/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 8:06 AM Erik Demaine  wrote:
> I wonder if it would make sense to offer a "missing argument" object (builtin?
> attribute of inspect.Parameter? attribute of types.FunctionType?) that
> actually simulates the behavior of that argument not being passed.  Let me
> call it `_missing` for now.  This would actually make it far easier to
> accomplish "pass in the second argument but not the first", both with early-
> and late-binding defaults:
>
> ```
> f(_missing, {'more'})
> g(_missing, {'more'})
> ```
>
> I started thinking about `_missing` when thinking about how to implement
> late-binding defaults.  It's at least one way to do it (then the function
> itself could even do the argument checks), though perhaps there are simpler
> ways that avoid the ref count increments.
>

The trouble with sentinel values is that you always need another one.
Sooner or later, you're going to need to talk about the _missing
object, and you'll need to distinguish it from the absence of an
object.

If there is a way to say "don't pass this argument", it would have to
be some kind of syntactic token.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/MSAWB5ZYZZOKYQ3JSC7C7Q2FK5PEVG4G/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Erik Demaine

On Tue, 26 Oct 2021, Ricky Teachey wrote:


At bottom I guess I'd describe the problem this way: with most APIs, there is a
way to PASS SOMETHING that says "give me the default". With this proposed API, 
we
don't have that; the only want to say "give me the default" is to NOT pass
something.

I don't KNOW if that's a problem, it just feels like one.


I agree that it's annoying, but it's actually an existing problem with 
early-bound defaults too.  Consider:


```
def f(eggs = [], spam = {}): ...
```

There isn't an easy way to get the defaults for the arguments, because they're 
not just *any* `[]` or `{}`, they're a specific list and dict.  So if you want 
to specify a value for the second argument but not the first, you'd need to do 
one of the following:


```
f(spam = {'more'})

f(f.__defaults__[0], {'more'})
```

The former would work just as well with PEP 671.

The latter depends on introspection, which we're still working out. 
Unfortunately, even if we can get access to the code that produces the 
default, we won't be able to actually call it, because it needs to be called 
from the function's scope.  For example, consider:


```
def g(eggs := [], spam := {}): ...
```

In this simple case, there are no dependencies, so we could do something like 
this:


```
g(g.__defaults__[0](), {'more'})
```

But in general we won't be able to make this call, because we don't have the 
scope until `g` gets called and its scope created...


So there is a bit of functionality loss with PEP 671, though I'm not sure it's 
that big a deal.



I wonder if it would make sense to offer a "missing argument" object (builtin? 
attribute of inspect.Parameter? attribute of types.FunctionType?) that 
actually simulates the behavior of that argument not being passed.  Let me 
call it `_missing` for now.  This would actually make it far easier to 
accomplish "pass in the second argument but not the first", both with early- 
and late-binding defaults:


```
f(_missing, {'more'})
g(_missing, {'more'})
```

I started thinking about `_missing` when thinking about how to implement 
late-binding defaults.  It's at least one way to do it (then the function 
itself could even do the argument checks), though perhaps there are simpler 
ways that avoid the ref count increments.


Erik
--
Erik Demaine  |  edema...@mit.edu  |  http://erikdemaine.org/
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/3DLKREVEG62RHDHY4KP2R6IX2PPA633F/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Ethan Furman

On 10/26/21 12:08 PM, Ricky Teachey wrote:
> On Tue, Oct 26, 2021 at 2:40 PM Chris Angelico wrote:

>> Do you have any examples where this isn't the case?
>
> I don't. I only have a niggling feeling that maybe this is a bigger problem 
than
> we're giving it credit for.
>
> At bottom I guess I'd describe the problem this way: with most APIs, there is 
a way
> to PASS SOMETHING that says "give me the default". With this proposed API, we 
don't
> have that; the only want to say "give me the default" is to NOT pass 
something.
>
> I don't KNOW if that's a problem, it just feels like one.

Several times I've had to write code that calls a function in several different ways, based solely on where I could pass 
None to get the default behavior:


my_wrapper_func(this, that, the_other=None):
if framework.version > 4:
framework.utility(this, that, the_other)
elif framework.version > 3.5:
if the_other is None:
   framework.utility(this, that)
else:
   framework.utility(this, that, the_other)

What a pain.

When this PEP originally came out I thought that passing None was the way to trigger it -- if that's not the case, and 
there is nothing we can pass to trigger it, I am much less interested.


--
~Ethan~
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/YBRFL4XISDGXKCVXTBR4WVS3UJWP4DRR/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 6:15 AM Paul Moore  wrote:
> Having the code stop working just because I changed the way the called
> function handles its defaults would be a nuisance (again, I'm not
> saying this would happen often, just that I could easily imagine it
> happening if this feature was available). The function f might well be
> from a library, or a script, that I wrote for something else, and it
> might be perfectly reasonable to use the new feature for that other
> use case.

The same will be true if it changes from "x=None" to "x=_sentinel".
Every change breaks someone's workflow.

I think Python would benefit from a "map these arguments to that
function signature" tool, but that's independent of this proposal.
Don't want to have massive scope creep here.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/BIEKFSLPA4OBPZAATTZTZKFL2XGA5UXU/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Paul Moore
On Tue, 26 Oct 2021 at 19:25, Chris Angelico  wrote:
>
> On Wed, Oct 27, 2021 at 5:05 AM Paul Moore  wrote:
> >
> > On Tue, 26 Oct 2021 at 16:48, Eric V. Smith  wrote:
> > >
> > > And also the "No Loss of Abilities If Removed" section sort of applies
> > > to late-bound function arguments: there's nothing proposed that can't
> > > currently be done in existing Python. I'll grant you that they might
> > > (might!) be more newbie-friendly, but I think the bar is high for
> > > proposals that make existing things doable in a different way, as
> > > opposed to proposals that add new expressiveness to the language.
> >
> > One issue with not having an introspection capability, which has been
> > bothering me but I've not yet had the time to come up with a complete
> > example, is the fact that with this new feature, you have functions
> > where there's no way to express "just use the default" without knowing
> > what the default actually *is*.
> >
> > Take for example
> >
> > def f(a, b=None):
> > if b is None:
> > b = len(a)
> > ...
> >
> > def g(a, b=>len(a)):
> > ...
> >
> > Suppose you want to call f as follows:
> >
> > args = [
> > ([1,2,3], 2),
> > ([4,5,6], None),
> > ([7,8,9], 4),
> > ]
> >
> > for a, b in args:
> > f(a, b)
> >
> > That works fine. But you cannot replace f by g, because None doesn't
> > mean "use the default", and in fact by design there's *nothing* that
> > means "use the default" other than "know what the default is and
> > supply it explicitly". So if you want to do something similar with g
> > (allowing the use of None in the list of tuples to mean "use the
> > default"), you need to be able to introspect g to know what the
> > default is. You may also need to manipulate first-class "deferred
> > expression" objects as well, just to have something you can return as
> > the default value (you could return a string and require the user to
> > eval it, I guess, but that doesn't seem particularly user-friendly...)
> >
> > I don't have a good solution for this, unfortunately. And maybe it's
> > something where a "good enough" solution would be sufficient. But
> > definitely, it should be discussed in the PEP so what's being proposed
> > is clear.
> >
>
> Wouldn't cases like this be most likely to use *args and/or **kwargs?
> Simply omitting the argument from those would mean "use the default".
> Or am I misunderstanding your example here?

Maybe. I don't want to make more out of this issue than it warrants.
But I will say that I genuinely would write code like I included
there. The reason comes from the fact that I do a lot of ad-hoc
scripting, and "copy this text file of data, edit it to look like a
Python list, paste it into my code" is a very common approach. I
wouldn't bother writing anything generic like f(*args), because it's
just a "quick hack". (I'd rewrite it to use f(*args) when I'd done the
same copy/paste exercise 20 times, and I was fed up enough to insist
that I was allowed the time to "write it properly this time" ;-))

Having the code stop working just because I changed the way the called
function handles its defaults would be a nuisance (again, I'm not
saying this would happen often, just that I could easily imagine it
happening if this feature was available). The function f might well be
from a library, or a script, that I wrote for something else, and it
might be perfectly reasonable to use the new feature for that other
use case.

There's no doubt that this is an artificial example - I'm not trying
to pretend otherwise. But it's a *plausible* one, in the sort of
coding I do regularly in my day job. And it's the sort of awkward edge
case where you'd expect there to be support for it, by analogy with
similar features elsewhere, so it would feel like a "wart" when you
found out you couldn't do it. As I said, I'm not demanding a solution,
but I would like to see it acknowledged and discussed in the PEP, just
so the trade-offs are clear to people.

Paul
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/A4IVU75OU5D4KTQP3OOR2V7DKA5E22EN/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Ricky Teachey
On Tue, Oct 26, 2021 at 2:40 PM Chris Angelico  wrote:

> On Wed, Oct 27, 2021 at 5:30 AM Ricky Teachey  wrote:
> > But with function k below, where the b parameter is deferred, you can't
> get the default b parameter by dynamically unpacking some values; you would
> have to pass c as a kwd arg:
> >
> > def k(a, b=>len(a), c=None):
> > ...
> >
> > Seems like it would be- needed? convenient?- to be able to "ask" for the
> default in a dynamic way... Am I making more of this than is justified?
> >
>
> Question: Does it make sense to ask for the default for the second
> positional parameter, while passing an actual value for the third? If
> it does, then the function needs an API that reflects this. The most
> obvious such API is what you already described: passing c as a
> keyword argument instead. I don't think this is a problem. When you're
> looking at positional args, it is only ever the last N that can be
> omitted. If it makes sense to pass any combination of arguments, then
> they should probably be keyword-only, to clarify this.
>
> Do you have any examples where this isn't the case?
>
> ChrisA
>

I don't. I only have a niggling feeling that maybe this is a bigger problem
than we're giving it credit for.

If I can, I'll try to substantiate it better. Maybe others can better flesh
out the concern here if it's valid.

At bottom I guess I'd describe the problem this way: with most APIs, there
is a way to PASS SOMETHING that says "give me the default". With this
proposed API, we don't have that; the only want to say "give me the
default" is to NOT pass something.

I don't KNOW if that's a problem, it just feels like one.

---
Ricky.

"I've never met a Kentucky man who wasn't either thinking about going home
or actually going home." - Happy Chandler
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/57JDHXLKZGK6G6WYVAQDN46XWW35ORYG/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 5:30 AM Ricky Teachey  wrote:
> But with function k below, where the b parameter is deferred, you can't get 
> the default b parameter by dynamically unpacking some values; you would have 
> to pass c as a kwd arg:
>
> def k(a, b=>len(a), c=None):
> ...
>
> Seems like it would be- needed? convenient?- to be able to "ask" for the 
> default in a dynamic way... Am I making more of this than is justified?
>

Question: Does it make sense to ask for the default for the second
positional parameter, while passing an actual value for the third? If
it does, then the function needs an API that reflects this. The most
obvious such API is what you already described: passing c as a
keyword argument instead. I don't think this is a problem. When you're
looking at positional args, it is only ever the last N that can be
omitted. If it makes sense to pass any combination of arguments, then
they should probably be keyword-only, to clarify this.

Do you have any examples where this isn't the case?

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/LNPLIXKAD6NNKRU4BRLPS3YJLQSUEKRR/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Brendan Barnwell

On 2021-10-25 18:56, Steven D'Aprano wrote:

Modifying the assignment symbol is wrong. This is not a new kind of
assignment, it should use the same `=` regular assignment. We are
tagging the parameter to use late-binding, not using a different sort of
assignment. The tag should be on the parameter name, not the assignment.


	I agree with half of this :-).  I agree that it's not a new kind of 
assignment.  But I don't think we're tagging the parameter to use late 
binding.  We're tagging the default value itself to not be evaluated 
right now (i.e., at function definition time) but later (at call time).


	To me this is another thing that suggests a more general 
deferred-evaluation system is the best way to handle this.  If we're 
tagging the parameter to not be evaluated "right now", why must we 
restrict "right now" to be "the time when we're defining a function" and 
restrict this to apply to function parameters rather than, well, 
anything?  Why not just say we can tag stuff as not being evaluated 
right now and then later evaluate it?


--
Brendan Barnwell
"Do not follow where the path may lead.  Go, instead, where there is no 
path, and leave a trail."

   --author unknown
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/2T4NCETN3PLAZX35DSVDCUNAMSELEGJR/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 5:21 AM Brendan Barnwell  wrote:
>
> On 2021-10-26 10:55, Chris Angelico wrote:
> >> >So why on earth NOT make these general "deferred" objects that can be 
> >> >used in other contexts?!
> > Because they're NOT deferred objects. They're argument defaults. Consider:
> >
> > def f(x=>print("Hello")):
> >  ...
> >
> > When should that print happen? Argument defaults are processed before
> > the function body begins, so you can be confident that it has happened
> > before the first line of actual code. But a deferred object might not
> > be evaluated until later, and in fact might not be evaluated at all.
>
> I don't think that's necessarily true.  If you have deferred objects,
> you could write some kind of decorator or class that evaluated deferred
> objects (or a particular kind of deferred object) when they occur as
> arguments.
>
> You're speaking as if you think "deferred object" and "argument
> default" are mutually exclusive.  But they're not.  You could have an
> argument whose default value is a deferred object which is evaluated
> before the function body begins.  Then it would be a default argument
> and a deferred object and would also be evaluated before the function
> body begins.  The details would have to be worked out (just like they do
> for your PEP) but it's not automatically impossible.
>

Okay, sure, they're not mutually exclusive... but why have a deferred
object that you evaluate in a decorator, when you could simply have an
argument default? Using an overly-generic tool means your code ends up
WAY more convoluted, and it doesn't help with introspection or
anything anyway; you may as well just have a dedicated sentinel object
with a suitable repr, and then use the standard "if x is sentinel"
check with the code in the body.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/FAVFMRITLNAETXRRIUF66WQHSKX5PVTI/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Ricky Teachey
On Tue, Oct 26, 2021 at 2:05 PM Paul Moore  wrote:

> On Tue, 26 Oct 2021 at 16:48, Eric V. Smith  wrote:
> >
> > And also the "No Loss of Abilities If Removed" section sort of applies
> > to late-bound function arguments: there's nothing proposed that can't
> > currently be done in existing Python. I'll grant you that they might
> > (might!) be more newbie-friendly, but I think the bar is high for
> > proposals that make existing things doable in a different way, as
> > opposed to proposals that add new expressiveness to the language.
>
> One issue with not having an introspection capability, which has been
> bothering me but I've not yet had the time to come up with a complete
> example, is the fact that with this new feature, you have functions
> where there's no way to express "just use the default" without knowing
> what the default actually *is*.
>
> Take for example
>
> def f(a, b=None):
> if b is None:
> b = len(a)
> ...
>
> def g(a, b=>len(a)):
> ...
>
> Suppose you want to call f as follows:
>
> args = [
> ([1,2,3], 2),
> ([4,5,6], None),
> ([7,8,9], 4),
> ]
>
> for a, b in args:
> f(a, b)
>
> That works fine. But you cannot replace f by g, because None doesn't
> mean "use the default", and in fact by design there's *nothing* that
> means "use the default" other than "know what the default is and
> supply it explicitly". So if you want to do something similar with g
> (allowing the use of None in the list of tuples to mean "use the
> default"), you need to be able to introspect g to know what the
> default is. You may also need to manipulate first-class "deferred
> expression" objects as well, just to have something you can return as
> the default value (you could return a string and require the user to
> eval it, I guess, but that doesn't seem particularly user-friendly...)
>
> I don't have a good solution for this, unfortunately. And maybe it's
> something where a "good enough" solution would be sufficient. But
> definitely, it should be discussed in the PEP so what's being proposed
> is clear.
>
> Paul
>

I had drafted an entire reply last night trying to explain this same
concern;  Paul Moore, you did a better job. Just want to say I agree: being
able to say "I want the defaults" seems like a useful ability.

But on the other hand, we also must recognize that right now there isn't
really a great, UNIVERSAL way to say "I want the defaults". It varies from
API to API. Many times getting the default means passing None, but many it
is True, or False, or a MISSING sentinel. You have to read the docs to find
out.

Taking PM's examples of f and g, the way you'd have to dynamically call g
this way be:

for arg_group in args:
g(*arg_group)

But this is going to be limiting because maybe you could also have a
function j, like this:

def j(a, b=None, c=None):
if b is None:
b = len(a)
...


args = [
([1,2,3], 2, "spam"),
([4,5,6], None, "eggs"),
([7,8,9], 4, "bacon"),
]

for a, b, c in args:
j(a, b, c)

But with function k below, where the b parameter is deferred, you can't get
the default b parameter by dynamically unpacking some values; you would
have to pass c as a kwd arg:

def k(a, b=>len(a), c=None):
...

Seems like it would be- needed? convenient?- to be able to "ask" for the
default in a dynamic way... Am I making more of this than is justified?

---
Ricky.

"I've never met a Kentucky man who wasn't either thinking about going home
or actually going home." - Happy Chandler
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/E6JHPA3KBO6RXLUBVRHJGVMYQFKQEBBZ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 5:05 AM Paul Moore  wrote:
>
> On Tue, 26 Oct 2021 at 16:48, Eric V. Smith  wrote:
> >
> > And also the "No Loss of Abilities If Removed" section sort of applies
> > to late-bound function arguments: there's nothing proposed that can't
> > currently be done in existing Python. I'll grant you that they might
> > (might!) be more newbie-friendly, but I think the bar is high for
> > proposals that make existing things doable in a different way, as
> > opposed to proposals that add new expressiveness to the language.
>
> One issue with not having an introspection capability, which has been
> bothering me but I've not yet had the time to come up with a complete
> example, is the fact that with this new feature, you have functions
> where there's no way to express "just use the default" without knowing
> what the default actually *is*.
>
> Take for example
>
> def f(a, b=None):
> if b is None:
> b = len(a)
> ...
>
> def g(a, b=>len(a)):
> ...
>
> Suppose you want to call f as follows:
>
> args = [
> ([1,2,3], 2),
> ([4,5,6], None),
> ([7,8,9], 4),
> ]
>
> for a, b in args:
> f(a, b)
>
> That works fine. But you cannot replace f by g, because None doesn't
> mean "use the default", and in fact by design there's *nothing* that
> means "use the default" other than "know what the default is and
> supply it explicitly". So if you want to do something similar with g
> (allowing the use of None in the list of tuples to mean "use the
> default"), you need to be able to introspect g to know what the
> default is. You may also need to manipulate first-class "deferred
> expression" objects as well, just to have something you can return as
> the default value (you could return a string and require the user to
> eval it, I guess, but that doesn't seem particularly user-friendly...)
>
> I don't have a good solution for this, unfortunately. And maybe it's
> something where a "good enough" solution would be sufficient. But
> definitely, it should be discussed in the PEP so what's being proposed
> is clear.
>

Wouldn't cases like this be most likely to use *args and/or **kwargs?
Simply omitting the argument from those would mean "use the default".
Or am I misunderstanding your example here?

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/ONZR547NULZKF74H7VRVXTTWRVFZ324Y/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Brendan Barnwell

On 2021-10-26 10:55, Chris Angelico wrote:

>So why on earth NOT make these general "deferred" objects that can be used in 
other contexts?!

Because they're NOT deferred objects. They're argument defaults. Consider:

def f(x=>print("Hello")):
 ...

When should that print happen? Argument defaults are processed before
the function body begins, so you can be confident that it has happened
before the first line of actual code. But a deferred object might not
be evaluated until later, and in fact might not be evaluated at all.


	I don't think that's necessarily true.  If you have deferred objects, 
you could write some kind of decorator or class that evaluated deferred 
objects (or a particular kind of deferred object) when they occur as 
arguments.


	You're speaking as if you think "deferred object" and "argument 
default" are mutually exclusive.  But they're not.  You could have an 
argument whose default value is a deferred object which is evaluated 
before the function body begins.  Then it would be a default argument 
and a deferred object and would also be evaluated before the function 
body begins.  The details would have to be worked out (just like they do 
for your PEP) but it's not automatically impossible.


--
Brendan Barnwell
"Do not follow where the path may lead.  Go, instead, where there is no 
path, and leave a trail."

   --author unknown
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/XGXVJSQJK6QYN2GYM7HOREVFFFDCGS5M/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Paul Moore
On Tue, 26 Oct 2021 at 16:48, Eric V. Smith  wrote:
>
> And also the "No Loss of Abilities If Removed" section sort of applies
> to late-bound function arguments: there's nothing proposed that can't
> currently be done in existing Python. I'll grant you that they might
> (might!) be more newbie-friendly, but I think the bar is high for
> proposals that make existing things doable in a different way, as
> opposed to proposals that add new expressiveness to the language.

One issue with not having an introspection capability, which has been
bothering me but I've not yet had the time to come up with a complete
example, is the fact that with this new feature, you have functions
where there's no way to express "just use the default" without knowing
what the default actually *is*.

Take for example

def f(a, b=None):
if b is None:
b = len(a)
...

def g(a, b=>len(a)):
...

Suppose you want to call f as follows:

args = [
([1,2,3], 2),
([4,5,6], None),
([7,8,9], 4),
]

for a, b in args:
f(a, b)

That works fine. But you cannot replace f by g, because None doesn't
mean "use the default", and in fact by design there's *nothing* that
means "use the default" other than "know what the default is and
supply it explicitly". So if you want to do something similar with g
(allowing the use of None in the list of tuples to mean "use the
default"), you need to be able to introspect g to know what the
default is. You may also need to manipulate first-class "deferred
expression" objects as well, just to have something you can return as
the default value (you could return a string and require the user to
eval it, I guess, but that doesn't seem particularly user-friendly...)

I don't have a good solution for this, unfortunately. And maybe it's
something where a "good enough" solution would be sufficient. But
definitely, it should be discussed in the PEP so what's being proposed
is clear.

Paul
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/YY5NAIKYXMNS2645SBSTYT3UXQYN2WBL/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 4:51 AM David Mertz, Ph.D.
 wrote:
>
> On Tue, Oct 26, 2021, 1:08 PM Chris Angelico  wrote:
>>
>> No, I agree. We have to still be able to introspect them.
>>
>> At the moment, when you look at a function's defaults, they are all values. 
>> With this change, some would be values and some would be markers saying that 
>> code would be executed.
>
>
> So why on earth NOT make these general "deferred" objects that can be used in 
> other contexts?!

Because they're NOT deferred objects. They're argument defaults. Consider:

def f(x=>print("Hello")):
...

When should that print happen? Argument defaults are processed before
the function body begins, so you can be confident that it has happened
before the first line of actual code. But a deferred object might not
be evaluated until later, and in fact might not be evaluated at all.

Deferred evaluation is a useful feature, but that isn't what I'm
proposing here. If you want to propose it as a completely separate
feature, and then posit that it makes PEP 671 unnecessary, then go
ahead; that's your proposal, not mine.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/VKRECIX6GZ5BXDWVYTTI6V75Q5D6IA4J/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread David Mertz, Ph.D.
On Tue, Oct 26, 2021, 1:08 PM Chris Angelico  wrote:

> No, I agree. We have to still be able to introspect them.
>
> At the moment, when you look at a function's defaults, they are all
> values. With this change, some would be values and some would be markers
> saying that code would be executed.
>

So why on earth NOT make these general "deferred" objects that can be used
in other contexts?!

Effectively that's what you need them to be. Going out of your way to make
sure they aren't used outside function signatures seems backwards.

I used the construct `defer: some_code`. Marc-André spells it without the
colon. I think now I like his better, but was initially emphasizing the
similarities with lambda. Punctuation would be fine too.

But the general idea is that a first class object is more useful, whether
it's spelled:

do_later = defer: foo(bar(baz))

do_later = defer foo(bar(baz))

do_later = $( foo(bar(baz)) )

Or some other way... I actually don't hate the bash-inspired punctuation as
much as I'm sure everyone else will... But I really just mean to suggest
"some punctuation."

>
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/WPMNFBJK7TVRX42FD3B5MAMU4JHZX6B6/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Marc-Andre Lemburg
On 26.10.2021 18:36, Erik Demaine wrote:
> On Tue, 26 Oct 2021, Marc-Andre Lemburg wrote:
> 
>> Now, it may not be obvious, but the key advantage of such
>> deferred objects is that you can pass them around, i.e. the
>> "defer os.listdir(DEFAULT_DIR)" could also be passed in via
>> another function.
> 
> Are deferred code pieces are dynamically scoped, i.e., they are evaluated in
> whatever scope they end up getting evaluated?  That would certainly 
> interesting,
> but also kind of dangerous (about as dangerous as eval), and I imagine fairly
> prone to error if they get passed around a lot. 

Yes, they would work more or less like copy & pasting the deferred
code into a new context and running it there.

Sure, you can abuse this, but the function running the deferred
can make sure that it's working in a trusted environment.

> If they're *not* dynamically
> scoped, then I think they're equivalent to lambda, and then they don't solve 
> the
> default parameter problem, because they'll be evaluated in the function's
> enclosing scope instead of the function's scope.

Indeed. Lambdas are similar, but not the same. The important part is
running the code in a different context.

-- 
Marc-Andre Lemburg
eGenix.com

Professional Python Services directly from the Experts (#1, Oct 26 2021)
>>> Python Projects, Coaching and Support ...https://www.egenix.com/
>>> Python Product Development ...https://consulting.egenix.com/


::: We implement business ideas - efficiently in both time and costs :::

   eGenix.com Software, Skills and Services GmbH  Pastor-Loeh-Str.48
D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg
   Registered at Amtsgericht Duesseldorf: HRB 46611
   https://www.egenix.com/company/contact/
 https://www.malemburg.com/

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/NUA442RSNDSHKEYNUINRY727XT7OVS63/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 3:39 AM Erik Demaine  wrote:
>
> On Tue, 26 Oct 2021, Steven D'Aprano wrote:
>
> >def func(x=x, y=>x)  # or func(x=x, @y=x)
>
> This makes me think of a "real" use-case for assigning all early-bound
> defaults before late-bound defaults: consider using closure hacks (my main use
> of early-bound defaults) together with late-bound defaults, as in
>
> ```
> for i in range(n):
>  def func(arg := expensive(i), i = i):
>  ...
> ```
>
> I think it's pretty common to put closure hacks at the end, so they don't get
> in the way of the caller.  (The intent is that the caller never specifies
> those arguments.)  But then it'd be nice to be able to use those variables in
> the late-bound defaults.
>
> I can't say this is beautiful code, but it is an application and would
> probably be convenient.

Got any realistic examples? Seems very hackish to me.

> Perhaps most natural is to add a new introspection object, say LateDefault,
> that can take place as a default value (but can't be used as an early-bound
> default?), and has a __code__ attribute.
>

Yeah, I'll have to play with this. It may be necessary to wrap *both*
types of default such that you can distinguish them. Effectively,
instead of a tuple of values, you'd have a tuple of defaults, each one
stating whether it's a value or a code block.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/DZVDQKPWDBBOTZ4ARLBNIS5NGSY7N2FK/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Tue, Oct 26, 2021 at 11:10 PM Steven D'Aprano  wrote:

> > Based on the multi-pass assignment model, which you still favour,
> > those WOULD be quite inconsistent, and some of them would make little
> > sense. It would also mean that there is a distinct semantic difference
> > between:
> >
> > def f1(x=>y + 1, y=2): ...
> > def f2(x=>y + 1, y=>2): ...
>
> Sure. They behave differently because they are different.
>
> These are different too:
>
> # Block 1
> y = 2
> x = y + 1
>
> # Block 2
> x = y + 1
> y = 2

Yes, those ARE different. Those are more equivalent to changing the
order of the parameters in the function signature, and I think we all
agree that that DOES make a difference. The question is whether these
could change meaning if you used a different type of assignment, such
as:

y := 2
x = y + 1

Does that suddenly make it legal? I think you'll find that this sort
of thing is rather surprising. And that's what we have here: changing
from one form of argument default to another changes whether
left-to-right applies or not.

I don't want that. And based on an experiment with a less-experienced
Python programmer (admittedly only a single data point), neither do
other people. Left-to-right makes sense; multi-pass does not.

> > Multi-pass initialization makes sense where it's necessary. Is it
> > really necessary here?
>
> We already have multi-pass initialisation.
>
> 1. positional arguments are applied, left to right;
> 2. then keyword arguments;
> 3. then defaults are applied.
>
> (It is, I think, an implementation detail whether 2 and 3 are literally
> two separate passes or whether they can be rolled into a single pass.
> There are probably many good ways to actually implement binding of
> arguments to parameters. But semantically, argument binding to
> parameters behaves as if it were multiple passes.

Those aren't really multi-pass assignment though, because they could
just as easily be assigned simultaneously. You can't, in Python code,
determine which order the parameters were assigned. There are rules
about how to map positional and keyword arguments to the names, but it
would be just as logical to say:

1. Assign all defaults
2. Assign all keyword args, overwriting defaults
3. Assign positional args, overwriting defaults but not kwargs

And the net result would be exactly the same. But with anything that
executes arbitrary Python code, it matters, and it matters what state
the other values are in. So we have a few options:

a) Assign all early-evaluated defaults and explicitly-passed
arguments, leaving others unbound; then process late-evaluated
defaults one by one
b) Assign parameters one by one, left to right

> Since the number of parameters is likely to be small (more likely 6
> parameters than 6000), we shouldn't care about the cost of a second pass
> to fill in the late-bound defaults after all the early-bound defaults
> are done.

I'm not concerned with performance, I'm concerned with semantics.

> > No, you misunderstand. I am not saying that less-skilled programmers
> > have to intuit things perfectly; I am saying that, when there are
> > drastic differences of expectation, there is probably a problem.
> >
> > I can easily explain "arguments are assigned left to right". It is
> > much harder to explain multi-stage initialization and why different
> > things can be referenced.
>
> I disagree that it is much harder.
>
> In any case, my fundamental model here is that if we can do something
> using pseudo-late binding (the "if arg is None" idiom), then it should
> (more or less) be possible using late-binding.
>
> We should be able to just move the expression from the body of the
> function to the parameter and in most cases it should work.

There are enough exceptions that this parallel won't really work, so
I'd rather leave aside the parallel and just describe how argument
defaults work. Yes, you can achieve the same effect in other ways, but
you can't do a mechanical transformation and expect it to behave
identically.

> Inside the body of a function, we can apply pseudo-late binding using
> the None idiom in any order we like. As late-binding parameters, we are
> limited to left-to-right. But we can get close to the (existing) status
> quo by ensuring that all early-bound defaults are applied before we
> start the late-bound defaults.
>
> # Status quo
> def function(arg, spam=None, eggs="something useful"):
> if spam is None:
> spam = process(eggs)
>
> eggs is guaranteed to have a result here because the early-bound
> defaults are all assigned before the body of the function is entered. So
> in the new regime of late-binding, I want to write:
>
> def function(arg, @spam=process(eggs), eggs="something useful"):
>
> and the call to process(eggs) should occur after the early bound default
> is assigned. The easiest way to get that is to say that early bound
> defaults are assigned in one pass, and late bound in a second pass.
>

[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 2:47 AM Eric V. Smith  wrote:
> Okay. I look forward to your thoughts. Omitting late-bound arguments or
> defaults would not be acceptable.

No, I agree. We have to still be able to introspect them.

At the moment, when you look at a function's defaults, they are all
values. With this change, some would be values and some would be
markers saying that code would be executed. The markers would
incorporate the source code for the expression in question (for human
readability), but I don't think they can include anything else; it
seems a bit costly to retain the AST, plus it's not going to be
dependable across versions anyway (the AST can change at any time).

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/UIJIDTTU5GMK24II3BYHHXVRJCEI2JZ5/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Parameter tuple unpacking in the age of positional-only arguments

2021-10-26 Thread Erik Demaine

On Tue, 26 Oct 2021, Eric V. Smith wrote:

You may or may not recall that a big reason for the removal of "tuple 
parameter unpacking" in PEP 3113 was that they couldn't be supported by the 
inspect module. Quoting that PEP: "Python has very powerful introspection 
capabilities. These extend to function signatures. There are no hidden 
details as to what a function's call signature is."


(Aside: I loved tuple parameter unpacking, and used it all the time! I was 
sad to see them go, but I agreed with PEP 3113.)


Having recently heard a friend say "the removal of tuple parameter unpacking 
was one thing that Python 3 got wrong", I read this and PEP 3113 with 
interest.


It seems like another approach would be to treat tuple-unpacking parameters as 
positional-only, now that this is a thing, or perhaps require that they are 
explicitly positional-only via in PEP 570:


def move((x, y), /): ...  # could be valid?
def move((x, y)): ... # could remain invalid?

Is it worth revisiting parameter tuple-unpacking in the age of positional-only 
arguments?  Or is this still a no-go from the perspective of introspection, 
because it violates "There are no hidden details as to what a function's call 
signature is."?  (This may be a very short-lived thread.)


Erik
--
Erik Demaine  |  edema...@mit.edu  |  http://erikdemaine.org/
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/4DEUPSGXRJMB4TWGVLEZUEOCZUX3TNMS/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Erik Demaine

On Tue, 26 Oct 2021, Steven D'Aprano wrote:


   def func(x=x, y=>x)  # or func(x=x, @y=x)


This makes me think of a "real" use-case for assigning all early-bound 
defaults before late-bound defaults: consider using closure hacks (my main use 
of early-bound defaults) together with late-bound defaults, as in


```
for i in range(n):
def func(arg := expensive(i), i = i):
...
```

I think it's pretty common to put closure hacks at the end, so they don't get 
in the way of the caller.  (The intent is that the caller never specifies 
those arguments.)  But then it'd be nice to be able to use those variables in 
the late-bound defaults.


I can't say this is beautiful code, but it is an application and would 
probably be convenient.


On Tue, 26 Oct 2021, Eric V. Smith wrote:


Among my objections to this proposal is introspection: how would that work?
The PEP mentions that the text of the expression would be available for
introspection, but that doesn't seem very useful.


I think what would make sense is for code objects to be visible, in the same 
way as `func.__code__`.  But it's definitely worth fleshing out whether:


1. Late-bound defaults are in `func.__defaults__` and `func.__kwdefaults__` -- 
where code objects are treated as special kind of default values.  This seems 
problematic because we can't distinguish between a late-bound default and an 
early-bound default that is a code object.


or

2. There are new defaults like `func.__late_defaults__` and 
`func.__late_kwdefaults__`.  The issue here is that it's not clear in what 
order to mix `func.__defaults__` and `func.__late_defaults` (each a tuple).


Perhaps most natural is to add a new introspection object, say LateDefault, 
that can take place as a default value (but can't be used as an early-bound 
default?), and has a __code__ attribute.


---

By the way, another thing missing from the PEP: presumably lambda expressions 
can also have late-bound defaults?



On Tue, 26 Oct 2021, Marc-Andre Lemburg wrote:


Now, it may not be obvious, but the key advantage of such
deferred objects is that you can pass them around, i.e. the
"defer os.listdir(DEFAULT_DIR)" could also be passed in via
another function.


Are deferred code pieces are dynamically scoped, i.e., they are evaluated in 
whatever scope they end up getting evaluated?  That would certainly 
interesting, but also kind of dangerous (about as dangerous as eval), and I 
imagine fairly prone to error if they get passed around a lot.  If they're 
*not* dynamically scoped, then I think they're equivalent to lambda, and then 
they don't solve the default parameter problem, because they'll be evaluated 
in the function's enclosing scope instead of the function's scope.


Erik
--
Erik Demaine  |  edema...@mit.edu  |  http://erikdemaine.org/
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/UPC3AX7ESRJ57IJS4DWEV4MS3N4SIISO/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Eric V. Smith

On 10/26/2021 11:19 AM, Chris Angelico wrote:

On Wed, Oct 27, 2021 at 2:05 AM Eric V. Smith  wrote:

Among my objections to this proposal is introspection: how would that
work? The PEP mentions that the text of the expression would be
available for introspection, but that doesn't seem very useful.

Doesn't it? It would certainly be useful in help().
Yes, help() should be accurate. I was referring to programmatic 
introspection with the inspect module.

At the very least, the PEP needs to talk about inspect.Signature
objects, and how they would support these late-bound function arguments.
And in particular, how would you create a Signature object that
represents a function with such arguments? What would the default values
look like? I think Dave Beazley has a talk somewhere where he
dynamically creates objects that implement specific Signatures, I'll try
to dig it up and produce an example. For me, it's a show-stopper if you
can't support this with late-bound arguments.

That's a very good point. I suspect that what will happen is that
these args will simply be omitted. It's the only way to ensure that
the values are calculated correctly. But I don't (yet) know enough of
the details of inspect.Signature to say for sure.


Okay. I look forward to your thoughts. Omitting late-bound arguments or 
defaults would not be acceptable. You may or may not recall that a big 
reason for the removal of "tuple parameter unpacking" in PEP 3113 was 
that they couldn't be supported by the inspect module. Quoting that PEP: 
"Python has very powerful introspection capabilities. These extend to 
function signatures. There are no hidden details as to what a function's 
call signature is."


(Aside: I loved tuple parameter unpacking, and used it all the time! I 
was sad to see them go, but I agreed with PEP 3113.)


And also the "No Loss of Abilities If Removed" section sort of applies 
to late-bound function arguments: there's nothing proposed that can't 
currently be done in existing Python. I'll grant you that they might 
(might!) be more newbie-friendly, but I think the bar is high for 
proposals that make existing things doable in a different way, as 
opposed to proposals that add new expressiveness to the language.


Eric

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/PCRF3W3II5RMIR2YH77RT3A4XXVTSR32/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Add __len__ to ipaddress._BaseNetwork

2021-10-26 Thread Guido van Rossum
BEst thing you can do is create an issue on bugs.python.org and attach a
pull request that makes _BaseNetwork a subclass of Sequence. Then maybe the
advantages will be clear -- or maybe in the process of writing the code you
realize that the idea is not so good after all. (I'm not a user of this
class so I don't have an opinion either way.)

On Tue, Oct 26, 2021 at 6:41 AM  wrote:

> Currently, ipaddress._BaseNetwork (and by extension, ipaddress.IPv4Network
> and ipaddress.IPv6Network) does not have a __len__ method, it only has
> num_addresses.
>
> This makes the following code not work:
>
> >>> random.choice(ipaddress.ip_network('6.0.0.0/8'))
> Traceback (most recent call last):
>   File "", line 1, in 
>   File "/home/nyuszika7h/.pyenv/versions/3.10.0/lib/python3.10/random.py",
> line 378, in choice
> return seq[self._randbelow(len(seq))]
> TypeError: object of type 'IPv4Network' has no len()
>
> The workaround is a bit ugly:
>
> >>> (network := ipaddress.ip_network('
> 6.0.0.0/8'))[random.randrange(network.num_addresses
> )]
> IPv4Address('6.60.184.142')
>
> With a custom subclass, all works well:
>
> >>> class MyIPv4Network(ipaddress.IPv4Network):
> ... def __len__(self):
> ... return self.num_addresses
> ...
> >>> random.choice(MyIPv4Network('6.0.0.0/8'))
> IPv4Address('6.40.110.63')
> ___
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-ideas@python.org/message/4OHZ6QZWDI3U2ADI5A36UU73OOXFOGJE/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/7SLNYEPBUJMZY2JL6YJCJZJKUKJCM2AK/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Wed, Oct 27, 2021 at 2:05 AM Eric V. Smith  wrote:
>
> Among my objections to this proposal is introspection: how would that
> work? The PEP mentions that the text of the expression would be
> available for introspection, but that doesn't seem very useful.

Doesn't it? It would certainly be useful in help().

> At the very least, the PEP needs to talk about inspect.Signature
> objects, and how they would support these late-bound function arguments.
> And in particular, how would you create a Signature object that
> represents a function with such arguments? What would the default values
> look like? I think Dave Beazley has a talk somewhere where he
> dynamically creates objects that implement specific Signatures, I'll try
> to dig it up and produce an example. For me, it's a show-stopper if you
> can't support this with late-bound arguments.

That's a very good point. I suspect that what will happen is that
these args will simply be omitted. It's the only way to ensure that
the values are calculated correctly. But I don't (yet) know enough of
the details of inspect.Signature to say for sure.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/G2BISTD2OTVPQERZJSFPJXF6A3R4P632/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Eric V. Smith
Among my objections to this proposal is introspection: how would that 
work? The PEP mentions that the text of the expression would be 
available for introspection, but that doesn't seem very useful.


At the very least, the PEP needs to talk about inspect.Signature 
objects, and how they would support these late-bound function arguments. 
And in particular, how would you create a Signature object that 
represents a function with such arguments? What would the default values 
look like? I think Dave Beazley has a talk somewhere where he 
dynamically creates objects that implement specific Signatures, I'll try 
to dig it up and produce an example. For me, it's a show-stopper if you 
can't support this with late-bound arguments.


And I suspect the answer to introspection is going to interact with the 
desire to create late-bound objects outside of the scope of function 
arguments and pass them around (as mentioned by MAL). They need to exist 
stand-alone, outside of function parameters.


Eric

On 10/23/2021 8:13 PM, Chris Angelico wrote:

Incorporates comments from the thread we just had.

Is anyone interested in coauthoring this with me? Anyone who has
strong interest in seeing this happen - whether you've been around the
Python lists for years, or you're new and interested in getting
involved for the first time, or anywhere in between!

https://www.python.org/dev/peps/pep-0671/

PEP: 671
Title: Syntax for late-bound function argument defaults
Author: Chris Angelico 
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 24-Oct-2021
Python-Version: 3.11
Post-History: 24-Oct-2021


Abstract


Function parameters can have default values which are calculated during
function definition and saved. This proposal introduces a new form of
argument default, defined by an expression to be evaluated at function
call time.


Motivation
==

Optional function arguments, if omitted, often have some sort of logical
default value. When this value depends on other arguments, or needs to be
reevaluated each function call, there is currently no clean way to state
this in the function header.

Currently-legal idioms for this include::

 # Very common: Use None and replace it in the function
 def bisect_right(a, x, lo=0, hi=None, *, key=None):
 if hi is None:
 hi = len(a)

 # Also well known: Use a unique custom sentinel object
 _USE_GLOBAL_DEFAULT = object()
 def connect(timeout=_USE_GLOBAL_DEFAULT):
 if timeout is _USE_GLOBAL_DEFAULT:
 timeout = default_timeout

 # Unusual: Accept star-args and then validate
 def add_item(item, *optional_target):
 if not optional_target:
 target = []
 else:
 target = optional_target[0]

In each form, ``help(function)`` fails to show the true default value. Each
one has additional problems, too; using ``None`` is only valid if None is not
itself a plausible function parameter, the custom sentinel requires a global
constant; and use of star-args implies that more than one argument could be
given.

Specification
=

Function default arguments can be defined using the new ``=>`` notation::

 def bisect_right(a, x, lo=0, hi=>len(a), *, key=None):
 def connect(timeout=>default_timeout):
 def add_item(item, target=>[]):

The expression is saved in its source code form for the purpose of inspection,
and bytecode to evaluate it is prepended to the function's body.

Notably, the expression is evaluated in the function's run-time scope, NOT the
scope in which the function was defined (as are early-bound defaults). This
allows the expression to refer to other arguments.

Self-referential expressions will result in UnboundLocalError::

 def spam(eggs=>eggs): # Nope

Multiple late-bound arguments are evaluated from left to right, and can refer
to previously-calculated values. Order is defined by the function, regardless
of the order in which keyword arguments may be passed.


Choice of spelling
--

Our chief syntax proposal is ``name=>expression`` -- our two syntax proposals
... ahem. Amongst our potential syntaxes are::

 def bisect(a, hi=>len(a)):
 def bisect(a, hi=:len(a)):
 def bisect(a, hi?=len(a)):
 def bisect(a, hi!=len(a)):
 def bisect(a, hi=\len(a)):
 def bisect(a, hi=`len(a)`):
 def bisect(a, hi=@len(a)):

Since default arguments behave largely the same whether they're early or late
bound, the preferred syntax is very similar to the existing early-bind syntax.
The alternatives offer little advantage over the preferred one.

How to Teach This
=

Early-bound default arguments should always be taught first, as they are the
simpler and more efficient way to evaluate arguments. Building on them, late
bound arguments are broadly equivalent to code at the top of the function::

 def add_item(item, target=>[]):

 # Equivalent pseudocode:
 def add_item(item, target=):
  

[Python-ideas] Add __len__ to ipaddress._BaseNetwork

2021-10-26 Thread nyuszika7h
Currently, ipaddress._BaseNetwork (and by extension, ipaddress.IPv4Network and 
ipaddress.IPv6Network) does not have a __len__ method, it only has 
num_addresses.

This makes the following code not work:

>>> random.choice(ipaddress.ip_network('6.0.0.0/8'))
Traceback (most recent call last):
  File "", line 1, in 
  File "/home/nyuszika7h/.pyenv/versions/3.10.0/lib/python3.10/random.py", line 
378, in choice
return seq[self._randbelow(len(seq))]
TypeError: object of type 'IPv4Network' has no len()

The workaround is a bit ugly:

>>> (network := 
>>> ipaddress.ip_network('6.0.0.0/8'))[random.randrange(network.num_addresses)]
IPv4Address('6.60.184.142')

With a custom subclass, all works well:

>>> class MyIPv4Network(ipaddress.IPv4Network):
... def __len__(self):
... return self.num_addresses
... 
>>> random.choice(MyIPv4Network('6.0.0.0/8'))
IPv4Address('6.40.110.63')
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/4OHZ6QZWDI3U2ADI5A36UU73OOXFOGJE/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Steven D'Aprano
On Tue, Oct 26, 2021 at 05:27:49PM +1100, Chris Angelico wrote:
> On Tue, Oct 26, 2021 at 3:00 PM Steven D'Aprano  wrote:
> >
> > On Tue, Oct 26, 2021 at 04:48:17AM +1100, Chris Angelico wrote:
> >
> > > The problem is the bizarre inconsistencies that can come up, which are
> > > difficult to explain unless you know exactly how everything is
> > > implemented internally. What exactly is the difference between these,
> > > and why should some be legal and others not?
> >
> > They should all be legal. Legal doesn't mean "works". Code that raises
> > an exception is still legal code.
> 
> Then there's no such thing as illegal code, 

I mean that code that compiles and runs is legal, even if it raises a 
runtime error. Code that cannot compile due to syntax errors is 
"illegal", we often talk about "illegal syntax":

None[0]  # Legal syntax, still raises

import() = while x or and else  # Illegal syntax

Sorry if I wasn't clear.


> and my entire basis for
> explanation is bunk. Come on, you know what I mean. If it causes
> SyntaxError:, it's not legal code.

Sorry Chris, I don't know what you mean. It only causes syntax 
error because you are forcing it to cause syntax error, not 
because it cannot be interpreted under the existing (proposed or actual) 
semantics.

You are (were?) arguing that something that is otherwise meaningful 
should be a syntax error because there are some circumstances that it 
could fail. That's not "illegal code" in the sense I mean, and I don't 
know why you want it to be a syntax error (unless you've changed your 
mind). We don't do this:

y = x+1  # Syntax error, because x might be undefined

and we shouldn't make this a syntax error

def func(@spam=eggs+1, @eggs=spam-1):

either just because `func()` with no arguments raises. So long as you 
pass at least one argument, it works fine, and that may be perfectly 
suitable for some uses.

Let linters worry about flagging that as an violation. The interpreter 
should be for consenting adults. There is plenty of code that we can 
already write that might raise a NameError or UnboundLocalError. This is 
not special enough to promote it to a syntax error.



> > > def f5(x=>y + 1):
> > > global y
> > > y = 2
> >
> 
> According to the previously-defined equivalencies, this would mean:
> 
> def f5(x=None):
> if x is None: x = y + 1
> global y
> y = 2

Of course it would not mean that. That's a straw-man. You have 
deliberately written code which you know is illegal (now, it wasn't 
illegal just a few releases back). Remember that "global y" is not an 
executable statement, it is a declaration, we can move the declaration 
anywhere we want to make the code legal.

So it would be equivalent to:

def f5(x=None):
global y
if x is None: x = y + 1
y = 2

And it can still raise NameError if y is not defined. Caveat utilitor 
(let the user beware).


Parameters (and their defaults) are not written inside the function 
body, they are written in the function header, and the function header 
by definition must preceed the body and any declarations inside it. We 
should not allow such an unimportant technicality to prevent late bound 
defaults from using globals.

Remember that for two decades or so, global declarations could be placed 
anywhere in the function body. It is only recently that we have 
tightened that up with a rule that the declaration must occur before any 
use of a name inside the function body. We created that more restrictive 
rule by fiat, we can loosen it *for late-bound expressions* by fiat too: 
morally, global declarations inside the body are deemed to occur before 
the parameter defaults.

Done and solved.

(I don't know why we decided on this odd rule that the global 
declaration has to occur before the usage of the variable, instead of 
just insisting that any globals be declared immediately after the 
function header and docstring. Oh well.)


> That
> implies that a global statement *anywhere* in a function will also
> apply to the function header, despite it not otherwise being legal to
> refer to a name earlier in the function than the global statement.

Great minds think alike :-)

If it makes you happy, you could enforce a rule that the global has to 
occur after the docstring and before the function body, but honestly I'm 
not sure why we would bother.


Some more comments, which hopefully match your vision of the feature:


If a late bound default refers to a name -- and most of them will -- we 
should follow the same rules as we otherwise would, to the extent that 
makes sense. For example:

* If the name in the default expression matches a parameter, then it 
  refers to the parameter, not the same name in the surrounding scope;
  parameters are always local to the function, so the name should be
  local to the function inside the default expression too.

* If the name in the default expression matches a local name in the
  body of the function, that 

[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Marc-Andre Lemburg
On 26.10.2021 10:54, Marc-Andre Lemburg wrote:
> [...]
> For the object version, the string would have to be compiled
> as well and then executed at the top of the function somehow :-)
> 
> I think for the latter, we'd need a more generic concept of
> deferred execution in Python, but even then, you'd not really
> save typing:
> 
> def process_files(processor, files=defer os.listdir(DEFAULT_DIR)):
> if deferred(files): files = eval(files)
> ...
> 
> The details are more complex than the above, but it demonstrates
> the idea.
> 
> Note that eval() would evaluate an already compiled expression
> encapsulated in a deferred object, so it's not slow or dangerous
> to use.
> 
> Now, it may not be obvious, but the key advantage of such
> deferred objects is that you can pass them around, i.e. the
> "defer os.listdir(DEFAULT_DIR)" could also be passed in via
> another function.

Here's a better way to write the above pseudo-code, which makes
the intent clearer:

def process_files(processor, files=defer os.listdir(DEFAULT_DIR)):
if isdeferred(files): files = files.eval()
...

isdeferred() would simply check the object for being a deferred
object.

I'm using "eval" for lack of a better word to say "please run
the deferred code now and in this context)". Perhaps a second
keyword could be used to wrap the whole "if isdeferred()..."
dance into something more intuitive.

Here's an old recipe which uses this concept:

https://code.activestate.com/recipes/502206/

BTW: While thinking about defer some more, I came up with this
alternative syntax for your proposal:

def process_files(processor, defer files=os.listdir(DEFAULT_DIR)):
# results in adding the deferred statement at the top of the
# function, if the parameter is not given, i.e.
if files is NotGiven: files = os.listdir(DEFAULT_DIR)
...

This has the advantage of making things a lot more obvious than the
small added ">", which is easy to miss and the main obstacle I see
with your PEP.

That said, I still like the idea to be able to "inject" expressions
into functions. This opens up lots of doors to make dynamic
programming more intuitive in Python.

-- 
Marc-Andre Lemburg
eGenix.com

Professional Python Services directly from the Experts (#1, Oct 26 2021)
>>> Python Projects, Coaching and Support ...https://www.egenix.com/
>>> Python Product Development ...https://consulting.egenix.com/


::: We implement business ideas - efficiently in both time and costs :::

   eGenix.com Software, Skills and Services GmbH  Pastor-Loeh-Str.48
D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg
   Registered at Amtsgericht Duesseldorf: HRB 46611
   https://www.egenix.com/company/contact/
 https://www.malemburg.com/

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/ZFVTQF7HARVSGFH7XRUKSK22KB6LW2HZ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Mon, Oct 25, 2021 at 11:26 PM Chris Angelico  wrote:
>
> On Mon, Oct 25, 2021 at 11:20 PM Marc-Andre Lemburg  wrote:
> > So whenever new syntax is discussed, I think it's important to
> > look at it from the perspective of a user who hasn't seen it before
> > (could be a programmer new to Python or one who has not worked with
> > the new feature before).
>
> I actually have a plan for that exact perspective. Was going to
> arrange things tonight, but it may have to wait for later in the week.
>

This is only one data point, so don't take this TOO strongly, but I
spoke with one of my brothers about his expectations. Walked through
some things, led him up against the problem, and watched him try to
make sense of things. Here's what I learned:

0) When led to the basic problem of "make the default be a new empty
list", he didn't stumble on the standard "=None" idiom, so this
proposal has definite value.
1) Pure left-to-right evaluation makes more sense than multi-stage evaluation
2) He expected failed forward references to look to the enclosing
scope, but on seeing that that isn't what Python does, expected
UnboundLocalError
3) To him, it's still basically an argument default value, so all
syntax ideas that he came up with were small changes to the "=" sign,
rather than being keywords or function-like constructs
5) It's really REALLY hard to devise examples of APIs that logically
would want out-of-order referencing, so it doesn't really even matter
that much
6) A full understanding of the exact semantics of Python argument
passing is (a) not as common as you might think, and (b) not actually
even necessary to most programmers

One small additional contribution was this syntax:

def spam(foo=):

I'm not sure whether it's worth adding to the list, but it's another idea.

If anyone else has friends or family who know a bit of Python, would
love to hear other people's viewpoints.

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/34WMBE23I6L3GTOV47BLOGC54DEKAPCM/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Rob Cliffe via Python-ideas



On 26/10/2021 02:56, Steven D'Aprano wrote:

On Tue, Oct 26, 2021 at 01:32:58AM +0100, Rob Cliffe via Python-ideas wrote:


Syntax bikeshedding: I still favour
     var := expr

That clashes with the walrus operator. Remember that the walrus operator
can appear inside the expression:

 var:=spam+eggs:=(something+other) or eggs
That is a SyntaxError.  I'm not sure what you mean, my best effort to 
make it legal is

        (var:=spam+(eggs:=(something+other) or eggs))
And I don't understand what point you're making here.  Yes, the walrus 
operator can appear in various places, how is that relevant? You could write

    def f(a := (b := c)):
which might be a tad confusing but would be unambiguous and legal, just as
    def f(a = (b := c)):
is currently legal (I tested it).  I don't see a clash.


Modifying the assignment symbol is wrong. This is not a new kind of
assignment, it should use the same `=` regular assignment. We are
tagging the parameter to use late-binding, not using a different sort of
assignment. The tag should be on the parameter name, not the assignment.
With respect, it IS a new kind of assignment.  One which happens at a 
different time (and whose value may vary in multiple calls of the 
function).  The value (however calculated) is assigned to the 
parameter.  Once assigned, the parameter and its value are 
indistinguishable from ones that used early binding, or indeed had a 
value passed by the caller.  It is not a new kind of parameter (in that 
sense).




IMO the similarity to early binding syntax is a good thing (or at least
not a bad thing).

Right, because binding is binding, and we should use the same `=`.

See above.




  Just as the walrus operator is similar to `=` - after
all they are both a form of assignment.

But the walrus is a different form of assignment, it is an expression,
not a statement.
True of course.  But it is also an assignment, in that the value on the 
RHS of the walrus is assigned to the variable on the LHS, just as with a 
regular assignment.

  Function parameter defaults are not literally
statements, they are declarations, which are a kind of statement.



I don't think argument defaults should be allowed to refer to later
arguments (or of course the current argument).  That's making the
interpreter's task too complicated, not to mention (surely?)
inefficient.  And it's confusing.

Worst case, the interpreter has to do two passes over the parameters
instead of one. The inefficiency is negligible.
Perhaps I wasn't clear.  When I said 'inefficiency', I meant to refer to 
cases like

    def f(a := b+1, b := e+1, c := a+1, d := 42, e := d+1)
where late-binding defaults are allowed to refer to subsequent 
arguments.  Here Python has to work out to assign first to d, then e, 
then b, then a, and finally c, which AFAICS requires multiple passes.  
But if it can all be worked out at compile time, it's not a runtime 
efficiency problem, though to my mind really obfuscated (why not write 
the arguments in the order they are intended to be calculated?).

But to play Devil's Advocate for a moment, here is a possible use case:
    def DrawCircle(centre, radius := circumference / TWO_PI, 
circumference := radius * TWO_PI):
        # Either radius or circumference can be passed, whichever is 
more convenient

As for confusing, I think you are conflating "it's new" for "it is
confusing". You aren't confused. I doubt that anyone capable of writing
a Python function would be confused by the concept:

 def func(a=1, @b=c+1, c=2):

is no more confusing than the status quo:

 def func(a=1, b=None, c=2):
 if b is None:
 b = c + 1
Confusing is perhaps the wrong word.  I think the first example IS 
harder to read.  When you read the first, you have to read a, then b, 
then 'oh what is the default value of b, I'll look at c', then skip back 
to b to see what the intention is, then forward again to c because 
you're interested in that too.  It would be better written as

        def func(a=1, c=2, @b=c+1):
There is some to-and-froing in the second example too, but the function 
header has fewer symbols and is easier to take in.  The information is 
presented in more, smaller chunks (3 lines instead of 1).
(Of course, this kind of argument could be used against all argument 
defaults (including early-bound ones), and a lot of other convenient 
language features as well.  We have to use our common 
sense/intuition/judgement to decide when conciseness outweighs 
explicitness.)


If you can understand the second, you can understand the first. All you
have to remember is that:

1. positional arguments are bound to parameters first, left to right;

2. keyword arguments are bound to parameters next;

3. regular (early bound) defaults are bound next;

4. and lastly, late-bound defaults are bound.

Easey-peasey.

I really wish people would stop assuming that fellow Python coders are
knuckle-dragging troglodytes incapable of learning behaviour 

[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Rob Cliffe via Python-ideas



On 26/10/2021 02:56, Steven D'Aprano wrote:

On Tue, Oct 26, 2021 at 01:32:58AM +0100, Rob Cliffe via Python-ideas wrote:


Syntax bikeshedding: I still favour
     var := expr

That clashes with the walrus operator. Remember that the walrus operator
can appear inside the expression:

 var:=spam+eggs:=(something+other) or eggs


Sorry, I finally understood your point.  I still don't see a problem.  
This would be legal:

    def f(var:=spam+(eggs:=(something+other) or egg)):
just as
    def f(var=spam+(eggs:=(something+other) or egg)):
is currently legal.  (The extra pair of parentheses I added are necessary.)
Rob Cliffe
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/5DE2RDUYGPFMTSN3LFQH46NDDQC6BMIE/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Unpacking in tuple/list/set/dict comprehensions

2021-10-26 Thread Irit Katriel via Python-ideas
 For the backwards compatibility section, it would be good to analyze how the 
change impacts error reporting.
(1) is the suggested syntax currently a 'common error' that will become harder 
to detect once it's not a syntax error?
(2) would adding this syntax impact the parser's ability to provide friendly 
error messages?  (Pablo did a lot of work on error messages for 3.10, so check 
a current python version).
I'm not saying I'm seeing an issue - just that these points need to be thought 
through and perhaps mentioned in the PEP.


On Tuesday, October 26, 2021, 02:55:09 AM GMT+1, Erik Demaine 
 wrote:  
 
 On Sat, 16 Oct 2021, Erik Demaine wrote:

> Assuming the support remains relatively unanimous for [*...], {*...}, and 
> {**...} (thanks for all the quick replies!), I'll put together a PEP.

As promised, I put together a pre-PEP (together with my friend and coteacher 
Adam Hartz, not currently subscribed, but I'll keep him aprised):

https://github.com/edemaine/peps/blob/unpacking-comprehensions/pep-.rst

For this to become an actual PEP, it needs a sponsor.  If a core developer 
would be willing to be the sponsor for this, please let me know.  (This is my 
first PEP, so if I'm going about this the wrong way, also let me know.)

Meanwhile, I'd welcome any comments!  In writing things up, I became convinced 
that generators should be supported, but arguments should not be supported; 
see the document for details why.

Erik
-- 
Erik Demaine  |  edema...@mit.edu  |  http://erikdemaine.org/
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/L6NZLEWOXM2KTGOIX7AHP5L76TLNKDPW/
Code of Conduct: http://python.org/psf/codeofconduct/
  ___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/UE73LCR65C6JOGQY2FHMQ2B5EOBP336J/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Marc-Andre Lemburg
On 25.10.2021 15:44, Chris Angelico wrote:
> On Mon, Oct 25, 2021 at 11:53 PM Marc-Andre Lemburg  wrote:
>>
>> On 25.10.2021 14:26, Chris Angelico wrote:
>>> On Mon, Oct 25, 2021 at 11:20 PM Marc-Andre Lemburg  wrote:

 On 25.10.2021 13:53, Chris Angelico wrote:
> On Mon, Oct 25, 2021 at 10:39 PM Marc-Andre Lemburg  
> wrote:
>> I would prefer to not go down this path.
>>
>> "Explicit is better than implicit" and this is too much "implicit"
>> for my taste :-)
>>
>> For simple use cases, this may save a few lines of code, but as soon
>> as you end up having to think whether the expression will evaluate to
>> the right value at function call time, the scope it gets executed
>> in, what to do with exceptions, etc., you're introducing too much
>> confusion with this syntax.
>
> It's always possible to be more "explicit", as long as explicit means
> "telling the computer precisely what to do". But Python has default
> arguments for a reason. Instead of simply allowing arguments to be
> optional, and then ALWAYS having code inside the function to provide
> values when they are omitted, Python allows us to provide actual
> default values that are visible to the caller (eg in help()). This is
> a good thing. Is it "implicit"? Yes, in a sense. But it's very clear
> what happens if the argument is omitted. The exact same thing is true
> with these defaults; you can see what happens.
>
> The only difference is whether it is a *value* or an *expression* that
> defines the default. Either way, if the argument is omitted, the given
> default is used instead.

 I guess I wasn't clear enough. What I mean with "implicit" is that
 execution of the expression is delayed by simply adding a ">" to
 the keyword default parameter definition.

 Given that this alters the timing of evaluation, a single character
 does not create enough attention to make this choice explicit.

 If I instead write:

 def process_files(processor, files=deferred(os.listdir(DEFAULT_DIR))):
>>
>> def process_files(processor, files=deferred("os.listdir(DEFAULT_DIR)")):
>>
>> @deferred(files="os.listdir(DEFAULT_DIR)")
> 
> Ahhh, okay. Now your explanation makes sense :)
> 
> This does deal with the problem of function calls looking like
> function calls. It comes at the price of using a string to represent
> code, so unless it has compiler support, it's going to involve eval(),
> which is quite inefficient. (And if it has compiler support, it should
> have syntactic support too, otherwise you end up with weird magical
> functions that don't do normal things.)

The decorator version would not need eval, since the decorator
would actually rewrite the function to include the parameter
defaulting logic right at the top of the function and recompile
it.

For the object version, the string would have to be compiled
as well and then executed at the top of the function somehow :-)

I think for the latter, we'd need a more generic concept of
deferred execution in Python, but even then, you'd not really
save typing:

def process_files(processor, files=defer os.listdir(DEFAULT_DIR)):
if deferred(files): files = eval(files)
...

The details are more complex than the above, but it demonstrates
the idea.

Note that eval() would evaluate an already compiled expression
encapsulated in a deferred object, so it's not slow or dangerous
to use.

Now, it may not be obvious, but the key advantage of such
deferred objects is that you can pass them around, i.e. the
"defer os.listdir(DEFAULT_DIR)" could also be passed in via
another function.

>>> It's also extremely verbose, given that it's making a very small
>>> difference to the behaviour - all it changes is when something is
>>> calculated (and, for technical reasons, where; but I expect that
>>> intuition will cover that).
>>
>> It is verbose indeed, which is why I still think that putting such
>> code directly at the top of the function is the better way
>> to go :-)
> 
> That's what I want to avoid though. Why go with the incredibly verbose
> version that basically screams "don't use this"? Use something much
> more akin to other argument defaults, and then it looks much more
> useful.
> 
>> That's fair, but since the late binding code will have to sit at
>> the top of the function definition anyway, you're not really saving
>> much.
>>
>> def add_item(item, target=>[]):
>>
>> vs.
>>
>> def add_item(item, target=None):
>> if target is None: target = []
> 
> It doesn't always have to sit at the top of the function; it can be
> anywhere in the function, including at the use site. More importantly,
> this is completely opaque to introspection. Tools like help() can't
> see that the default is a new empty list - they just see that the
> default is None. That's not meaningful, that's not helpful.

You'd typically write about those defaults in the 

[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Rob Cliffe via Python-ideas



On 26/10/2021 02:12, Chris Angelico wrote:

On Tue, Oct 26, 2021 at 11:44 AM Rob Cliffe via Python-ideas
 wrote:

I prefer 1).  Easier to understand and debug in examples with side-effects such 
as
 def f(a := enter_codes(), b = assign_targets(), c := unlock_missiles(), d 
= FIRE()):
(not that this is something to be particularly encouraged).


It's worth noting that this would call the functions at different
times; assign_targets and FIRE would be called when the function is
defined, despite not entering the codes and unlocking the missiles
until you actually call f().

The difference between early evaluation and late evaluation is that
one retains the *value* and the other retains the *expression*. So
it's something like:

_b_default = assign_targets(); _d_default = FIRE()
def f(a, b, c, d):
 if a is not set: a = enter_codes()
 if b is not set: b = _b_default
 if c is not set: c = unlock_missiles()
 if d is not set: d = _d_default

ChrisA
You're right, I wasn't thinking clearly.  It would have been a better 
example if I had used late binding for ALL arguments.

(Still, I averted a nuclear war, albeit accidentally. )
Rob

___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/IXXK2T2UEV3T7D7PZ25ICVUR6C4CHXTQ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: PEP 671: Syntax for late-bound function argument defaults

2021-10-26 Thread Chris Angelico
On Tue, Oct 26, 2021 at 3:00 PM Steven D'Aprano  wrote:
>
> On Tue, Oct 26, 2021 at 04:48:17AM +1100, Chris Angelico wrote:
>
> > The problem is the bizarre inconsistencies that can come up, which are
> > difficult to explain unless you know exactly how everything is
> > implemented internally. What exactly is the difference between these,
> > and why should some be legal and others not?
>
> They should all be legal. Legal doesn't mean "works". Code that raises
> an exception is still legal code.

Then there's no such thing as illegal code, and my entire basis for
explanation is bunk. Come on, you know what I mean. If it causes
SyntaxError:, it's not legal code. Just because that's a catchable
exception doesn't change anything. Example:

> > def f5(x=>y + 1):
> > global y
> > y = 2
>

According to the previously-defined equivalencies, this would mean:

def f5(x=None):
if x is None: x = y + 1
global y
y = 2

And that's a SyntaxError. Do you see what I mean now? Either these
things are not consistent with existing idioms, or they're not
consistent with each other.

Since writing that previous post, I have come to the view that
"consistency with existing idioms" is the one that gets sacrificed to
resolve this. I haven't yet gotten started on implementation
(definitely gonna get to that Real Soon Now™), but one possible
interpretation of f5, once disconnected from the None parallel, is
that omitting x would use one more than the module-level y. That
implies that a global statement *anywhere* in a function will also
apply to the function header, despite it not otherwise being legal to
refer to a name earlier in the function than the global statement.

> And lastly, f5() assigns positional arguments first (there are none),
> then keyword arguments (still none), then early-bound defaults left to
> right (none of these either), then late-bound defaults left to right
> (x=y+1) which might raise NameError if global y doesn't exist, otherwise
> it will succeed.

It's interesting that you assume this. By any definition, the header
is a reference prior to the global statement, which means the global
statement would have to be hoisted. I think that's probably the
correct behaviour, but it is a distinct change from the current
situation.

> However there is a real, and necessary, difference in behaviour which I
> think you missed:
>
> def func(x=x, y=>x)  # or func(x=x, @y=x)
>
> The x=x parameter uses global x as the default. The y=x parameter uses
> the local x as the default. We can live with that difference. We *need*
> that difference in behaviour, otherwise these examples won't work:
>
> def method(self, x=>self.attr)  # @x=self.attr
>
> def bisect(a, x, lo=0, hi=>len(a))  # @hi=len(a)
>
> Without that difference in behaviour, probably fifty or eighty percent
> of the use-cases are lost. (And the ones that remain are mostly trivial
> ones of the form arg=[].) So we need this genuine inconsistency.

I agree, we do need that particular inconsistency. I want to avoid
others where possible.

> If you can live with that actual inconsistency, why are you losing sleep
> over behaviour (functions f1 through f4) which isn't actually inconsistent?

(Sleep? What is sleep? I don't lose what I don't have!)

Based on the multi-pass assignment model, which you still favour,
those WOULD be quite inconsistent, and some of them would make little
sense. It would also mean that there is a distinct semantic difference
between:

def f1(x=>y + 1, y=2): ...
def f2(x=>y + 1, y=>2): ...

in that it changes what's viable and what's not. (Since you don't like
the term "legal" here, I'll go with "viable", since a runtime
exception isn't terribly useful.) Changing the default from y=2 to
y=>2 would actually stop the example from working.

Multi-pass initialization makes sense where it's necessary. Is it
really necessary here?

> > And importantly, do Python core devs agree with less-skilled Python
> > programmers on the intuitions?
>
> We should write a list of the things that Python wouldn't have if the
> intuitions of "less-skilled Python programmers" was a neccessary
> condition.
>
> - no metaclasses, descriptors or decorators;
> - no classes, inheritence (multiple or single);
> - no slices or zero-based indexing;
> - no mutable objects;
> - no immutable objects;
> - no floats or Unicode strings;
>
> etc. I think that, *maybe*, we could have `print("Hello world")`, so
> long as the programmer's intuition is that print needs parentheses.

No, you misunderstand. I am not saying that less-skilled programmers
have to intuit things perfectly; I am saying that, when there are
drastic differences of expectation, there is probably a problem.

I can easily explain "arguments are assigned left to right". It is
much harder to explain multi-stage initialization and why different
things can be referenced.

> > Two-phase initialization is my second-best preference after rejecting
> > with SyntaxError, but I would love to