[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Steven D'Aprano
On Sun, Oct 24, 2021 at 02:09:59PM +1100, Chris Angelico wrote:

> The biggest problem with thunks is knowing when to trigger evaluation.

I think Algol solved that problem by having thunks a purely internal 
mechanism, not a first-class value that users could store or pass 
around.


> We already have functions if you want to be explicit about that:

Yes, if we are satisfied with purely manually evaluating thunks. The 
point of a thunk though is that the interpreter knows when to evaluate 
it, you don't have to think about it.


> Thunks would be another great feature, but I think they're orthogonal 
> to this.

If we had thunks, that would give us late binding for free:

def bisect(a, x, lo=0, hi=thunk len(a), *, key=None)

Aaaand we're done. So thunks would make this PEP obsolete.

But if thunks are implausible, too hard, or would have too high a cost, 
then this PEP remains less ambitious and therefore easier.


-- 
Steve
___
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/WG36UOCBSR7RKWGHDX36OK6CHGSSSMG6/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Steven D'Aprano
On Sat, Oct 23, 2021 at 08:29:54PM -0700, Brendan Barnwell wrote:

>   For me the biggest problem with this idea is that it only handles a
> subset of cases, namely those that can be expressed as an expression
> inlined into the function definition.

True. But that's equally true for default values under early binding. 
Would you say that existing syntax for default values is problematic 
because it only covers the 90% of cases where the default value can be 
computed from an expression?

If push comes to shove, we can always write a helper function.


> This subset is too small, because
> we'll still have to write code in the function body for cases where the
> default depends on more complex logic.  But it is also too large,
> because it will encourage people to cram complex expressions into the
> function definition.

Just like they do now?

I think people can write bad code regardless of the features available, 
but I don't think that adding late binding will particularly make that 
tendency worse. Most uses of late binding will be pretty simple:

- mutable literals like [] and {};

- length of another argument (like the bisect example);

- references to an attribute of self;

- call out to another function.

I don't think that there is good reason to believe that adding late 
binding will cause a large increase in the amount of overly complex 
default values.

As you say, they are limited to a single expression, so the really 
complex blocks will have to stay inside the body of the function where 
they belong. And we have thirty years of default values, and no sign 
that people abuse them by writing lots of horrific defaults:

def func(arg=sum(min(seq) for seq in [[LOOKUP[key]]+list(elements)  
 for key, elements in zip(map(Mapper(spam, eggs), keys),
 iterable_of_sets) if condition(key)] if len(seq) > 5)):
...

People just don't do that sort of thing in anywhere near enough numbers 
to worry about them doing it just because we have late binding.

And if they do?

"People will write crappy code" is a social problem, not a technology 
problem, which is best solved socially, using code reviews, linters, a 
quick application of the Clue Bat to the offending coder's head, etc.


>   I would prefer to see this situation handled as part of a 
>   larger-scale
> change of adding some kind of "inline lambda" which executes directly in
> the calling scope.

That would be what I called a "thunk" in two posts now, stealing the 
term from Algol.

It would be nice if one of the core devs who understand the deep 
internals of the interpreter could comment on whether that sort of 
delayed evaluation of an expression is even plausible for Python.

If it is, then I agree: we should focus on a general thunk mechanism, 
which would then give us late binding defaults for free, plus many more 
interesting use-cases.

(Somewhere in my hard drive I have a draft proto-PEP regarding this.)

But if it is not plausible, then a more limited mechanism for late bound 
defaults will, I think, be a useful feature that improves the experience 
of writing functions.

We already document functions like this:

def bisect(a, x, lo=0, hi=len(a))

It would be an improvement if we could write them like that too.


-- 
Steve
___
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/REVRTYICAKVOZRV2X5PPOY2U5XUY5RQG/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 3:51 PM Brendan Barnwell  wrote:
>
> On 2021-10-23 09:07, Chris Angelico wrote:
> > Proposal: Proper syntax and support for late-bound argument defaults.
> >
> > def spaminate(thing, count=:thing.getdefault()):
> >  ...
>
> I'm -1 on it.
>
> For me the biggest problem with this idea is that it only handles a
> subset of cases, namely those that can be expressed as an expression
> inlined into the function definition.  This subset is too small, because
> we'll still have to write code in the function body for cases where the
> default depends on more complex logic.  But it is also too large,
> because it will encourage people to cram complex expressions into the
> function definition.
> ...
> Also, insofar as glancing at the function signature is useful, I
> suspect that putting this change in will *also* lead to help() being
> unhelpful, because, as I mentioned above, if the default uses anything
> but the most trivial logic, the signature will become cluttered with
> stuff that ought to be separated out as actual logic.

These two considerations, together, are the exact push that
programmers need: keep the expression short, don't cram everything
into the function definition. It's like writing a list comprehension;
technically you can put any expression into the body of it, but it's
normally going to be short enough to not get unwieldy.

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/3WSNUFX4WS27HG75AYRODIKN7N5DIC7C/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 3:43 PM Steven D'Aprano  wrote:
> So far, I dislike all of those syntaxes (regardless of which symbol is
> used as the modifier). They are all predicated on the idea that this is
> a new sort of assignment, which I think is the wrong way to think about
> it. I think that the better way to think about it is one of the
> following:
>
> 1. It's not the assignment that is different, it is the expression
>being bound.
>
> 2. It is not the assignment that is different, it is the parameter.

Or 3. It is not the assignment that is different, just when it occurs.

> Suggestion #2 is, I will argue, the most natural way to think about
> this. It is the parameter that differs: some parameters use early
> binding, and some use late binding. Binding is binding, regardless of
> when it is performed. When we do late binding manually, we don't do
> this:
>
> if param is None:
> param ?= expression  # Look, it's LATE BINDING assignment!!!

That's because, by the time you can even think about doing that, ANY
assignment is that. So it's a plain equals sign.

> - No matter how ludicrously confusing the annotation or expression
>   gets, the @ modifier still stands out and is obvious.

I dispute the value of this. It shouldn't stand out that much, because
ultimately, it is still defining an optional parameter and giving it a
default value.

> Here's a trivial advantage: with the "modify the equals sign" syntax, if
> you decide to copy the assignment outside of the function signature, you
> are left with a syntax error:
>
> def function(param?=expression)
> # double-click on "param", drag to expression, copy and paste
>
> param?=expression  # SyntaxError

Yes, but that's true of many things, including dict display, and even
assignment expressions.

> Disadvantages:
>
> - Maybe "at symbol" is clunkier to talk about than "arrow operator" or
>   "reverse walrus"?
>
> - Search engines aren't really great at searching for the at symbol:
>
> https://www.google.com.au/search?q=python+what+does+%40+mean
>
> https://duckduckgo.com/?q=python+what+does+%40+mean
>
> DDG gives the top hit a Quora post about the at symbol, but everything
> else is a miss; Google is even worse. But then any other symbol is going
> to be subject to the same problem.

Searching "python at sign" gives some good results, but the other
problem with searching for symbols is that they're used in multiple
ways. Ultimately, you won't get what you want by just searching for
individual characters. Short of inventing a new language keyword, I
don't think we're going to solve that.

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/5ZVOCUGF2Q3TUJCRZZX54ATBP7PIKBZM/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Brendan Barnwell

On 2021-10-23 09:07, Chris Angelico wrote:

Proposal: Proper syntax and support for late-bound argument defaults.

def spaminate(thing, count=:thing.getdefault()):
 ...


I'm -1 on it.

For me the biggest problem with this idea is that it only handles a
subset of cases, namely those that can be expressed as an expression
inlined into the function definition.  This subset is too small, because
we'll still have to write code in the function body for cases where the
default depends on more complex logic.  But it is also too large,
because it will encourage people to cram complex expressions into the
function definition.

To me, this is definitely not worth adding special syntax for.  I seem
to be the only person around here who detests "ASCII art" "arrow"
operators but, well, I do, and I'd hate to see them used for this.  The
colon or alternatives like ? or @ are less offensive but still too
inscrutable to be used for something that can already be handled in a
more explicit way.

I do have one other specific objection to the rationale:

> It's clear what value lo gets if you omit it. It's less clear what hi
> gets. And the situation only gets uglier if None is a valid argument,
> and a unique sentinel is needed; this standard idiom makes help()
> rather unhelpful:

Not really.  help() shows the *documentation* of the function.  A
person calling it should *read the documentation*, not just glance at
the function signature.  I don't see any compelling benefit to having a
mini-lambda be retrievable via introspection tools.  There is simply no
substitute for actually reading (and writing) the documentation.

Also, insofar as glancing at the function signature is useful, I
suspect that putting this change in will *also* lead to help() being
unhelpful, because, as I mentioned above, if the default uses anything
but the most trivial logic, the signature will become cluttered with
stuff that ought to be separated out as actual logic.

I would prefer to see this situation handled as part of a larger-scale
change of adding some kind of "inline lambda" which executes directly in
the calling scope.  (I think this is similar to the "deferred
computation" idea mentioned by David Mertz elsewhere in the thread.)
This would also allow extracting the logic out of the function
definition into a separate variable (holding the "inline lambda"), which
could help with cases similar to the bisect examples discussed elsewhere
in the thread, where multiple functions share late-binding logic.

--
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/KO6L4IVCPUERZOQ2Z65QZLFT7WCXSFWY/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-10-23 Thread Steven D'Aprano
Let's talk about the choice of spelling. So far, most of the suggested 
syntaxes have modified the assignment symbol `=` using either a prefix 
or a suffix. I'm going to use '?' as the generic modifier. So the 
parameter will look like:

# Modifier as a prefix
param?=expression
param:Type?=expression

# Modifier as a suffix
param=?expression
param:Type=?expression

One problem with these is that (depending on the symbol used), they can 
be visually confused with existing or possible future features, such as:

* using a colon may be confusable with a type hint or walrus operator;

* using a greater-than may be confusable with the proposed Callable 
  sugar -> or lambda sugar =>
  
* as well as just plain old greater-than;

* using a question mark may be confusable with hypothetical 
  None-aware ??= assignment.

By confusable, I don't mean that a sophisticated Python programmer who 
reads the code carefully with full attention to detail can't work out 
what it means.

I mean that new users may be confused between (say) the walrus `:=` and 
"reverse walrus" `=:`. Or the harrassed and stressed coder working at 
3am while their customer on the other side of the world keep messaging 
them. We don't always get to work carefully with close attention to 
detail, so we should prefer syntax that is less likely to be confusable.

So far, I dislike all of those syntaxes (regardless of which symbol is 
used as the modifier). They are all predicated on the idea that this is 
a new sort of assignment, which I think is the wrong way to think about 
it. I think that the better way to think about it is one of the 
following:

1. It's not the assignment that is different, it is the expression
   being bound.

2. It is not the assignment that is different, it is the parameter.

Suggestion #1 suggests that we might want a new kind of expression, 
which for lack of a better term I'm going to call a thunk (the term is 
stolen from Algol). Thunks represent a unit of delayed evaluation, and 
if they are worth doing, they are worth doing anywhere, not just in 
parameter defaults. So this is a much bigger idea, and a lot more 
pie-in-the-sky as it relies on thunks being plausible in Python's 
evaluation model, so I'm not going to talk about #1 here.

Suggestion #2 is, I will argue, the most natural way to think about 
this. It is the parameter that differs: some parameters use early 
binding, and some use late binding. Binding is binding, regardless of 
when it is performed. When we do late binding manually, we don't do 
this:

if param is None:
param ?= expression  # Look, it's LATE BINDING assignment!!!

So we shouldn't modify the assignment operator. It's still a binding. 
What we want to do is tell the compiler that *this parameter* is 
special, and so the assignment needs to be delayed to function call time 
rather than function build time. We can do that by tagging the 
parameter.

What's another term for tagging something? Decorating it. That suggests 
a natural syntax:

# arg1 uses early binding, arg2 uses late binding
def function(arg1=expression, @arg2=expression):

And with type annotations:

def function(arg1:Type=expression, @arg2:Type=expression) -> Type:

I know it's not an actual decorator, but it suggests the idea that we're 
decorating the parameter to use late binding instead of early.

Advantages:

- The modifer is right up front, where it is obvious.

- Doesn't look like grit on the monitor.

- It can't be confused with anything inside the type hint or 
  the expression.

- No matter how ludicrously confusing the annotation or expression
  gets, the @ modifier still stands out and is obvious.

- Forward-compatible: even if we invent a prefix-unary @ operator
  in the future, this will still work:

def function(@param:@Type=@expression)

- Likewise for postfix unary operators:

def function(@param:Type@=expression@)

Here's a trivial advantage: with the "modify the equals sign" syntax, if 
you decide to copy the assignment outside of the function signature, you 
are left with a syntax error:

def function(param?=expression)
# double-click on "param", drag to expression, copy and paste

param?=expression  # SyntaxError

Its not a big deal, but I can see it being a minor annoyance, especially 
confusing for newbies. But with a leading @ symbol, you can double-click 
on the param name, drag to the expression, copy and paste, and in most 
GUI editors, the @ symbol will not be selected or copied.

def function(@param=expression)
# double-click on "param", drag to expression, copy and paste

param=expression  # Legal code.

(I don't know of any GUI editors that consider @ to be part of a word 
when double-clicking, although I suppose there might be some.)

Disadvantages:

- Maybe "at symbol" is clunkier to talk about than "arrow operator" or 
  "reverse walrus"?

- Search engines aren't really great at searching for the at symbol:


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

2021-10-23 Thread Guido van Rossum
On Sat, Oct 23, 2021 at 9:21 PM Chris Angelico  wrote:

> On Sun, Oct 24, 2021 at 2:52 PM David Mertz, Ph.D.
>  wrote:
> >
> > On Sat, Oct 23, 2021, 11:46 PM David Mertz, Ph.D.
> >>>
> >>> def f(x=defer: a + b):
> >>> a, b = 3, 5
> >>> return x
> >>>
> >>> Would this return 8, or a defer-expression? If 8, then the scope isn't
> truly dynamic, since there's no way to keep it deferred until it moves to
> another scope. If not 8, then I'm not sure how you'd define the scope or
> what triggers its evaluation.
> >
> >
> > Oh... Keep in mind I'm proposing a strawman deliberately, but the most
> natural approach to keeping an object deferred rather than evaluated is
> simply to say so:
> >
> > def f(x=defer: a + b):
> > a, b = 3, 5
> > fn2(defer: x)  # look for local a, b within fn2() if needed
> > # ... other stuff
> > return x  # return 8 here
> >
>
> How would it know to look for a and b inside fn2's scope, instead of
> looking for x inside fn2's scope?
>

I am worried that this side-thread about dynamic scopes (which are a
ridiculous idea IMO) will derail the decent proposal of the PEP.

-- 
--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/67GZJXK7LIXT5EG5TX6T5TGPUVXHWB5A/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 2:52 PM David Mertz, Ph.D.
 wrote:
>
> On Sat, Oct 23, 2021, 11:46 PM David Mertz, Ph.D.
>>>
>>> def f(x=defer: a + b):
>>> a, b = 3, 5
>>> return x
>>>
>>> Would this return 8, or a defer-expression? If 8, then the scope isn't 
>>> truly dynamic, since there's no way to keep it deferred until it moves to 
>>> another scope. If not 8, then I'm not sure how you'd define the scope or 
>>> what triggers its evaluation.
>
>
> Oh... Keep in mind I'm proposing a strawman deliberately, but the most 
> natural approach to keeping an object deferred rather than evaluated is 
> simply to say so:
>
> def f(x=defer: a + b):
> a, b = 3, 5
> fn2(defer: x)  # look for local a, b within fn2() if needed
> # ... other stuff
> return x  # return 8 here
>

How would it know to look for a and b inside fn2's scope, instead of
looking for x inside fn2's scope?

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/4ESVBZZOSPOUZJIHSZCLKXPMXRAHYY2Z/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-10-23 Thread David Mertz, Ph.D.
On Sat, Oct 23, 2021, 11:46 PM David Mertz, Ph.D.

> def f(x=defer: a + b):
>> a, b = 3, 5
>> return x
>>
>> Would this return 8, or a defer-expression? If 8, then the scope isn't
>> truly dynamic, since there's no way to keep it deferred until it moves to
>> another scope. If not 8, then I'm not sure how you'd define the scope or
>> what triggers its evaluation.
>
>
Oh... Keep in mind I'm proposing a strawman deliberately, but the most
natural approach to keeping an object deferred rather than evaluated is
simply to say so:

def f(x=defer: a + b):
a, b = 3, 5
fn2(defer: x)  # look for local a, b within fn2() if needed
# ... other stuff
return x  # return 8 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/YXM3T2AXCL23EVWXE5JR6QOHQJTIZEFY/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread 2QdxY4RzWzUUiLuE
On 2021-10-24 at 13:23:51 +1100,
Steven D'Aprano  wrote:

> On Sat, Oct 23, 2021 at 02:54:54PM -0700, 2qdxy4rzwzuui...@potatochowder.com 
> wrote:
> 
> [...]
> > > The function header is a syntactic construct - the "def" line, any
> > > decorators, annotations, etc.
> > 
> > If you mean that def statements and decorators run at compile time, then
> > I agree.  If you mean something else, then I don't understand.
> 
> Pedantic note: def statements and decorators run at runtime, the same as 
> all other statements. (Including class statements.)

Yep.  My mistake.

> > I can't make the leap to claiming that late binding is part of defining
> > the function's arguments.  You say "late binding of function arguments";
> > I say "the part of the function that translates the arguments into
> > something useful for the algorithn the function encapsulates."
> 
> Consider what happens when you call a function now:
> 
> bisect(alist, obj)

[good explanation of what happens with default parameters snipped]

> Now consider what would happen if we used late binding. Everything would 
> be the same, *except* that instead of fetching a static reference to a 
> pre-existing object, the interpreter would have to fetch a reference to 
> some code, evaluate the code, and use *that* object as the default.
> 
> There is no reason to consider that part of the function body, it is 
> still performed by the interpreter.
> 
> It is only habit from 30 years of working around the lack of late- 
> binding defaults by putting the code inside the function body that leads 
> us to think that late-binding is necessarily part of the body.
> 
> In Python today, of course late-binding is part of the body, because 
> that's the only way we have to delay the evaluation of an expression ...

Aha.  I see it now (and it's not just those 30 years of Python, it's the
previous decade of Basic, FORTRAN, Pascal, C, etc.).  My first impulse
remains that all those things "the interpreter" does with default values
are still part of the function, and that the shorthand declarative
syntax is still just sugar for the explicit logic.

> But consider languages which have late-binding, I think Smalltalk and 
> Lisp are the two major examples. I'm not an expert on either, but I can 
> read StackOverflow and extrapolate a meaning to code :-)
> 
> (defun test1 (&optional (x 0))
>(+ x x))
> 
> is a function that takes one argument with a default value of 0. Lisp 
> uses late-binding: the default value is an expression (an S-expression?) 
> that is evaluated when the code is called, not at compile time, but it 
> is not part of the body of the function (the `(+ x x)` expression.

Yep.  I've written some non-toy Lisp code, and perhaps because of the
language in Lisp documentation ("initial forms" rather than "default
values"), I definitely see all of that binding as part of the function,
whether I write it in the body or in the lambda list.

(FWIW, there's also a 3-tuple form of optional parameter, where the
third element is a predicate that is bound to a boolean value that
indicates whether the value of the argument came from the caller or the
default value (thus eliminating the need for unique sentinels).  If
you're so inclined, find "supplied-p-parameter" on
.)

As I said before, pushing this sort of logic into "late binding"
scratches an itch I don't have.  I gladly write two (or more) [public]
functions that call a common [possibly private] function rather than one
[public] function with optional arguments and default values.  Easy to
write; easy to read; and easy to test, maintain, and extend.  Yes,
theoretically, the number of functions grows exponentially, but rarely
do I need more than a few such glue functions to cover most (if not all)
of the real use cases.
___
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/B6OK2OZ5UEF76T4O3VYTKRYSBR4Q3PBY/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-10-23 Thread David Mertz, Ph.D.
On Sat, Oct 23, 2021, 11:28 PM Chris Angelico  wrote:

> > So stawman proposal:
> >
> >   def fun(seq, low=0, high=defer: len(seq)):
> >   assert low < high
> >   # other stuff...
> >
> > Where the implication here is that the "defer expression" creates a
> dynamic scope.
>
> At what point is this defer-expression to be evaluated? For instance:
>
> def f(x=defer: a + b):
> a, b = 3, 5
> return x
>
> Would this return 8, or a defer-expression? If 8, then the scope isn't
> truly dynamic, since there's no way to keep it deferred until it moves to
> another scope. If not 8, then I'm not sure how you'd define the scope or
> what triggers its evaluation.
>

This would return 8.

Basically as if the expression were passed as a string, and `eval(x)` were
run when the name "x" was looked up within a scope.

An elaboration of this strawman could allow partial binding at the static
scope as well. E.g.

cursor = db_connection.cursor()
table = "employees"
expensive_query = defer c=cursor, t=table: c.execute(
f"SELECT * FROM {t} WHERE name={name}")

def employee_check(q=expensive_query):
if random.random() > 0.5:
name = "Smith"
return q

So 'c' and 't' would be closed over when the deferred is defined, but
'name' would utilize the dynamic scope. In particular, the expensive
operation of resolving the deferred would only occur if it was needed.
___
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/O4YEWWA7DNLQ2VYP37LLRLJELZLKUXHB/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 2:21 PM David Mertz, Ph.D.
 wrote:
>
> On Sat, Oct 23, 2021, 10:58 PM Steven D'Aprano
>>
>> > ... On the other hand, if this could express a much more general deferred 
>> > computation, I'd be really enthusiastic (subject to syntax and behavioral 
>> > details).
>> >
>> > However, I recognize that a new general "dynamically scoped lambda" would 
>> > indeed have a lot of edge cases.
>>
>> Dynamic scoping is not the same as deferred computation.
>
>
> Of course not generally. But a dynamic deferred could cover this specific 
> desire of the proposal.
>
> So stawman proposal:
>
>   def fun(seq, low=0, high=defer: len(seq)):
>   assert low < high
>   # other stuff...
>
> Where the implication here is that the "defer expression" creates a dynamic 
> scope.
>

At what point is this defer-expression to be evaluated? For instance:

def f(x=defer: a + b):
a, b = 3, 5
return x

Would this return 8, or a defer-expression? If 8, then the scope isn't
truly dynamic, since there's no way to keep it deferred until it moves
to another scope. If not 8, then I'm not sure how you'd define the
scope or what triggers its evaluation.

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/SZNBWNTSAZKRXMH74UU4XO4JLUKZWQBM/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-10-23 Thread David Mertz, Ph.D.
On Sat, Oct 23, 2021, 10:58 PM Steven D'Aprano

> > ... On the other hand, if this could express a much more general
> deferred computation, I'd be really enthusiastic (subject to syntax and
> behavioral details).
> >
> > However, I recognize that a new general "dynamically scoped lambda"
> would indeed have a lot of edge cases.
>
> Dynamic scoping is not the same as deferred computation.
>

Of course not generally. But a dynamic deferred could cover this specific
desire of the proposal.

So stawman proposal:

  def fun(seq, low=0, high=defer: len(seq)):
  assert low < high
  # other stuff...

Where the implication here is that the "defer expression" creates a dynamic
scope.

The reason this could appeal to me is that it wouldn't be limited to
function signatures, nor even necessarily most useful there.  Instead, a
deferred object would be a completely general thing that could be bound
anywhere any object can.

Such a capability would allow passing around potential computational "code
blocks", but only perform the work if or when a value is required.



>
___
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/M77Z4AL2W435YUDXHH7TBHDEVF5RVLKK/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 1:53 PM Steven D'Aprano  wrote:
>
> On Sun, Oct 24, 2021 at 01:16:02PM +1100, Chris Angelico wrote:
>
> > What I'm more often seeing is cases that are less obviously a
> > late-binding, but where the sentinel is replaced with the "real" value
> > at the point where it's used, rather than up the top of the function.
>
> Got any examples you can share?

Not from the standard library, but try something like this:

def generate_code(secret, timestamp=None):
...
...
...
ts = (timestamp or now()).to_bytes(8, "big")
...

The variable "timestamp" isn't ever actually set to its true default
value, but logically, omitting that parameter means "use the current
time", so this would be better written as:

def generate_code(secret, timestamp=>now()):

That's what I mean by "at the point where it's used" - it's embedded
into the expression that uses it. But the body of a function is its
own business; what matters is the header, which is stating that the
default is None, where the default is really now().

> And is it really a problem if we delay the late-binding to the point
> where the value is actually needed? Here's a toy example:
>
> # Using only early binding.
> def function(spam, eggs=None, cheese=None):
> if eggs is None:
> eggs = cheap_default()
> # do stuff using eggs
> ...
> if condition:
> return result
> if cheese is None:
> cheese = expensive_default()
> # do stuff using cheese
> ...
> return result
>
>
> The cheese parameter only gets used if the processing of spam with eggs
> fails to give a result. But if cheese is used, the default is expensive.
> Is it really a problem if we delay evaluating that default to the point
> where it is needed?

Delaying evaluation isn't a problem, though it also isn't usually an
advantage. (If the default is truly expensive, then you wouldn't want
to use this, but now we're looking at optimizations, where the
decision is made to favour performance over clarity.)

> The expression is evaluated only if and when the body of the function
> attempts to use the value of arg, if the caller has not provided a
> value. So if the function looks like this:
>
> # late binding with a thunk that delays execution until needed
> def func(flag, arg=1/0):
> if flag:
> print("Boom!")
> return arg
> return None
>
> then func(True) will print Boom! and then raise ZeroDivisionError, and
> func(False) will happily return None.
>
> I have no idea whether thunk-like functionality is workable in Python's
> execution model without slowing down every object reference, but if it
> is possible, there could be other really nice use-cases beyond just
> function defaults.

The biggest problem with thunks is knowing when to trigger evaluation.
We already have functions if you want to be explicit about that:

def func(flag, arg=lambda:1/0):
   ...
  return arg()

so any thunk feature would need some way to trigger its full
evaluation. Should that happen when it gets touched in any way? Or
leave it until some attribute is looked up? What are the consequences
of leaving it unevaluated for too long?

Thunks would be another great feature, but I think they're orthogonal to this.

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/M4IXKTDOP42WHS5YDKNECSB7LWKOZTAM/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Bruce Leban
On Sat, Oct 23, 2021 at 7:55 PM Steven D'Aprano  wrote:

>
> And is it really a problem if we delay the late-binding to the point
> where the value is actually needed? ...

   



[in that csse] I would stick to manual late-
> binding using None, and only evaluate it as needed.
>
> Maybe this is an argument for some sort of thunk, as in Algol, which is
> only evaluated at need.
>



I have no idea whether thunk-like functionality is workable in Python's
> execution model without slowing down every object reference, but if it
> is possible, there could be other really nice use-cases beyond just
> function defaults.
>

 Your message here and my message on this passed in the mail. Yes, this is
a really good point and would apply to the cases I've seen where the
evaluation was in the middle. Thanks for raising it. I also don't know if
it's workable but it should be considered.

--- Bruce
___
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/COCFKCNJUIOKMZ24GNGJZRMJ4KX45SG4/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Bruce Leban
--- Bruce




On Sat, Oct 23, 2021 at 7:55 PM Chris Angelico  wrote:

> On Sun, Oct 24, 2021 at 1:48 PM Bruce Leban  wrote:
> >
> >
> > On Sat, Oct 23, 2021 at 6:23 PM Jelle Zijlstra 
> wrote:
> >>
> >> In the PEP's example:
> >>
> >> def bisect_right(a, x, lo=0, hi=>len(a), *, key=None):
> >>
> >> This reads to me like we're putting "hi" into "len(a)", when it's in
> fact the reverse.
> >
> > Every language I am aware of that has adopted a short hand lambda
> notation (without a keyword) has used => or -> except APL, Ruby, SmallTalk.
> See https://en.wikipedia.org/wiki/Anonymous_function
>
>
> Anonymous functions are an awkward parallel here. The notation you're
> describing will create a function which accepts one argument, and then
> returns a value calculated from that argument. We're actually doing
> the opposite: hi is being set to len(a), it's not that len(a) is being
> calculated from hi.
>
> That said, though, I still count "=>" among my top three preferences
> (along with "=:" and "?="), and flipping the arrow to "<=" is too
> confusable with the less-eq operator.
>

Sorry I was less than clear. The syllogism here is

(1) late-evaluated argument default should use => because that's the
proposal for shorthand lambda

(2) shorthand lambda should use => because that's what other languages use.

I was talking about (2) but I should have been explicit. And yes, you
highlight a potential source of confusion.

def f(x=>x + 1): ...

means that x is 1 more than the value of x from the enclosing global scope
(at function call time) while

g = x => x + 1

sets g to a single-argument function that adds 1 to its argument value.

--- Bruce
___
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/4X2ESYS4OK6HSPKTPCUP742NS2XJUZT4/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-10-23 Thread Steven D'Aprano
On Sun, Oct 24, 2021 at 05:49:50AM +0400, David Mertz, Ph.D. wrote:

> ... On the other hand, if this could express a much more general deferred
> computation, I'd be really enthusiastic (subject to syntax and behavioral
> details).
> 
> However, I recognize that a new general "dynamically scoped lambda" would
> indeed have a lot of edge cases.

Dynamic scoping is not the same as deferred computation.


-- 
Steve
___
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/YAFUINJRJOOFQHSYJF4HLH6HNTDL4E3Q/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Bruce Leban
On Sat, Oct 23, 2021 at 7:00 PM Steven D'Aprano  wrote:

> I challenge that assertion. I've never knowingly seen a function where
> the late binding is "buried deeper in the function", certainly not deep
> enough that it is not obvious. It is a very strong convention that such
> late binding operations occur early in the function body.
>
> You know, before you use the parameter, not afterwards *wink*
>

I've seen this and recently. The case is where the evaluation of the
default expression might be expensive, and is only used depending on the
value of other arguments.

--- Bruce
___
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/YMNQPJTVFUBHR3WALI2TRC2UPKHREYJ5/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Steven D'Aprano
On Sun, Oct 24, 2021 at 01:16:02PM +1100, Chris Angelico wrote:

> What I'm more often seeing is cases that are less obviously a
> late-binding, but where the sentinel is replaced with the "real" value
> at the point where it's used, rather than up the top of the function.

Got any examples you can share?

And is it really a problem if we delay the late-binding to the point 
where the value is actually needed? Here's a toy example:

# Using only early binding.
def function(spam, eggs=None, cheese=None):
if eggs is None:
eggs = cheap_default()
# do stuff using eggs
...
if condition:
return result
if cheese is None:
cheese = expensive_default()
# do stuff using cheese
... 
return result


The cheese parameter only gets used if the processing of spam with eggs 
fails to give a result. But if cheese is used, the default is expensive. 
Is it really a problem if we delay evaluating that default to the point 
where it is needed?

So this would be another example where automatic late-binding wouldn't 
be used. If the default is very expensive, I would stick to manual late- 
binding using None, and only evaluate it as needed.

Maybe this is an argument for some sort of thunk, as in Algol, which is 
only evaluated at need. Then we could just write:

# Assume late-binding with thunks.
def function(spam, eggs=cheap_default(), cheese=expensive_default()):
# do stuff using eggs
...
if condition:
return result
# do stuff using cheese
... 
return result

and the thunks `cheap_default()` and `expensive_default()` will only be 
evaluated *if they are actually needed*, rather than automatically 
when the function is called.

To be clear about the semantics, let me illustrate. I am deliberately 
not using any extra syntax for late-binding.

# early binding (the status quo)
def func(arg=expression): ...


The expression is evaluated when the def statement is run and the func 
object is created.


# late binding (minus any extra syntax)
def func(arg=expression): ...

The expression is evaluated eagerly when the function is called, if and 
only if the parameter arg has not been given a value by the caller.


# late binding with thunk
def func(arg=expression): ...

The expression is evaluated only if and when the body of the function 
attempts to use the value of arg, if the caller has not provided a 
value. So if the function looks like this:

# late binding with a thunk that delays execution until needed
def func(flag, arg=1/0):
if flag:
print("Boom!")
return arg
return None

then func(True) will print Boom! and then raise ZeroDivisionError, and 
func(False) will happily return None.

I have no idea whether thunk-like functionality is workable in Python's 
execution model without slowing down every object reference, but if it 
is possible, there could be other really nice use-cases beyond just 
function defaults.


-- 
Steve
___
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/NW63MNKO7JFQLTP7LTSJQP7W5JB72DGN/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 1:48 PM Bruce Leban  wrote:
>
>
> On Sat, Oct 23, 2021 at 6:23 PM Jelle Zijlstra  
> wrote:
>>
>> In the PEP's example:
>>
>> def bisect_right(a, x, lo=0, hi=>len(a), *, key=None):
>>
>> This reads to me like we're putting "hi" into "len(a)", when it's in fact 
>> the reverse.
>
>
> I think in most cases what's on the right side will be something that's not 
> assignable. Likewise with the proposal to use => for lambda, someone could 
> read (a => a + 1) as putting a into a + 1. I think they're going to get over 
> that.
>
> Every language I am aware of that has adopted a short hand lambda notation 
> (without a keyword) has used => or -> except APL, Ruby, SmallTalk. See 
> https://en.wikipedia.org/wiki/Anonymous_function
>
> APL uses a tacit syntax while Ruby and SmallTalk use explicit syntaxes. The 
> equivalent of x => x + 1 in each of these is
>
> APL ⍺+1   (I think)
> Ruby|x| x + 1
> SmallTalk   [ :x | x + 1 ]
>

Anonymous functions are an awkward parallel here. The notation you're
describing will create a function which accepts one argument, and then
returns a value calculated from that argument. We're actually doing
the opposite: hi is being set to len(a), it's not that len(a) is being
calculated from hi.

That said, though, I still count "=>" among my top three preferences
(along with "=:" and "?="), and flipping the arrow to "<=" is too
confusable with the less-eq operator.

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/WFOWBIFZZ3ICJLRIMQTX6K2NDXYVQMNV/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Bruce Leban
On Sat, Oct 23, 2021 at 6:23 PM Jelle Zijlstra 
wrote:

> In the PEP's example:
>
> def bisect_right(a, x, lo=0, hi=>len(a), *, key=None):
>
> This reads to me like we're putting "hi" into "len(a)", when it's in fact
> the reverse.
>

I think in most cases what's on the right side will be something that's not
assignable. Likewise with the proposal to use => for lambda, someone could
read (a => a + 1) as putting a into a + 1. I think they're going to get
over that.

Every language I am aware of that has adopted a short hand lambda notation
(without a keyword) has used => or -> except APL, Ruby, SmallTalk. See
https://en.wikipedia.org/wiki/Anonymous_function

APL uses a tacit syntax while Ruby and SmallTalk use explicit syntaxes. The
equivalent of x => x + 1 in each of these is

APL ⍺+1   (I think)
Ruby|x| x + 1
SmallTalk   [ :x | x + 1 ]


--- Bruce
___
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/D5CWXOZY2VS44F3PNWNMNJ5IL2U7TUCK/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-10-23 Thread Thomas Mc Kay
Love the proposed syntax, it's clear and visible without being verbose. I was a 
bit worried with the "=:" syntax as, at first glance, it can be easily mistaken 
with a regular "=" but you can't miss "=>". Also, I think the syntax fits 
really well with the meaning of the operator, which is important.

I'm also partial to "?=", as a second option, as it reads like a ternary 
operator (x?=len(y)  -> x if x else len(y))

The point about scope for late-binding is also interesting and a good idea. It 
gives us what I think is powerful syntax for OO:

class Foo:
def bar(self, baz=>self.baz):
...

This is great! +10 from me.
___
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/UUEGPM3KRGYTMGPPN5R7OZHS3C5WVKD6/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Steven D'Aprano
On Sat, Oct 23, 2021 at 02:54:54PM -0700, 2qdxy4rzwzuui...@potatochowder.com 
wrote:

[...]
> > The function header is a syntactic construct - the "def" line, any
> > decorators, annotations, etc.
> 
> If you mean that def statements and decorators run at compile time, then
> I agree.  If you mean something else, then I don't understand.

Pedantic note: def statements and decorators run at runtime, the same as 
all other statements. (Including class statements.)

Broadly speaking (I may have some of the fine details wrong) there are 
three stages in executing a function:

- compile-time, when the interpreter statically analyses the function 
  source code and compiles the body of the function, plus a set of 
  byte-codes (or equivalent) to assemble the function object;

- runtime, when the def *statement* is executed by running the second
  lot of byte-code, and the function object is assembled -- we often
  call that "function definition time"; and

- runtime, when the function object is actually called, and the first
  set of byte-code, the one that represents the body of the function,
  gets executed.

There is no Python code executed at compile-time, and the def statement 
is not executed until runtime.


[...]
> I can't make the leap to claiming that late binding is part of defining
> the function's arguments.  You say "late binding of function arguments";
> I say "the part of the function that translates the arguments into
> something useful for the algorithn the function encapsulates."

Consider what happens when you call a function now:

bisect(alist, obj)

At runtime, the interpreter has to bind arguments to parameters (and 
handle any errors, such as too few or too many arguments). The bisect 
function takes five parameters, but only two arguments are given. So the 
interpreter currently has to look up default values for the missing 
three parameters (which it gets from the function object, or possibly 
the code object, I forget which).

Those values are static references to objects which were evaluated at 
function definition time, so that the process of fetching the default is 
nothing more than grabbing the object from the function object.

That process of the interpreter matching up arguments to parameters, 
filling in missing arguments with defaults, and checking for error 
conditions, is not normally considered to be part of the function 
execution itself. It is part of the interpreter, not part of the 
function.

Now consider what would happen if we used late binding. Everything would 
be the same, *except* that instead of fetching a static reference to a 
pre-existing object, the interpreter would have to fetch a reference to 
some code, evaluate the code, and use *that* object as the default.

There is no reason to consider that part of the function body, it is 
still performed by the interpreter.

It is only habit from 30 years of working around the lack of late- 
binding defaults by putting the code inside the function body that leads 
us to think that late-binding is necessarily part of the body.

In Python today, of course late-binding is part of the body, because 
that's the only way we have to delay the evaluation of an expression. 
But consider languages which have late-binding, I think Smalltalk and 
Lisp are the two major examples. I'm not an expert on either, but I can 
read StackOverflow and extrapolate a meaning to code :-)

(defun test1 (&optional (x 0))
   (+ x x))

is a function that takes one argument with a default value of 0. Lisp 
uses late-binding: the default value is an expression (an S-expression?) 
that is evaluated when the code is called, not at compile time, but it 
is not part of the body of the function (the `(+ x x)` expression.



-- 
Steve
___
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/7U5EB6VE5VA6OIXPQ4RA6B3VO6SGK2M5/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 1:12 PM Ricky Teachey  wrote:
>
> This might be a dumb question but is the context built only left to right? Or 
> will this be legal?
>
> def f(a, *, b=:c, c=:b):
> print(f"{a=} {b=} {c=}")
>
> >>> f(1, b=2)
> a=1 b=2 c=2
>
> >>> f(1, c=2)
> a=1 b=2 c=2
>

That's something where we may need to get a reference implementation
before deciding, but I am open to either of two possibilities:

1) Keyword args are resolved before late-bound defaults, so your
example would technically work, despite being confusing
2) Late-bound defaults explicitly reject (with SyntaxError) any
references to arguments to their right.

Python already enforces this kind of check:

>>> def f():
... x = 1
... global x
...
  File "", line 3
SyntaxError: name 'x' is assigned to before global declaration

Even if it IS legal, I would say that this would be something to
avoid. Same with using assignment expressions to mutate other
arguments - I suspect that this won't cause technical problems, but it
would certainly hurt the brains of people who read it:

def f(a, b=>c:=a, c=>b:=len(a)):
print("wat")

Yeah, just don't :)

Not a dumb question. It's a little underspecified in the PEP at the
moment (I only mention that they may refer to previous values, but not
whether subsequently-named arguments are fair game), and am open to
discussion about whether this should be locked down. Currently, I'm
inclined to be permissive, and let people put anything they like
there, just like you can put crazy expressions into other places where
they probably wouldn't improve your code :)

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/L2POAISCQ33YSEYNPOQDM7BUFSASLNQP/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 1:00 PM Steven D'Aprano  wrote:
>
> On Sun, Oct 24, 2021 at 06:54:36AM +1100, Chris Angelico wrote:
>
> [...]
> > Teaching moment. Currently, the equivalent second function would be this:
> >
> > def f2(l=None):
> > if l is None: l = []
> > l.append(4)
> > return l
> >
> > And the whole "early bind or late bind" question is there just the
> > same; the only difference is that the late binding happens somewhere
> > inside the function body, instead of being visible as part of the
> > function's header. (In this toy example, it's the very next line,
> > which isn't a major problem; but in real-world examples, it's often
> > buried deeper in the function, and it's not obvious that passing None
> > really is the same as passing the array's length, or using a system
> > random number generator, or constructing a new list, or whatever it
> > is.)
>
> I challenge that assertion. I've never knowingly seen a function where
> the late binding is "buried deeper in the function", certainly not deep
> enough that it is not obvious. It is a very strong convention that such
> late binding operations occur early in the function body.
>
> You know, before you use the parameter, not afterwards *wink*
>
> But then I mostly look at well-written functions that are usually less
> than two, maybe three, dozen lines long, with a managable number of
> parameters.
>
> If you are regularly reading badly-written functions that are four pages
> long, with fifty parameters, your experience may differ :-)
>
> The bisect function you gave earlier is a real-world example of a
> non-toy function. You will notice that the body of bisect_right:
>
> - does the late binding early in the body, immediately after checking
>   for an error condition;
>
> - and is a manageable size (19 LOC).

What I'm more often seeing is cases that are less obviously a
late-binding, but where the sentinel is replaced with the "real" value
at the point where it's used, rather than up the top of the function.

> https://github.com/python/cpython/blob/3.10/Lib/bisect.py
>
> The bisect module is also good evidence that this proposal may not be as
> useful as we hope. We have:
>
> def insort_right(a, x, lo=0, hi=None, *, key=None):
>
> which just passes the None on to bisect_right. So if we introduced
> optional late-binding, the bisect module has two choices:
>
> - keep the status quo (don't use the new functionality);
>
> - or violate DRY (Don't Repeat Yourself) by having both functions
>   duplicate the same late-binding.
>
> It's only a minor DRY violation, but still, if the bisect module was
> mine, I wouldn't use the new late-binding proposal.
>
> So I think that your case is undermined a little by your own example.
>

The truth is, though, that the default for hi is not None - it's
really "length of the given list". Python allows us to have real
defaults for parameters, rather than simply leaving trailing
parameters undefined as JavaScript does; this means that you can read
off the function header and see what the meaning of parameter omission
is. Late-binding semantics allow this to apply even if the default
isn't a constant.

If this proposal is accepted, I would adopt the DRY violation, since
it would literally look like this:

def insort_right(a, x, lo=0, hi=>len(a), *, key=None):
def bisect_right(a, x, lo=0, hi=>len(a), *, key=None):
def insort_left(a, x, lo=0, hi=>len(a), *, key=None):
def bisect_left(a, x, lo=0, hi=>len(a), *, key=None):

That's not really a lot of repetition, and now everyone can see that
the default lo is 0 and the default hi is the length of a. Four
"hi=>len(a)" isn't really different from four "hi=None" when it comes
down to it.

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/BLL643IZYZLYAUVX6CGDIJFFCNMEE4QO/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Ricky Teachey
This might be a dumb question but is the context built only left to right?
Or will this be legal?

def f(a, *, b=:c, c=:b):
print(f"{a=} {b=} {c=}")

>>> f(1, b=2)
a=1 b=2 c=2

>>> f(1, c=2)
a=1 b=2 c=2
___
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/CXF5DEE7UBR63ZCAXJKWI4E3ALQM3X2U/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 12:44 PM Rob Cliffe  wrote:
> I'm not keen on the
>  var = > expr
> syntax.  IMO the arrow is pointing the wrong way.  expr is assigned to var.

That's definitely open to discussion.

> Some possible alternatives, if there is no technical reason they
> wouldn't work (as far as I know they are currently a syntax error, and
> thus unambiguous, inside a function header, unless annotations change
> things.  I know nothing about annotations):

Annotations come before the default, so the question is mainly whether
it's possible for an annotation to end with the symbol in question.

>  var <= exp  # Might be confused with less-than-or-equals

Not a fan, for that exact reason.

>  var := expr # Reminiscent of the walrus operator in other contexts.
>  # This might be considered a good thing or
> a bad thing.
>  # Possibly too similar to `var = expr'

This is less concerning than <=, since... ahem. I spy a missed
opportunity here. Must correct.

This is less confusing or equally confusing as <=, since ":=" is a
form of name binding, which function parameters are doing. Not a huge
fan but I'd be more open to this than the leftward-arrow.

>  var : expr   # Less evocative.  Looks like part of a dict display.

(That is precisely the syntax for annotations, so that one's out)

>  var <- expr# Trailing `-` is confusing: is it part of (-expr) ?

Not a fan. I don't like syntaxes where a space makes a distinct
difference to the meaning.

>  var =  # Hm.  At least it's clearly distinct from `var=expr`.

It took me a couple of readings to understand this, since  is a
common way of indicating a placeholder. This might be a possibility,
but it would be very difficult to discuss it.

>  var << expr   # Ditto.

Like <=, this is drawing an analogy with an operator that has nothing
to do with name binding, so I think it'll just be confusing.

>  (var = expr)
>  [var = expr]   # Ditto

Bracketing the argument seems like a weird way to indicate
late-binding, but if anything, I'd go with the round ones.

> And as I'm criticising my own suggestions, I'll do the same for the ones
> in the PEP:
>
>  def bisect(a, hi=:len(a)): # Just looks weird, doesn't suggest anything 
> to me
>  def bisect(a, hi?=len(a)): # Hate question marks (turning Python into 
> Perl?)
> # Ditto for using $ or % or ^ or &
>  def bisect(a, hi!=len(a)): # Might be confused with not-equals

Yeah, I don't like != for the same reason that I don't like <= or <<.

>  def bisect(a, hi=\len(a)): # `\` looks like escape sequence or end of 
> line
>  # What if you wanted to break a long line at 
> that point?

No worse than the reuse of backslashes in regexes and string literals.

>  def bisect(a, hi=`len(a)`):# Backquotes are fiddly to type.  Is 
> `len(a)   `   allowed?

Not sure why it wouldn't - the spaces don't change the expression.
Bear in mind that the raw source code of the expression would be saved
for inspection, so there'd be a difference with help(), but other than
that, it would have the same effect.

>  def bisect(a, hi=@len(a)): # Just looks weird, doesn't suggest anything 
> to me

Agreed, don't like that one.

> My personal current (subject to change) preference would be   `var := expr`.
>
> All this of course is my own off-the-cuff subjective reaction.
> Y M M (probably will) V.


Subjective reactions are extremely important. If the syntax is ugly,
the proposal is weak.

But I can add := to the list of alternates, for the sake of having it listed.

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/IZLHGZR6HKLMWH536PSZ6W2UX7KKGLS3/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Steven D'Aprano
On Sun, Oct 24, 2021 at 06:54:36AM +1100, Chris Angelico wrote:

[...]
> Teaching moment. Currently, the equivalent second function would be this:
> 
> def f2(l=None):
> if l is None: l = []
> l.append(4)
> return l
> 
> And the whole "early bind or late bind" question is there just the
> same; the only difference is that the late binding happens somewhere
> inside the function body, instead of being visible as part of the
> function's header. (In this toy example, it's the very next line,
> which isn't a major problem; but in real-world examples, it's often
> buried deeper in the function, and it's not obvious that passing None
> really is the same as passing the array's length, or using a system
> random number generator, or constructing a new list, or whatever it
> is.)

I challenge that assertion. I've never knowingly seen a function where 
the late binding is "buried deeper in the function", certainly not deep 
enough that it is not obvious. It is a very strong convention that such 
late binding operations occur early in the function body.

You know, before you use the parameter, not afterwards *wink*

But then I mostly look at well-written functions that are usually less 
than two, maybe three, dozen lines long, with a managable number of 
parameters.

If you are regularly reading badly-written functions that are four pages 
long, with fifty parameters, your experience may differ :-)

The bisect function you gave earlier is a real-world example of a 
non-toy function. You will notice that the body of bisect_right:

- does the late binding early in the body, immediately after checking 
  for an error condition;

- and is a manageable size (19 LOC).

https://github.com/python/cpython/blob/3.10/Lib/bisect.py

The bisect module is also good evidence that this proposal may not be as 
useful as we hope. We have:

def insort_right(a, x, lo=0, hi=None, *, key=None):

which just passes the None on to bisect_right. So if we introduced 
optional late-binding, the bisect module has two choices:

- keep the status quo (don't use the new functionality);

- or violate DRY (Don't Repeat Yourself) by having both functions
  duplicate the same late-binding.

It's only a minor DRY violation, but still, if the bisect module was 
mine, I wouldn't use the new late-binding proposal.

So I think that your case is undermined a little by your own example.


-- 
Steve
___
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/PQESO4G5LXCJBCN5LNYL5PFUAIPIOS65/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-10-23 Thread David Mertz, Ph.D.
>From what I've seen so far, I'm -0 on this.

I understand the pattern it addresses, but it doesn't feel all that common,
nor that hard to address with the existing sentinel-check pattern alien in
the PEP draft.

This just doesn't feel big enough to merit it's own syntax.

... On the other hand, if this could express a much more general deferred
computation, I'd be really enthusiastic (subject to syntax and behavioral
details).

However, I recognize that a new general "dynamically scoped lambda" would
indeed have a lot of edge cases.


On Sat, Oct 23, 2021, 8:15 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=):
> if target was omitted: target = []
>
>
> Open Issues
> ===
>
> - yield/await? Will they cause problems? Might end up being a non-issue.
>
> - annotations? They go before the default, so is there any way an anno
> could
>   want to end with ``=>``?

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

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 12:10 PM Steven D'Aprano  wrote:
>
> Eight hours from the initial post on Python-Ideas, to a PEP, with just
> eight responses from six people. Is that some sort of a record?
>
> And in the wee hours of the morning too (3am to 11am local time). I
> thought my sleep habits were bad. Do you not sleep any more? :-)
>

Fair point, but this is something that just keeps on coming up in some
form or another. Anyway, if it ends up going nowhere, it's still not
wasted time.

Sleep? What is sleep?

https://docs.python.org/3/library/time.html#time.sleep

Ah yes. That. :)

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/HTI6FPWGAHKSA2U2MHAOT5WDNRYZGYOF/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Jelle Zijlstra
El sáb, 23 oct 2021 a las 12:57, Guido van Rossum ()
escribió:

> I like that you're trying to fix this wart! I think that using a different
> syntax may be the only way out. My own bikeshed color to try would be `=>`,
> assuming we'll introduce `(x) => x+1` as the new lambda syntax, but I can
> see problems with both as well :-).
>
> In the PEP's example:

def bisect_right(a, x, lo=0, hi=>len(a), *, key=None):

This reads to me like we're putting "hi" into "len(a)", when it's in fact
the reverse. What about:

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

Another option (going back to Chris's original suggestion) could be:

 def bisect_right(a, x, lo=0, hi:=len(a), *, key=None):

Which is the same as the walrus operator, leaning on the idea that this is
kind of like the walrus: a name gets assigned based on something evaluated
right here.

Bikeshedding aside, thanks Chris for the initiative here! This is a tricky
corner of the language and a promising improvement.
___
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/B6IDAKCVS7JENMGYC7ZYCK7SHVCBVPEI/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-10-23 Thread Steven D'Aprano
Eight hours from the initial post on Python-Ideas, to a PEP, with just 
eight responses from six people. Is that some sort of a record?

And in the wee hours of the morning too (3am to 11am local time). I 
thought my sleep habits were bad. Do you not sleep any more? :-)


-- 
Steve
___
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/PDYR4AMKI4CBD7QGJLUGQZ5G3MWGI5SX/
Code of Conduct: http://python.org/psf/codeofconduct/


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

2021-10-23 Thread Chris Angelico
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=):
if target was omitted: target = []


Open Issues
===

- yield/await? Will they cause problems? Might end up being a non-issue.

- annotations? They go before the default, so is there any way an anno could
  want to end with ``=>``?


References
==


Copyright
=

This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.
___
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/KR2TMLPFR7NHDZCDOS6VTNWDKZQQJN3V/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 9:05 AM Ethan Furman  wrote:
>
> On 10/23/21 2:23 PM, Chris Angelico wrote:
>
>  > It seems like there's broad interest in this, but a lot of details to
>  > nut out. I think it may be time for me to write up a full PEP. Guido,
>  > if I'm understanding recent SC decisions correctly, a PEP editor can
>  > self-sponsor, correct?
>
> Of all people, PEP editor or not, you should be able to self-sponsor.  ;-)
>
> But if you need one, I'm happy to step up.
>

Heh. Thanks. :) I'll start writing then.

ChrisA
not a core dev, although someone recently credited me using that descriptor
___
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/ZCRBGUKPCBTAJPG6HKZVIAO2SYNX3Q2H/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 8:56 AM <2qdxy4rzwzuui...@potatochowder.com> wrote:
>
> On 2021-10-24 at 06:54:36 +1100,
> Chris Angelico  wrote:
>
> > On Sun, Oct 24, 2021 at 6:18 AM <2qdxy4rzwzuui...@potatochowder.com> wrote:
> > > > The expression would be evaluated in the function's context, having
> > > > available to it everything that the function has. Notably, this is NOT
> > > > the same as the context of the function definition, but this is only
> > > > rarely going to be significant (eg class methods where a bare name in
> > > > an early-bound argument default would come from class scope, but the
> > > > same bare name would come from local scope if late-bound).
> > >
> > > > The purpose of this change is to have the function header define, as
> > > > fully as possible, the function's arguments. Burying part of that
> > > > definition inside the function is arbitrary and unnecessary.
> > >
> > > Those two paragraphs contradict each other.  If the expression is
> > > evaluated in the function's context, then said evaluation is (by
> > > definition?) part of the function and not part of its argumens.
> >
> > The function header is a syntactic construct - the "def" line, any
> > decorators, annotations, etc.
>
> If you mean that def statements and decorators run at compile time, then
> I agree.  If you mean something else, then I don't understand.

"Function header" is not about time, it's about place.

def spam(x: int, y: int) -> Thingy:
...

The annotations might be evaluated at function definition time
(although some proposals are looking at changing that), but they may
also be evaluated long before that, during a static analysis phase. We
don't need a separate place in the code for "stuff that runs during
static analysis", because logically, it's all about those same
function parameters.

Late-bound argument defaults are still argument defaults. When you're
thinking about how you call the function, what matters is "this
argument is optional, and if you don't specify it, this is what
happens". Sometimes the definition of 'this' is a specific value
(calculated by evaluating an expression at definition time).
Sometimes, it's some other behaviour, defined by the function itself.
All this proposal does is make the most common of those into a new
option: defining it as an expression.

> > And the whole "early bind or late bind" question is there just the
> > same; the only difference is that the late binding happens somewhere
> > inside the function body, instead of being visible as part of the
> > function's header. (In this toy example, it's the very next line,
> > which isn't a major problem; but in real-world examples, it's often
> > buried deeper in the function, and it's not obvious that passing None
> > really is the same as passing the array's length, or using a system
> > random number generator, or constructing a new list, or whatever it
> > is.)
>
> It's only not obvious if the documentation is lacking, or the tools are
> lacking, or the programmer is lacking.  The deeper "it" is in the
> function, the more you make my point that it's part of the function
> itself and not part of setting up the arguments.

It currently is, due to a technical limitation. There's no particular
reason that it HAS to be. For instance, consider these two:

def popitem(items, which=-1): ...
def popitem(items, which=len(items) - 1): ...

Both of them allow you to omit the argument and get the last one. The
first one defines it with a simple value and relies on the fact that
you can subscript lists with -1 to get the last element; the second
doesn't currently work. Is there a fundamental difference between
them, or only a technical one?

> > This is, ultimately, the same teaching moment that you can get in
> > classes:
> >
> > class X:
> > items = []
> > def add_item(self, item): self.items.append(item)
> >
> > class Y:
> > def __init__(self): self.items = []
> > def add_item(self, item): self.items.append(item)
> >
> > Understanding these distinctions is crucial to understanding what your
> > code is doing. There's no getting away from that.
>
> Understanding the difference between defining a class and instantiating
> that class is crucial, as is noticing the very different source code
> contexts in which X.items and self.item are created.  I agree.

It's mainly about what [] means and when it's evaluated. Either way,
self.items is a list. The only difference is whether instantiating the
class creates a new list, or you keep referring to the same one every
time.

> Stuff in class definitions (X.items, X.add_item, Y.__init__, Y.add_item)
> happens when X is created, arguably at compile time.  The code inside
> the function suites (looking up and otherwise manipulating self.items)
> happens later, arguably at run-time.
>
> In f1, everything in the "def" statement happens when f1 is defined.  In
> f2, part of the "def" statement (i.e., defining f2) happens when f2 is
> defined (at compile-time), but the 

[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Ethan Furman

On 10/23/21 2:23 PM, Chris Angelico wrote:

> It seems like there's broad interest in this, but a lot of details to
> nut out. I think it may be time for me to write up a full PEP. Guido,
> if I'm understanding recent SC decisions correctly, a PEP editor can
> self-sponsor, correct?

Of all people, PEP editor or not, you should be able to self-sponsor.  ;-)

But if you need one, I'm happy to step up.

--
~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/LC6TOTPNPWJAM7SVGDUE5VEAUFNBTF6Y/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread 2QdxY4RzWzUUiLuE
On 2021-10-24 at 06:54:36 +1100,
Chris Angelico  wrote:

> On Sun, Oct 24, 2021 at 6:18 AM <2qdxy4rzwzuui...@potatochowder.com> wrote:
> > > The expression would be evaluated in the function's context, having
> > > available to it everything that the function has. Notably, this is NOT
> > > the same as the context of the function definition, but this is only
> > > rarely going to be significant (eg class methods where a bare name in
> > > an early-bound argument default would come from class scope, but the
> > > same bare name would come from local scope if late-bound).
> >
> > > The purpose of this change is to have the function header define, as
> > > fully as possible, the function's arguments. Burying part of that
> > > definition inside the function is arbitrary and unnecessary.
> >
> > Those two paragraphs contradict each other.  If the expression is
> > evaluated in the function's context, then said evaluation is (by
> > definition?) part of the function and not part of its argumens.
> 
> The function header is a syntactic construct - the "def" line, any
> decorators, annotations, etc.

If you mean that def statements and decorators run at compile time, then
I agree.  If you mean something else, then I don't understand.

> But for the late-binding expressions to be useful, they MUST be
> evaluated in the context of the function body, not its definition.
> That's the only way that expressions like len(a) can be of value.
> (Admittedly, this feature would have some value even without that, but
> it would be extremely surprising and restrictive.)

I think we're saying the same thing, but drawing different conclusions.
I agree with everything in the first paragraph I quoted above, but I
can't make the leap to claiming that late binding is part of defining
the function's arguments.  You say "late binding of function arguments";
I say "the part of the function that translates the arguments into
something useful for the algorithn the function encapsulates."

> > As a separate matter, are following (admittedly toy) functions (a) an
> > infinite confusion factory, or (b) a teaching moment?
> >
> > def f1(l=[]):
> > l.append(4)
> > return l
> >
> > def f2(l=:[]):
> > l.append(4)
> > return l
> 
> Teaching moment. Currently, the equivalent second function would be this:
> 
> def f2(l=None):
> if l is None: l = []
> l.append(4)
> return l
> 
> And the whole "early bind or late bind" question is there just the
> same; the only difference is that the late binding happens somewhere
> inside the function body, instead of being visible as part of the
> function's header. (In this toy example, it's the very next line,
> which isn't a major problem; but in real-world examples, it's often
> buried deeper in the function, and it's not obvious that passing None
> really is the same as passing the array's length, or using a system
> random number generator, or constructing a new list, or whatever it
> is.)

It's only not obvious if the documentation is lacking, or the tools are
lacking, or the programmer is lacking.  The deeper "it" is in the
function, the more you make my point that it's part of the function
itself and not part of setting up the arguments.

> This is, ultimately, the same teaching moment that you can get in
> classes:
> 
> class X:
> items = []
> def add_item(self, item): self.items.append(item)
> 
> class Y:
> def __init__(self): self.items = []
> def add_item(self, item): self.items.append(item)
> 
> Understanding these distinctions is crucial to understanding what your
> code is doing. There's no getting away from that.

Understanding the difference between defining a class and instantiating
that class is crucial, as is noticing the very different source code
contexts in which X.items and self.item are created.  I agree.

Stuff in class definitions (X.items, X.add_item, Y.__init__, Y.add_item)
happens when X is created, arguably at compile time.  The code inside
the function suites (looking up and otherwise manipulating self.items)
happens later, arguably at run-time.

In f1, everything in the "def" statement happens when f1 is defined.  In
f2, part of the "def" statement (i.e., defining f2) happens when f2 is
defined (at compile-time), but the other part (the logic surrounding l
and its default value) happens when f2 is called (at run-time).

> I'm aware that blessing this with nice syntax will likely lead to a
> lot of people (a) using late-binding everywhere, even if it's
> unnecessary; or (b) using early-binding, but then treating
> late-binding as a magic bandaid that fixes problems if you apply it in
> the right places. Programmers are lazy. We don't always go to the
> effort of understanding what things truly do. But we can't shackle
> ourselves just because some people will misuse a feature - we have
> plenty of footguns in every language, and it's understood that
> programmers should be allowed to use them if they choose.

I 

[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 7:58 AM Bruce Leban  wrote:
>
> On Sat, Oct 23, 2021 at 12:56 PM Guido van Rossum  wrote:
>>
>> I like that you're trying to fix this wart! I think that using a different 
>> syntax may be the only way out. My own bikeshed color to try would be `=>`, 
>> assuming we'll introduce `(x) => x+1` as the new lambda syntax, but I can 
>> see problems with both as well :-).
>>
>
> +1 to this spelling. I started writing a message arguing that this should be 
> spelled with lambda because the fact that you're (effectively) writing a 
> function should be explicit (see below). But the syntax is ugly needing both 
> a lambda and a variant = operator. This solves that elegantly.
>

Agreed; if it needs to remain self-contained, then yes, it would have
to be a function, and there'd be good reason for making it look like
one. As it is, it's more just "code that happens at the start of the
function", but I think there's still value in using a syntax that
people understand as a late binding system (as lambda functions will
be).

> On Sat, Oct 23, 2021 at 9:10 AM Chris Angelico  wrote:
>>
>> Proposal: Proper syntax and support for late-bound argument defaults.
>>
>> def bisect(a, x, lo=0, hi=:len(a)):
>> if lo < 0:
>> raise ValueError('lo must be non-negative')
>
>
>> The syntax I've chosen is deliberately subtle, since - in many many
>> cases - it won't make any difference whether the argument is early or
>> late bound, so they should look similar.
>
>
> I think a subtle difference in syntax is a bad idea since this is not a 
> subtle difference in behavior. If it makes no difference whether the argument 
> is early or late bound then you wouldn't be using it.
>

There IS a difference, but the difference should be subtle. Consider:

x = 5
x = 5.

These are deliberately similar, but they are quite definitely
different, and they behave differently. (You can't use the second one
to index a list, for instance.) Does the difference matter?
Absolutely. Does the similarity matter? Yep. Similar things should
look similar.

The current front-runner syntax is:

def bisect(a, x, lo=0, hi=>len(a)):

This is only slightly less subtle. It's still a one-character
difference which means that instead of being evaluated at definition
time, it's evaluated at call time. This is deliberate; it should still
look like (a) a parameter named "hi", (b) which is optional, and (c)
which will default to the result of evaluating "len(a)". That's a good
thing.

> Here's one way you could imagine writing this today:
>
> def bisect(a, x, lo=0, hi=lambda: len(a)):
> hi = hi() if callable(hi) else hi
> ...
>
> which is clumsy and more importantly doesn't work because the binding of the 
> lambda occurs in the function definition context which doesn't have access to 
> the other parameters.
>

Right. It also doesn't solve the help() problem, since it's just going
to show the (not-very-helpful) repr of a lambda function. It's not
really much better than using object() as a sentinel, although it does
at least avoid the global-pollution problem.

It seems like there's broad interest in this, but a lot of details to
nut out. I think it may be time for me to write up a full PEP. Guido,
if I'm understanding recent SC decisions correctly, a PEP editor can
self-sponsor, correct?

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/2LHM6OSINJ7WF65AMDPVWPLLGMQG456J/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 6:55 AM Guido van Rossum  wrote:
>
> I like that you're trying to fix this wart! I think that using a different 
> syntax may be the only way out. My own bikeshed color to try would be `=>`, 
> assuming we'll introduce `(x) => x+1` as the new lambda syntax, but I can see 
> problems with both as well :-).
>

Sounds good. I can definitely get behind this as the preferred syntax,
until such time as we find a serious problem.

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/GJJJK3IHNRD3NS2ECMUHIRVCZSJFUBUU/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Bruce Leban
On Sat, Oct 23, 2021 at 12:56 PM Guido van Rossum  wrote:

> I like that you're trying to fix this wart! I think that using a different
> syntax may be the only way out. My own bikeshed color to try would be `=>`,
> assuming we'll introduce `(x) => x+1` as the new lambda syntax, but I can
> see problems with both as well :-).
>
>
+1 to this spelling. I started writing a message arguing that this should
be spelled with lambda because the fact that you're (effectively) writing a
function should be explicit (see below). But the syntax is ugly needing
both a lambda and a variant = operator. This solves that elegantly.

On Sat, Oct 23, 2021 at 9:10 AM Chris Angelico  wrote:

> Proposal: Proper syntax and support for late-bound argument defaults.
>
> def bisect(a, x, lo=0, hi=:len(a)):
> if lo < 0:
> raise ValueError('lo must be non-negative')


The syntax I've chosen is deliberately subtle, since - in many many
> cases - it won't make any difference whether the argument is early or
> late bound, so they should look similar.
>

I think a subtle difference in syntax is a bad idea since this is not a
subtle difference in behavior. If it makes no difference whether the
argument is early or late bound then you wouldn't be using it.

Here's one way you could imagine writing this today:

def bisect(a, x, lo=0, hi=lambda: len(a)):
hi = hi() if callable(hi) else hi
...

which is clumsy and more importantly doesn't work because the binding of
the lambda occurs in the function definition context which doesn't have
access to the other parameters.



--- Bruce
___
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/M3RR7EMNA4ZOLJ2LZ6HUWLWRKGUQQQ2V/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Paul Moore
On Sat, 23 Oct 2021 at 17:09, Chris Angelico  wrote:
>
> Proposal: Proper syntax and support for late-bound argument defaults.
>
> def spaminate(thing, count=:thing.getdefault()):
> ...
>
> def bisect(a, x, lo=0, hi=:len(a)):
> if lo < 0:
> raise ValueError('lo must be non-negative')
>

+1 from me. I agree that getting a good syntax will be tricky, but I
like the functionality. I do quite like Guido's "hi=>len(a)" syntax,
but I admit I'm not seeing the potential issues he alludes to, so
maybe I'm missing something :-)

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/7Y2O3XTWCSBPVISSXJKCXKK7VDTVKH6H/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 6:18 AM <2qdxy4rzwzuui...@potatochowder.com> wrote:
> > The expression would be evaluated in the function's context, having
> > available to it everything that the function has. Notably, this is NOT
> > the same as the context of the function definition, but this is only
> > rarely going to be significant (eg class methods where a bare name in
> > an early-bound argument default would come from class scope, but the
> > same bare name would come from local scope if late-bound).
>
> > The purpose of this change is to have the function header define, as
> > fully as possible, the function's arguments. Burying part of that
> > definition inside the function is arbitrary and unnecessary.
>
> Those two paragraphs contradict each other.  If the expression is
> evaluated in the function's context, then said evaluation is (by
> definition?) part of the function and not part of its argumens.

The function header is a syntactic construct - the "def" line, any
decorators, annotations, etc.

But for the late-binding expressions to be useful, they MUST be
evaluated in the context of the function body, not its definition.
That's the only way that expressions like len(a) can be of value.
(Admittedly, this feature would have some value even without that, but
it would be extremely surprising and restrictive.)

> As a separate matter, are following (admittedly toy) functions (a) an
> infinite confusion factory, or (b) a teaching moment?
>
> def f1(l=[]):
> l.append(4)
> return l
>
> def f2(l=:[]):
> l.append(4)
> return l

Teaching moment. Currently, the equivalent second function would be this:

def f2(l=None):
if l is None: l = []
l.append(4)
return l

And the whole "early bind or late bind" question is there just the
same; the only difference is that the late binding happens somewhere
inside the function body, instead of being visible as part of the
function's header. (In this toy example, it's the very next line,
which isn't a major problem; but in real-world examples, it's often
buried deeper in the function, and it's not obvious that passing None
really is the same as passing the array's length, or using a system
random number generator, or constructing a new list, or whatever it
is.)

This is also why the evaluation has to happen in the function's
context: the two forms should be broadly equivalent. You should be
able to explain a late-bound function default argument by saying "it's
like using =None and then checking for None in the function body, only
it doesn't use None like that".

This is, ultimately, the same teaching moment that you can get in classes:

class X:
items = []
def add_item(self, item): self.items.append(item)

class Y:
def __init__(self): self.items = []
def add_item(self, item): self.items.append(item)

Understanding these distinctions is crucial to understanding what your
code is doing. There's no getting away from that.

I'm aware that blessing this with nice syntax will likely lead to a
lot of people (a) using late-binding everywhere, even if it's
unnecessary; or (b) using early-binding, but then treating
late-binding as a magic bandaid that fixes problems if you apply it in
the right places. Programmers are lazy. We don't always go to the
effort of understanding what things truly do. But we can't shackle
ourselves just because some people will misuse a feature - we have
plenty of footguns in every language, and it's understood that
programmers should be allowed to use them if they choose.

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/FO4D3ZCFL2BE6E7ZODYJN23ELMOS75GR/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Guido van Rossum
I like that you're trying to fix this wart! I think that using a different
syntax may be the only way out. My own bikeshed color to try would be `=>`,
assuming we'll introduce `(x) => x+1` as the new lambda syntax, but I can
see problems with both as well :-).

-- 
--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/T4VPHDZJUP4QYIXJA26JQ6EQJJKRUEYP/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread 2QdxY4RzWzUUiLuE
On 2021-10-24 at 03:07:45 +1100,
Chris Angelico  wrote:

> def bisect(a, x, lo=0, hi=None):
> if lo < 0:
> raise ValueError('lo must be non-negative')
> if hi is None:
> hi = len(a)
> 
> It's clear what value lo gets if you omit it. It's less clear what hi
> gets. And the situation only gets uglier if None is a valid argument,
> and a unique sentinel is needed; this standard idiom makes help()
> rather unhelpful:
> 
> _missing = object()
> def spaminate(thing, count=_missing):
> if count is _missing: count = thing.getdefault()
> 
> Proposal: Proper syntax and support for late-bound argument defaults.
> 
> def spaminate(thing, count=:thing.getdefault()):
> ...
> 
> def bisect(a, x, lo=0, hi=:len(a)):
> if lo < 0:
> raise ValueError('lo must be non-negative')

[...]

> The expression would be evaluated in the function's context, having
> available to it everything that the function has. Notably, this is NOT
> the same as the context of the function definition, but this is only
> rarely going to be significant (eg class methods where a bare name in
> an early-bound argument default would come from class scope, but the
> same bare name would come from local scope if late-bound).

> The purpose of this change is to have the function header define, as
> fully as possible, the function's arguments. Burying part of that
> definition inside the function is arbitrary and unnecessary.

Those two paragraphs contradict each other.  If the expression is
evaluated in the function's context, then said evaluation is (by
definition?) part of the function and not part of its argumens.

As a separate matter, are following (admittedly toy) functions (a) an
infinite confusion factory, or (b) a teaching moment?

def f1(l=[]):
l.append(4)
return l

def f2(l=:[]):
l.append(4)
return l
___
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/5AFFSYSQDCPBKZ3JFPEZURVYFIHJRVXB/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 4:12 AM Ethan Furman  wrote:
>
> On 10/23/21 9:44 AM, Chris Angelico wrote:
>
>  > Feel free to propose an improvement to the syntax. Whatever spelling
>  > is ultimately used, this would still be of value.
>
>  def bisect(a, x, lo=0, hi?=len(a)):
>

Ah, okay. I had considered =? as an option, but that's also viable. I
don't think there's any situation where this would be ambiguous, so
I'll docket that as another possibility. Thanks.

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/EF6QQWZORFHECDM2MJRKGWVAEPKU3I27/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: def variable = value

2021-10-23 Thread Jonathan Fine
>From my phone.

An important thing about def x and class A is that the strings x and A are
made available to the constructor for x and A respectively. The same is not
true for x=val.

Jonathan
___
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/GMUQMIGOHZGXF5VA6DG5SBM7WQRQBF7O/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: def variable = value

2021-10-23 Thread Steven D'Aprano
Why would anyone want to type:

def variable = value

when they could just type:

variable = value

instead? Perhaps if I was being paid by the character typed...

def   my__really__good__variable  =  (  value  )# Assign value to 
my__really__good__variable.

*wink*

But all joking aside, what benefit would this "def" statement have? Why 
would I want to use it?

On Sat, Oct 23, 2021 at 04:37:20PM -, blek blek wrote:

> I was thinking of a "def" statement in Python.
> Its really weird that it can define only method, since it means "define".

It means *define function*, not just "define anything". We don't use def 
to define classes, or modules, or lists, or other objects.


-- 
Steve
___
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/D6ALT2A35PJFD64ZBZTMOLRBVDW27AF6/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Ethan Furman

On 10/23/21 9:44 AM, Chris Angelico wrote:

> Feel free to propose an improvement to the syntax. Whatever spelling
> is ultimately used, this would still be of value.

def bisect(a, x, lo=0, hi?=len(a)):

--
~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/HHAD2XUPIIERWOK5CU3MTTSDGWWBVJ7P/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] def variable = value

2021-10-23 Thread blek blek
Hello people,
I was thinking of a "def" statement in Python.
Its really weird that it can define only method, since it means "define".

It shouldn't be hard to detect if this is a variable or a method, because 
method needs "()" to have arguments(even if there are zero of them), 
and defining variable value supposed to have "=" as the definition mark.

Hope it would be added someday
___
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/P7V5SIWR3VGKJZ6KCN2PI5VZ4JNZKYSB/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
On Sun, Oct 24, 2021 at 3:26 AM Ethan Furman  wrote:
>
> On 10/23/21 9:07 AM, Chris Angelico wrote:
>
>  > Proposal: Proper syntax and support for late-bound argument defaults.
>
> I like the proposal.
>
>  > def spaminate(thing, count=:thing.getdefault()):
>  >  ...
>  >
>  > def bisect(a, x, lo=0, hi=:len(a)):
>  >  if lo < 0:
>  >  raise ValueError('lo must be non-negative')
>
> But not the color -- I see "=:" too easily confused with ":=".  However, I do 
> like:
>

(Truncated email, I presume)

Yeah, I'm not wedded to the precise syntax. But it needs to be simple
and easy to read, it needs to not be ugly, and it needs to not be
valid syntax already. There are a few alternatives, but I like them
even less:

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

Feel free to propose an improvement to the syntax. Whatever spelling
is ultimately used, this would still be of value.

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/VI566NWZT3OKE6HWRF3BJXODLV2ACODR/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Syntax for late-bound arguments

2021-10-23 Thread Ethan Furman

On 10/23/21 9:07 AM, Chris Angelico wrote:

> Proposal: Proper syntax and support for late-bound argument defaults.

I like the proposal.

> def spaminate(thing, count=:thing.getdefault()):
>  ...
>
> def bisect(a, x, lo=0, hi=:len(a)):
>  if lo < 0:
>  raise ValueError('lo must be non-negative')

But not the color -- I see "=:" too easily confused with ":=".  However, I do 
like:

--
~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/ZEN4ZGBSMDCUW5JT5LLD45NKQIQDC5UT/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Syntax for late-bound arguments

2021-10-23 Thread Chris Angelico
Spun off from PEP 505 thread on python-dev.

One of the weaker use-cases for None-coalescing is function default
arguments, where the technical default is None but the function wants
to use some other value instead. This is a weak argument in favour of
None-coalescing because only a small set of such situations are only
slightly improved by a "??" operator, and the problem is far larger.
Consider the bisect.bisect() function [1]:

def bisect(a, x, lo=0, hi=None):
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)

It's clear what value lo gets if you omit it. It's less clear what hi
gets. And the situation only gets uglier if None is a valid argument,
and a unique sentinel is needed; this standard idiom makes help()
rather unhelpful:

_missing = object()
def spaminate(thing, count=_missing):
if count is _missing: count = thing.getdefault()

Proposal: Proper syntax and support for late-bound argument defaults.

def spaminate(thing, count=:thing.getdefault()):
...

def bisect(a, x, lo=0, hi=:len(a)):
if lo < 0:
raise ValueError('lo must be non-negative')

This would be approximately equivalent to the _missing idiom, with the
following exceptions:

1) Inspecting the function would reveal the source code for the late-bound value
2) There is no value which can be passed as an argument to achieve the
same effect
3) All late-bound defaults would be evaluated before any other part of
the function executes (ie the "if hi is None" check would now be done
prior to "if lo < 0").

The syntax I've chosen is deliberately subtle, since - in many many
cases - it won't make any difference whether the argument is early or
late bound, so they should look similar. While it is visually similar
to the inline assignment operator, neither form is currently valid in
a function's parameter list, and thus should not introduce ambiguity.

The expression would be evaluated in the function's context, having
available to it everything that the function has. Notably, this is NOT
the same as the context of the function definition, but this is only
rarely going to be significant (eg class methods where a bare name in
an early-bound argument default would come from class scope, but the
same bare name would come from local scope if late-bound).

The purpose of this change is to have the function header define, as
fully as possible, the function's arguments. Burying part of that
definition inside the function is arbitrary and unnecessary.

ChrisA

[1] Technically it's bisect_right but I'm using the simpler name here.
Also, it recently grew a key parameter, which would only get in the
way here. https://docs.python.org/3/library/bisect.html#bisect.bisect_right
___
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/LWWVXBXNAUVANWGKAJDGBWQFDPSPQU4E/
Code of Conduct: http://python.org/psf/codeofconduct/