Quick idea: defining variables from functions that take the variable name

In mypy we have a need for type variables, which are created like this: from typing import TypeVar T = TypeVar('T') I just saw a lightning talk about sympy where they define symbols to be used in mathematical equations, like this: from sympy import Symbol x = Symbol('x') I'm sure this is not a new idea, but so far I've always thought that this is pretty esoteric and the approach here is good enough. But maybe we can actually do better, and whatever solution we come up with might also be useful for extracting attributes from an object or values from a mapping? -- --Guido van Rossum (python.org/~guido)

On May 30, 2016 6:20 PM, "Guido van Rossum" <guido@python.org> wrote:
In mypy we have a need for type variables, which are created like this:
from typing import TypeVar
T = TypeVar('T')
I just saw a lightning talk about sympy where they define symbols to be used in mathematical equations, like this:
from sympy import Symbol
x = Symbol('x')
I'm sure this is not a new idea, but so far I've always thought that this is pretty esoteric and the approach here is good enough. But maybe we can actually do better....
One of the negatives of the above usages is that the two names don't have to match. I can do: Q = TypeVar('U') for example. Dedicated syntax could fix that, e.g., from typing import TypeVar as T$ And instead of writing x I write T$x. That compiles to a call to TypeVar('x'). A function imported this way would be required to always return the same value. That is T$xyz is T$xyz and the compiler would be free to optimize calls away or not. If I want a runtime eval that won't be optimized away I can write T$('x' + 'yz') which is T$xyz. Of course $ could be :: or ! or something else.x Nothing prevents me from writing x = T$x but I can be assured that this is the same as T$x elsewhere (barring different code importing different functions under the same name). --- Bruce

On Mon, May 30, 2016 at 06:16:26PM -0700, Guido van Rossum wrote: [...]
T = TypeVar('T') x = Symbol('x')
I'm sure this is not a new idea, but so far I've always thought that this is pretty esoteric and the approach here is good enough. But maybe we can actually do better, and whatever solution we come up with might also be useful for extracting attributes from an object or values from a mapping?
This comes up a lot and it would be nice to clean it up. T = type('T', bases, ns) Record = namedtuple('Record', fields) It came up in the discussion on dict unpacking: a, b, c = **dict # unpacks keys 'a', 'b', 'c' which was rejected as too magical. But maybe it will seem less magical if we have a special assignment operator that takes the left hand symbol(s) and copies them to the right? How do you feel about an arrow operator? T -> TypeVar() x -> Symbol() T -> type(bases, ns) Record -> namedtuple(fields) In general: name [, name, ...] -> callable(...) where "callable" is some expression, e.g. a name, a dot lookup, etc. # okay name -> sympy.Symbol() The easy case ------------- All assignment targets are plain names. The arrow operator takes the names, turns them into strings, and passes them to the callable on the right as a tuple of strings, given as the first positional argument. Since the most common case will be a single target, I think it is worth while to treat it as a special case and pass just a string: T -> Type() # like Type('T') a, b, c -> Spam(arg) # like Spam(('a', 'b', 'c'), arg) The callable being called doesn't need to know or care whether it is being called with the -> or = assignment. It just receives the name(s) as the first argument. This will mean that callables that take the name as (say) the second argument cannot be used with this syntax. Using the arrow operator with arbitrary expressions on the right will be a SyntaxError: x -> y + 1 my_name -> spam*f() my_name -> f() or g() I reject that third example as it is unclear whether both f and g, or only f, get the name as first argument. But this is allowed: my_name -> f(g()) # like f('my_name', g()) If f isn't *actually* a callable, you get a runtime TypeError as usual. The hard case ------------- Assignment target(s) is not a plain name. spam[2].eggs().attr -> Symbol() I'm going to take the cowards way out and just say "we don't allow that". -- Steve

That all sounds not unreasonable, except the -> operator feels kind of wrong given its use in function signatures. In terms of restricted syntax, the precedent for such syntax restrictions would be decorators, which also don't allow arbitrary expressions after the @. In fact, maybe we could somehow unifying this with structural unpacking *and* variable decorators (also oft-requested)? I know you must think "Guido is high" or "someone broke into Guido's account". My actual excuse is that I'm procrastinating on rehearsing my PyCon keynote, which is in about 11 hours. So don't take all this as too much of a ringing endorsement. :-) On Mon, May 30, 2016 at 8:08 PM, Steven D'Aprano <steve@pearwood.info> wrote:
On Mon, May 30, 2016 at 06:16:26PM -0700, Guido van Rossum wrote:
[...]
T = TypeVar('T') x = Symbol('x')
I'm sure this is not a new idea, but so far I've always thought that this is pretty esoteric and the approach here is good enough. But maybe we can actually do better, and whatever solution we come up with might also be useful for extracting attributes from an object or values from a mapping?
This comes up a lot and it would be nice to clean it up.
T = type('T', bases, ns) Record = namedtuple('Record', fields)
It came up in the discussion on dict unpacking:
a, b, c = **dict # unpacks keys 'a', 'b', 'c'
which was rejected as too magical. But maybe it will seem less magical if we have a special assignment operator that takes the left hand symbol(s) and copies them to the right?
How do you feel about an arrow operator?
T -> TypeVar() x -> Symbol() T -> type(bases, ns) Record -> namedtuple(fields)
In general:
name [, name, ...] -> callable(...)
where "callable" is some expression, e.g. a name, a dot lookup, etc.
# okay name -> sympy.Symbol()
The easy case -------------
All assignment targets are plain names.
The arrow operator takes the names, turns them into strings, and passes them to the callable on the right as a tuple of strings, given as the first positional argument.
Since the most common case will be a single target, I think it is worth while to treat it as a special case and pass just a string:
T -> Type() # like Type('T')
a, b, c -> Spam(arg) # like Spam(('a', 'b', 'c'), arg)
The callable being called doesn't need to know or care whether it is being called with the -> or = assignment. It just receives the name(s) as the first argument.
This will mean that callables that take the name as (say) the second argument cannot be used with this syntax.
Using the arrow operator with arbitrary expressions on the right will be a SyntaxError:
x -> y + 1 my_name -> spam*f() my_name -> f() or g()
I reject that third example as it is unclear whether both f and g, or only f, get the name as first argument.
But this is allowed:
my_name -> f(g()) # like f('my_name', g())
If f isn't *actually* a callable, you get a runtime TypeError as usual.
The hard case -------------
Assignment target(s) is not a plain name.
spam[2].eggs().attr -> Symbol()
I'm going to take the cowards way out and just say "we don't allow that".
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)

I like the arrow idea, another option is a syntax more in line with the one being discussed for dict unpacking, something like adding a __define__ dunder that returns a tuple of (name, value). So that say, Typevar's __define__ returns the tuple `(self.__name__, self)`. Which is then converted into an assignment. The downside of this being that its assignment overloading(which can be abused), but at least it can only be done when specifically asked for. The other downside is that the syntax define TypeVar('T') # and I assume we can also declare a list of them def ident(t: T) -> T: return t looks both foreign and a bit magical. Also new keyword, but I have a feeling that the same keyword could probably be used here and with dict unpacking. (the *really* big downside is for a library implementer to do something devious like have __define__ always return ("constant", self), or generally something that wasn't clear to the caller, because we get names appearing out of no where with no clear history, but my argument would be that we should trust libraries not to do that and say that it is a very bad thing) The upside is that it avoids the problem of Steven's implementation, since the name is decided internally by the callable. Like with his example, define `Typevar('7')` and similar invalid names would throw an error, in this case probably a TypeError. --Josh On Mon, May 30, 2016 at 11:08 PM Steven D'Aprano <steve@pearwood.info> wrote:
On Mon, May 30, 2016 at 06:16:26PM -0700, Guido van Rossum wrote:
[...]
T = TypeVar('T') x = Symbol('x')
I'm sure this is not a new idea, but so far I've always thought that this is pretty esoteric and the approach here is good enough. But maybe we can actually do better, and whatever solution we come up with might also be useful for extracting attributes from an object or values from a mapping?
This comes up a lot and it would be nice to clean it up.
T = type('T', bases, ns) Record = namedtuple('Record', fields)
It came up in the discussion on dict unpacking:
a, b, c = **dict # unpacks keys 'a', 'b', 'c'
which was rejected as too magical. But maybe it will seem less magical if we have a special assignment operator that takes the left hand symbol(s) and copies them to the right?
How do you feel about an arrow operator?
T -> TypeVar() x -> Symbol() T -> type(bases, ns) Record -> namedtuple(fields)
In general:
name [, name, ...] -> callable(...)
where "callable" is some expression, e.g. a name, a dot lookup, etc.
# okay name -> sympy.Symbol()
The easy case -------------
All assignment targets are plain names.
The arrow operator takes the names, turns them into strings, and passes them to the callable on the right as a tuple of strings, given as the first positional argument.
Since the most common case will be a single target, I think it is worth while to treat it as a special case and pass just a string:
T -> Type() # like Type('T')
a, b, c -> Spam(arg) # like Spam(('a', 'b', 'c'), arg)
The callable being called doesn't need to know or care whether it is being called with the -> or = assignment. It just receives the name(s) as the first argument.
This will mean that callables that take the name as (say) the second argument cannot be used with this syntax.
Using the arrow operator with arbitrary expressions on the right will be a SyntaxError:
x -> y + 1 my_name -> spam*f() my_name -> f() or g()
I reject that third example as it is unclear whether both f and g, or only f, get the name as first argument.
But this is allowed:
my_name -> f(g()) # like f('my_name', g())
If f isn't *actually* a callable, you get a runtime TypeError as usual.
The hard case -------------
Assignment target(s) is not a plain name.
spam[2].eggs().attr -> Symbol()
I'm going to take the cowards way out and just say "we don't allow that".
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

3 years ago, I've proposed syntax def name = expr for binding names to arbitrary objects https://mail.python.org/pipermail/python-ideas/2013-May/020501.html 2016-05-31 6:57 GMT+02:00 Joshua Morton <joshua.morton13@gmail.com>:
I like the arrow idea, another option is a syntax more in line with the one being discussed for dict unpacking, something like adding a __define__ dunder that returns a tuple of (name, value). So that say, Typevar's __define__ returns the tuple `(self.__name__, self)`. Which is then converted into an assignment. The downside of this being that its assignment overloading(which can be abused), but at least it can only be done when specifically asked for. The other downside is that the syntax
define TypeVar('T') # and I assume we can also declare a list of them def ident(t: T) -> T: return t
looks both foreign and a bit magical. Also new keyword, but I have a feeling that the same keyword could probably be used here and with dict unpacking. (the *really* big downside is for a library implementer to do something devious like have __define__ always return ("constant", self), or generally something that wasn't clear to the caller, because we get names appearing out of no where with no clear history, but my argument would be that we should trust libraries not to do that and say that it is a very bad thing)
The upside is that it avoids the problem of Steven's implementation, since the name is decided internally by the callable.
Like with his example, define `Typevar('7')` and similar invalid names would throw an error, in this case probably a TypeError.
--Josh
On Mon, May 30, 2016 at 11:08 PM Steven D'Aprano <steve@pearwood.info> wrote:
On Mon, May 30, 2016 at 06:16:26PM -0700, Guido van Rossum wrote:
[...]
T = TypeVar('T') x = Symbol('x')
I'm sure this is not a new idea, but so far I've always thought that this is pretty esoteric and the approach here is good enough. But maybe we can actually do better, and whatever solution we come up with might also be useful for extracting attributes from an object or values from a mapping?
This comes up a lot and it would be nice to clean it up.
T = type('T', bases, ns) Record = namedtuple('Record', fields)
It came up in the discussion on dict unpacking:
a, b, c = **dict # unpacks keys 'a', 'b', 'c'
which was rejected as too magical. But maybe it will seem less magical if we have a special assignment operator that takes the left hand symbol(s) and copies them to the right?
How do you feel about an arrow operator?
T -> TypeVar() x -> Symbol() T -> type(bases, ns) Record -> namedtuple(fields)
In general:
name [, name, ...] -> callable(...)
where "callable" is some expression, e.g. a name, a dot lookup, etc.
# okay name -> sympy.Symbol()
The easy case -------------
All assignment targets are plain names.
The arrow operator takes the names, turns them into strings, and passes them to the callable on the right as a tuple of strings, given as the first positional argument.
Since the most common case will be a single target, I think it is worth while to treat it as a special case and pass just a string:
T -> Type() # like Type('T')
a, b, c -> Spam(arg) # like Spam(('a', 'b', 'c'), arg)
The callable being called doesn't need to know or care whether it is being called with the -> or = assignment. It just receives the name(s) as the first argument.
This will mean that callables that take the name as (say) the second argument cannot be used with this syntax.
Using the arrow operator with arbitrary expressions on the right will be a SyntaxError:
x -> y + 1 my_name -> spam*f() my_name -> f() or g()
I reject that third example as it is unclear whether both f and g, or only f, get the name as first argument.
But this is allowed:
my_name -> f(g()) # like f('my_name', g())
If f isn't *actually* a callable, you get a runtime TypeError as usual.
The hard case -------------
Assignment target(s) is not a plain name.
spam[2].eggs().attr -> Symbol()
I'm going to take the cowards way out and just say "we don't allow that".
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- 闇に隠れた黒い力 弱い心を操る

On 31May2016 04:57, Joshua Morton <joshua.morton13@gmail.com> wrote:
I like the arrow idea, another option is a syntax more in line with the one being discussed for dict unpacking, something like adding a __define__ dunder that returns a tuple of (name, value). So that say, Typevar's __define__ returns the tuple `(self.__name__, self)`. Which is then converted into an assignment. The downside of this being that its assignment overloading(which can be abused), but at least it can only be done when specifically asked for. The other downside is that the syntax
define TypeVar('T') # and I assume we can also declare a list of them def ident(t: T) -> T: return t [...] The upside is that it avoids the problem of Steven's implementation, since the name is decided internally by the callable.
Which to me is a downside. A big downside. Ignoring "from blah import *", when I write a Python module I have complete control over the local names: from os.path import join from os.path import join as joinpath x = 3 def y(): All of these are only capable of defining names that I have typed literally into my code. If I understand Joshua's suggestion: define TypeVar('T') might define _any_ name, because the __define__ dunder can return any name. Maybe I misunderstand, but otherwise I think this is like a little landmine. Yes, code can monkey patch. But this feels more insidious. Why not require the __dunder__ to always return __name__? And then perhaps support both: define TypeVar('T') define TypeVar('T') as Not_T so that providing the local name (which we're trying to avoid needing to do) _is_ possible, as the special case as it is with import. Cheers, Cameron Simpson <cs@zip.com.au>

On 31 May 2016 at 04:08, Steven D'Aprano <steve@pearwood.info> wrote:
How do you feel about an arrow operator?
T -> TypeVar() x -> Symbol() T -> type(bases, ns) Record -> namedtuple(fields)
I like this. Simplifying it, how about name [, name ...] -> callable which is equivalent to name [, name ...] = callable(('name' [, 'name' ... ])) I.e., the RHS is a callable, and we call it with the names. No need to inject an argument into an existing call. This avoids any confusion over the RHS being "too complex". For callables that have extra arguments, just use functools.partial. I agree with the special case of a single name becoming a string (rather than a tuple of strings). If you want a 1-tuple, do "name, -> function" (the same trick as for unpacking assignment). As for the operator name, I like -> but I see why Guido might object because of the type signature usage (although is there actually any clash?) Alternatives could be := (from Pascal, although that doesn't have the implication of "sending the LHS to the right") or => (which might work, but is easily confused with >=/<=). If a 3-character symbol is acceptable, ->> might work. Keywords don't really sit right with me for this. Paul

Paul Moore writes:
On 31 May 2016 at 04:08, Steven D'Aprano <steve@pearwood.info> wrote:
How do you feel about an arrow operator?
T -> TypeVar() x -> Symbol() T -> type(bases, ns) Record -> namedtuple(fields)
I like this.
Well, it's cuter than Hello Kitty, what's not to like? But ... It give me no clue what it's supposed to mean. In math the forward arrow is often used to name a function type (including the domain preceding the arrow and the codomain following it in the name), although what the parentheses and their contents mean, I don't know. The codomain type is the value of TypeVar()? What's that? In Pascal and R, the reverse arrow T <- TypeVar() is used to mean assignment (which you could read as "T receives TypeVar()", but the implicit argument on the RHS is a double-take for me in both syntaxes -- the argument to TypeVar is not optional! IIRC, we have two syntaxes in Python itself that take the name of an identifier and reify it as a string (ie, inject it into a namespace): def and class. I know you don't think a keyword works for you, but either the recently reraised "def <name> = <type-expr>" or perhaps "type <name>: <type-expr>" make more sense to me right out of the box. Another problem is that none of these syntaxes (including the keyword- based ones) provides a clue as to whether the type is a distinct type or a type alias. It's not that one couldn't learn, but it doesn't seem very Pythonic to me: not conformant to EIBTI and creating ambiguity that forces the programmer to guess (or even more painful, read the doc!) I'm +1 for stopping the bikeshedding until we've all got a lot of stubfile reading under our belts.

On 31 May 2016 at 09:56, Stephen J. Turnbull <stephen@xemacs.org> wrote:
I know you don't think a keyword works for you, but either the recently reraised "def <name> = <type-expr>" or perhaps "type <name>: <type-expr>" make more sense to me right out of the box.
I was thinking along the lines of "name <some_keyword> callable", which I don't think works because it needs some "punctuation" to separate the name from the callable. But "def name = callable" (or some other preceding keyword combined with =) might work. I don't like "type" though, as the point here (I thought - see below) is to come up with a construct useful for more than just types.
I'm +1 for stopping the bikeshedding until we've all got a lot of stubfile reading under our belts.
If this was simply about type definitions, I'd agree. But I thought the point of Guido's post was that having seen two examples (TypeVar and Symbol) is there a more general approach that might cover these two cases as well as others? So just looking at the problem in terms of stub files isn't really the point here. Paul

On 31.05.2016 11:05, Paul Moore wrote:
If this was simply about type definitions, I'd agree. But I thought the point of Guido's post was that having seen two examples (TypeVar and Symbol) is there a more general approach that might cover these two cases as well as others? So just looking at the problem in terms of stub files isn't really the point here.
I don't know why this needs special syntax anyway. Maybe, somebody could explain. Even Guido said it was just for procrastinating. So, I don't give much weight to it. Anyway, what's the difference between: a = <something> and def a = <something>

And here we go again. Sorry for posting unfinished stuff: On 31.05.2016 11:05, Paul Moore wrote:
If this was simply about type definitions, I'd agree. But I thought the point of Guido's post was that having seen two examples (TypeVar and Symbol) is there a more general approach that might cover these two cases as well as others? So just looking at the problem in terms of stub files isn't really the point here.
I don't know why this needs special syntax anyway. Maybe, somebody could explain. Even Guido said it was just for procrastinating. So, I don't give much weight to it. Anyway, what's the difference between: a = <something> and def a = <something> ? Both evaluate RHS and assign the result to a name (if not already defined, define the name) The only benefit I can see is some a syntax like: def a which defines a name in the scope without content. If that's useful? Maybe. Best, Sven

On Tue, May 31, 2016 at 03:01:48PM +0200, Sven R. Kunze wrote:
And here we go again. Sorry for posting unfinished stuff:
On 31.05.2016 11:05, Paul Moore wrote:
If this was simply about type definitions, I'd agree. But I thought the point of Guido's post was that having seen two examples (TypeVar and Symbol) is there a more general approach that might cover these two cases as well as others? So just looking at the problem in terms of stub files isn't really the point here.
I don't know why this needs special syntax anyway. Maybe, somebody could explain.
Any time you have an object that needs to know its own name, you have to provide it as a string, AND as an assignment target: T = TypeVar('T') x = sympy.Symbol('x') myclass = namedtuple("myclass", fields) klass = type('klass', bases, ns) The only exceptions are when you can use compiler magic do to it for you. We don't have to write these: math = import math func = def func(arg): ... MyClass = class MyClass(Parent): ... because the compiler does it for us. Likewise we have @ decorator syntax to avoid writing the function name three times: def spam(): ... spam = decorate(spam) This solves the same problem for ordinary assignment: how to get the name or names on the left hand side over to the right hand side without re-typing them as strings?
Even Guido said it was just for procrastinating. So, I don't give much weight to it.
This comes up from time to time. It was one of the motives for adding @ decorator syntax, so maybe its the right time for it now.
Anyway, what's the difference between:
a = <something>
and
def a = <something>
?
Both evaluate RHS and assign the result to a name (if not already defined, define the name)
The "def a = ..." is not my suggested syntax, so I can't tell you exactly what it will do, but *my* suggested syntax is: name -> Function(args) will be expanded to: name = Function('name', args) by the compiler. The "def a = ..." syntax will probably be similar. Somehow, in some fashion, the name "a" on the LHS will be passed as a string to something on the RHS. -- Steve

On 31.05.2016 15:37, Steven D'Aprano wrote:
because the compiler does it for us. Likewise we have @ decorator syntax to avoid writing the function name three times:
def spam(): ...
spam = decorate(spam)
This solves the same problem for ordinary assignment: how to get the name or names on the left hand side over to the right hand side without re-typing them as strings?
So, why don't do it for all assignments without a special syntax? Btw. there would be huge use-case for Django development. So, given your explanation, it sounds useful. Sven

On 31 May 2016, at 16:37, Sven R. Kunze <srkunze@mail.de> wrote:
Btw. there would be huge use-case for Django development. So, given your explanation, it sounds useful.
Could you elaborate on that use-case, because I'm not seeing it. I think maybe you mean the ORM and declarative forms, correct? Maybe knowing a use-case could better direct the efforts?

On 01.06.2016 08:25, Sjoerd Job Postmus wrote:
On 31 May 2016, at 16:37, Sven R. Kunze <srkunze@mail.de> wrote:
Btw. there would be huge use-case for Django development. So, given your explanation, it sounds useful. Could you elaborate on that use-case, because I'm not seeing it. I think maybe you mean the ORM and declarative forms, correct?
Exactly. That is one reason why Django needs metaclasses all over the place. Because fields just don't know their name. This is especially true for forms as django does not magic there. It always causes headaches if you need it. Best, Sven

On 1 Jun 2016, at 15:22, Sven R. Kunze <srkunze@mail.de> wrote:
On 01.06.2016 08:25, Sjoerd Job Postmus wrote:
On 31 May 2016, at 16:37, Sven R. Kunze <srkunze@mail.de> wrote:
Btw. there would be huge use-case for Django development. So, given your explanation, it sounds useful. Could you elaborate on that use-case, because I'm not seeing it. I think maybe you mean the ORM and declarative forms, correct?
Exactly. That is one reason why Django needs metaclasses all over the place. Because fields just don't know their name.
This is especially true for forms as django does not magic there. It always causes headaches if you need it.
Best, Sven _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
As an alternative suggestion, maybe there could be a `get_assignment_target` function which returns either a string, or `None`. No new syntax required at all, but it comes across to me as completely hacky.

On Wed, Jun 1, 2016 at 9:23 AM Sven R. Kunze <srkunze@mail.de> wrote:
On 01.06.2016 08:25, Sjoerd Job Postmus wrote:
On 31 May 2016, at 16:37, Sven R. Kunze <srkunze@mail.de> wrote:
Btw. there would be huge use-case for Django development. So, given your explanation, it sounds useful. Could you elaborate on that use-case, because I'm not seeing it. I think maybe you mean the ORM and declarative forms, correct?
Exactly. That is one reason why Django needs metaclasses all over the place. Because fields just don't know their name.
This is especially true for forms as django does not magic there. It always causes headaches if you need it.
Are you referring to code like this [0]? your_name = forms.CharField(label='Your name') If so, I don't think we actually want the LHS to get passed in as the label. Not all labels will be valid identifiers. If you're talking about the CharField knowing what attribute it has been assigned to, I couldn't find that code in the Django source. Could you point it out? [0] https://docs.djangoproject.com/en/1.9/topics/forms/#the-form-class

On Wed, Jun 1, 2016 at 12:12 PM Michael Selik <michael.selik@gmail.com> wrote:
On Wed, Jun 1, 2016 at 9:23 AM Sven R. Kunze <srkunze@mail.de> wrote:
On 31 May 2016, at 16:37, Sven R. Kunze <srkunze@mail.de> wrote:
Btw. there would be huge use-case for Django development. So, given your explanation, it sounds useful. Could you elaborate on that use-case, because I'm not seeing it. I
On 01.06.2016 08:25, Sjoerd Job Postmus wrote: think maybe you mean the ORM and declarative forms, correct?
Exactly. That is one reason why Django needs metaclasses all over the place. Because fields just don't know their name.
This is especially true for forms as django does not magic there. It always causes headaches if you need it.
Are you referring to code like this [0]? your_name = forms.CharField(label='Your name')
If so, I don't think we actually want the LHS to get passed in as the label. Not all labels will be valid identifiers. If you're talking about the CharField knowing what attribute it has been assigned to, I couldn't find that code in the Django source. Could you point it out?
I tracked it down https://github.com/django/django/blob/master/django/forms/forms.py BaseForm looks like a Mixin that needs the subclass to implement the collection of the base_fields class attribute. Form uses the DeclarativeFieldsMetaclass to inspect the constructed attributes and capture the appropriate ones as base_fields. I don't see how that would be improved by a special syntax for the LHS to know the identifier it's being assigned to. Could you clarify that?

On 01.06.2016 18:24, Michael Selik wrote:
I don't see how that would be improved by a special syntax for the LHS to know the identifier it's being assigned to. Could you clarify that?
I wasn't referring to labels here although somebody might find that useful too. I was referring to the issue that fields don't know neither their containing form classes nor their form instances. In some circumstances, one need to do that in order to correctly work with the field (think of the prefix of the form which needs to be reflected in the field but there are more missing pieces of information). As mentioned to Steven, in this case the name does not suffices at all. So, I am not sure how to handle this right now. This might belong to a greater issue of *referring back* which currently needs to be handled manually in all cases. Best, Sven

On Wed, Jun 1, 2016 at 3:14 PM Sven R. Kunze <srkunze@mail.de> wrote:
On 01.06.2016 18:24, Michael Selik wrote:
I don't see how that would be improved by a special syntax for the [RHS] to know the identifier it's being assigned to. Could you clarify that?
I was referring to the issue that fields don't know neither their containing form classes nor their form instances. In some circumstances, one need to do that in order to correctly work with the field (think of the prefix of the form which needs to be reflected in the field but there are more missing pieces of information).
As mentioned to Steven, in this case the name does not suffices at all. So, I am not sure how to handle this right now. This might belong to a greater issue of referring back which currently needs to be handled manually in all cases.
I always find bi-directional has-a relationships to be troublesome. If it were necessary frequently, I'd expect the Form metaclass to loop over the declared_fields and set some .form attribute to the new_class. Django is way too big for me to grok from light reading, so I won't presume to understand the choices made there. https://github.com/django/django/blob/master/django/forms/forms.py#L27

On 01.06.2016 21:48, Michael Selik wrote:
On Wed, Jun 1, 2016 at 3:14 PM Sven R. Kunze <srkunze@mail.de <mailto:srkunze@mail.de>> wrote:
On 01.06.2016 18:24, Michael Selik wrote:
I don't see how that would be improved by a special syntax for the [RHS] to know the identifier it's being assigned to. Could you clarify that?
I was referring to the issue that fields don't know neither their containing form classes nor their form instances. In some circumstances, one need to do that in order to correctly work with the field (think of the prefix of the form which needs to be reflected in the field but there are more missing pieces of information).
As mentioned to Steven, in this case the name does not suffices at all. So, I am not sure how to handle this right now. This might belong to a greater issue of referring back which currently needs to be handled manually in all cases.
I always find bi-directional has-a relationships to be troublesome. If it were necessary frequently, I'd expect the Form metaclass to loop over the declared_fields and set some .form attribute to the new_class. Django is way too big for me to grok from light reading, so I won't presume to understand the choices made there.
https://github.com/django/django/blob/master/django/forms/forms.py#L27
That would be a hand-made solution and would work quite efficiently in terms of productivity for Django projects. Recently, I started a private side project where I would need that back-relationship as well. This time, it's about rigid body simulation where I would like to pick a point in body space of a body and always get the correct world space coordinates when accessing. Same thing, but also hand-made by myself and it requires special attention from time to time. I don't say I have a nice, readable solution for this but it seems to me that *providing context* to Python objects would as much help as it helps Google to provide extremely accurate search results. Best, Sven

On 5/31/2016 7:10 PM, Greg Ewing wrote:
Steven D'Aprano wrote:
name -> Function(args)
will be expanded to:
name = Function('name', args)
This is sounding a lot like PEP 359, the "make" statement. There's also a lot of discussion on the mailing lists back then about similar constructs. The make statement was focused on blocks, but it did have similar semantics with a callable. Eric.

On 01.06.2016 01:10, Greg Ewing wrote:
Steven D'Aprano wrote:
name -> Function(args)
will be expanded to:
name = Function('name', args)
Another possibility would be to expand it to
name = Function(args) name.__name__ = "name"
Another possibility would be (requiring no syntax change): name = function(args) would always be expanded to name = function(args) name.__name__ = "name" or to name = function(args) # and function.__name__ is already available while function is called Maybe, somebody knows a reason why a called callable shouldn't know that piece of information. I can't think of one. If there's no assignment, __name__ is None. Best, Sven

On 1 June 2016 at 14:29, Sven R. Kunze <srkunze@mail.de> wrote:
Another possibility would be (requiring no syntax change):
name = function(args)
would always be expanded to
name = function(args) name.__name__ = "name"
or to
name = function(args) # and function.__name__ is already available while function is called
Maybe, somebody knows a reason why a called callable shouldn't know that piece of information. I can't think of one.
If there's no assignment, __name__ is None.
I'm not sure I follow this proposal. Could you explain a little please? Given x = Symbol('x') as the current approach, how would you modify that to work with your proposal? If you could show how you'd write a wrapper function AutoSymbol, so that x = AutoSymbol() did the same as the above, that'd help me a lot. Thanks, Paul

On 01.06.2016 16:06, Paul Moore wrote:
I'm not sure I follow this proposal. Could you explain a little please?
Given
x = Symbol('x')
as the current approach, how would you modify that to work with your proposal? If you could show how you'd write a wrapper function AutoSymbol, so that
x = AutoSymbol()
did the same as the above, that'd help me a lot.
Thanks, Paul
Okay, let's see. class Symbol: def __init__(self, name): self.name = name # that's me :) That's the current way. If now, the compiler would assign "x" to a special dunder attribute/variable, that would allow __init__ to extract that name and use it as if it were a parameter: class AutoSymbol: def __init__(self): self.name = __assigned_name__ # that's me :) That's just it. I consider __assigned_name__ the same as __module__. A special variable available if you need it. A two-step approach like the following would also be possible: class AutoSymbol: def __init__(self): pass # no name available :( def __name__(self, name): self.name = name # that's me In this case, name is not available in the constructor which could be regarded as a drawback. A advantage would be possible optimization. No __name__ member, no implicit call of __name__ On the caller site, nothing changes. It's just: x = AutoSymbol() Autosymbol either has __assigned_name__ set during constructor execution or __name__() will be set later. The same logic could be applied to other callables, functions, lambdas, etc. This post does not really care about the name of the special variable/member. It could also be '__lhs__'. I think others will come up with better names. In the end, one name needs to be chosen of course. ;-) Best, Sven

What is the value of __assigned_name__ here: x = y = z = AutoSymbol() ? Is it a list?

On Wed, Jun 01, 2016 at 11:24:35AM -0400, marky1991 . wrote:
What is the value of __assigned_name__ here:
x = y = z = AutoSymbol()
? Is it a list?
With my proposal, that would be a syntax error. However this would be allowed: x, y, z -> Symbol() # there's nothing "auto" about it and Symbol would receive a single argument, a tuple ('x', 'y', 'z'). Presumably then it could: * raise an exception, to indicate that it doesn't support creating three symbols at once; * return three different symbols, one for each of the names; * return a single symbol duplicated three times whatever makes sense for the application. But the important thing is, this would be *exactly* the same as: x, y, z = Symbol(('x', 'y', 'z')) The only magic is that you don't have to manually copy the names from the left hand side over onto the right and quote them. All Symbol knows is that it got passed a three-tuple as argument. It cannot know how it got there. -- Steve

On 01.06.2016 17:24, marky1991 . wrote:
What is the value of __assigned_name__ here:
x = y = z = AutoSymbol()
? Is it a list?
I don't think so. It's basically the first assignment that defines the __assigned_name. If you want to initialize 3 symbols at once: x, y, z = AutoSymbol(), AutoSymbol(), AutoSymbol() Best, Sven

On Wed, Jun 01, 2016 at 05:10:47PM +0200, Sven R. Kunze wrote:
class Symbol: def __init__(self, name): self.name = name # that's me :)
That's the current way. If now, the compiler would assign "x" to a special dunder attribute/variable, that would allow __init__ to extract that name and use it as if it were a parameter:
class AutoSymbol: def __init__(self): self.name = __assigned_name__ # that's me :)
I don't think that will work. Greg's proposal was to expand the syntax x -> Function(args) to x = Function(args) x.__name__ = 'x' which means that the name won't be available until after __new__ and __init__ have completed. If you want instead to use a magic local variable somehow injected automatically into the __init__ method at runtime, I think that's very clever. TOO clever, and far too magical. Automatically providing a function argument is quite easy to understand, since the argument is right there in the method definition, and it is conceptually just like "self". It also means that both of these will work will work exactly the same way: x = Function('x', args) # explicitly provide the name x -> Function(args) # use the new syntax But with your magic __assigned_name__ local variable, the signature of the function must change depending on whether it is being called with one argument or two.
That's just it. I consider __assigned_name__ the same as __module__. A special variable available if you need it.
I'm not sure what you mean by __module__. py> class A: ... def __init__(self): ... print(__module__) ... py> a = A() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in __init__ NameError: name '__module__' is not defined -- Steve

On 01.06.2016 17:29, Steven D'Aprano wrote:
If you want instead to use a magic local variable somehow injected automatically into the __init__ method at runtime, I think that's very clever. TOO clever, and far too magical.
I consider a special syntax for this narrow kind of usecase way too much as it doesn't seem very flexible and extensible in the future. Dunder methods/attributes/variables allow far more. Especially while pondering over a reply to Michael's post, there's more to consider than just the name of the defining scope. One would need the scope itself. We could add another dunder attribute/method/variable for this as well; or provide a combined dunder access to it.
Automatically providing a function argument is quite easy to understand, since the argument is right there in the method definition, and it is conceptually just like "self". It also means that both of these will work will work exactly the same way:
x = Function('x', args) # explicitly provide the name
x -> Function(args) # use the new syntax
I admit it looks looks neat but to me it's not really worth such restricting syntax as explained above.
But with your magic __assigned_name__ local variable, the signature of the function must change depending on whether it is being called with one argument or two.
I don't think so: class Symbol: def __init__(self, name=None): self.name = name or __assigned_name__
That's just it. I consider __assigned_name__ the same as __module__. A special variable available if you need it. I'm not sure what you mean by __module__.
Sorry, if that was not clear. Here you are:
class X: ... pass ... X.__module__ '__main__'
So, we already have this kind of "let me know what my defining scope looked like" dunder attributes. Best, Sven

On Wed, Jun 01, 2016 at 09:04:29PM +0200, Sven R. Kunze wrote:
I consider a special syntax for this narrow kind of usecase way too much as it doesn't seem very flexible and extensible in the future.
Dunder methods/attributes/variables allow far more.
There's nothing to extend. It's a simple problem: how to give the right hand side of an assignment access to the name of the assignment target without having to type the name again as a string. If you think that problem is too trivial to bother solving, that's a reasonable opinion to hold. But if you want to extend it to solve "far more" (whatever you mean by that), please don't hijack this thread. If you think of some other problems you would like to solve (whatever they might be), please feel free to propose a solution to them in another thread. But *this* discussion is about solving *this problem*: Some (but not all) functions need to know the name of their assignment target, so that the object they return can give itself a name that matches that target. Or to put it another way, some objects need to know their own name. The existing solution to that is simple, but inelegant and annoying: you have to manually type the name as a string and pass it as an argument to the function. If you want to solve some other problem, please do! But that's not part of this thread, and if the proposed solution to *this* problem doesn't solve *your* problem, that's irrelevant. -- Steve

2016-06-02 14:45 GMT+02:00 Steven D'Aprano <steve@pearwood.info>:
But if you want to extend it to solve "far more" (whatever you mean by that), please don't hijack this thread. If you think of some other problems you would like to solve (whatever they might be), please feel free to propose a solution to them in another thread. But *this* discussion is about solving *this problem*:
Some (but not all) functions need to know the name of their assignment target, so that the object they return can give itself a name that matches that target. Or to put it another way, some objects need to know their own name. The existing solution to that is simple, but inelegant and annoying: you have to manually type the name as a string and pass it as an argument to the function.
If you want to solve some other problem, please do! But that's not part of this thread, and if the proposed solution to *this* problem doesn't solve *your* problem, that's irrelevant.
I want to extend it to: some object also need to know module they're declared and their own qualified name (for example for pickling support). -- 闇に隠れた黒い力 弱い心を操る

On 02.06.2016 14:55, Piotr Duda wrote:
I want to extend it to: some object also need to know module they're declared and their own qualified name (for example for pickling support).
Here we go. I can also weigh in the relation between field+forms in Django. @Steven The name is simply not enough. You might think so and that's your right to do. However, it's just not that simple and it does not reflect a good technical solution. As a consequence this is relevant to the "only-the-name-please" thread. Before accusing somebody of "hijack[ing a] thread", I'd appreciate if you think why somebody contributed something first. Furthermore, your tone seems disrespectful to me. Best, Sven

On Thu, Jun 02, 2016 at 03:35:59PM +0200, Sven R. Kunze wrote:
The name is simply not enough. You might think so and that's your right to do. However, it's just not that simple and it does not reflect a good technical solution.
A good technical solution to *what*? That's the point I am making. The name alone is a perfect technical solution to the problem Guido raised in the first place.
As a consequence this is relevant to the "only-the-name-please" thread. Before accusing somebody of "hijack[ing a] thread", I'd appreciate if you think why somebody contributed something first. Furthermore, your tone seems disrespectful to me.
I am sorry, my post was not intended to be disrespectful. But I stand by it: if you wish to solve some other problem, then start a new thread. In particular, you need to state what problem you are trying to solve. It is not enough to just say "this isn't enough". -- Steve

On 03.06.2016 00:10, Steven D'Aprano wrote:
I am sorry, my post was not intended to be disrespectful. But I stand by it: if you wish to solve some other problem, then start a new thread.
The point was that solving "how to get the name" is only part of the bigger problem "how to get more context". So, putting things into perspective is important in order to avoid overfitting to a single use-case. Maybe, my intention wasn't clear but it seems that I am not the only one seeing issues with a particular solution. Sven NOTE: sys._getframe(1) is not really a to-go solution. It tastes more like an internal API which normal devs shouldn't touch except for debugging purposes. Maybe, I am wrong here, but the _underscore supports that view.

On Thu, Jun 02, 2016 at 02:55:25PM +0200, Piotr Duda wrote:
I want to extend it to: some object also need to know module they're declared and their own qualified name (for example for pickling support).
If you are serious about this, then you should start a thread about it. You should give some examples of which objects need this, and how people currently solve the problem, and why it is unsuitable. If I have understood you correctly, the usual solution to this is to use sys._getframe(1). I don't know what Jython and IronPython use. -- Steve

2016-06-03 7:15 GMT+02:00 Steven D'Aprano <steve@pearwood.info>:
On Thu, Jun 02, 2016 at 02:55:25PM +0200, Piotr Duda wrote:
I want to extend it to: some object also need to know module they're declared and their own qualified name (for example for pickling support).
If you are serious about this, then you should start a thread about it.
You should give some examples of which objects need this, and how people currently solve the problem, and why it is unsuitable.
If I have understood you correctly, the usual solution to this is to use sys._getframe(1). I don't know what Jython and IronPython use.
Examples would be any type object created by function that need pickling support, like stdlib namedtuple or Enum, both of them use _getframe hack as default, but this doesn't work in Jython or IronPython, and doesn't work if they're wrapped by another function, Enum also allow explicity specify module and qualname via keyword arguments, like: Animal = Enum('Animal', 'ant bee cat dog', module=__name__, qualname='SomeData.Animal') with new syntax: def Animal = Enum('ant bee cat dog')

On 1 June 2016 at 16:10, Sven R. Kunze <srkunze@mail.de> wrote:
That's the current way. If now, the compiler would assign "x" to a special dunder attribute/variable, that would allow __init__ to extract that name and use it as if it were a parameter:
class AutoSymbol: def __init__(self): self.name = __assigned_name__ # that's me :)
Why doesn't the assignment here set __assigned_name__ too, and override the value assigned by the "outer" assignment? Also, as someone else pointed out, in a = b = c = AutoSymbol() what would __assigned_name__ be? The advantage of a special operation is that it gives you a way of pointing out precisely *which* name assignment we want to remember... Paul

On 01.06.2016 17:42, Paul Moore wrote:
On 1 June 2016 at 16:10, Sven R. Kunze <srkunze@mail.de> wrote:
That's the current way. If now, the compiler would assign "x" to a special dunder attribute/variable, that would allow __init__ to extract that name and use it as if it were a parameter:
class AutoSymbol: def __init__(self): self.name = __assigned_name__ # that's me :)
Why doesn't the assignment here set __assigned_name__ too, and override the value assigned by the "outer" assignment?
What's point with regards to the current discussion? We need the name from the outer assignment. __assigned_name__ would solve that as it provides us with the needed piece of information. I don't see why an assignment on the same level where __assigned_name__ is used, should override this very __assigned_name__ variable. The assignment inside of __init__ might do so at a even deeper level but __assigned_name__ would stay constant *for* __init__ throughout the execution *of* __init__. To me __assigned_name__ is just a magic variable as __module__ is. It is different from module to module. __assigned_name__ is different from function execution to function execution.
Also, as someone else pointed out, in
a = b = c = AutoSymbol()
what would __assigned_name__ be?
That depends on which approach you choose. It's either dynamic and changes with each assignment or it is just set on the first assignment and never changes then. I suspect most usecases need the latter. So, that speaks for the magic variable approach (independently of how characters are used for assignment operator and how we name that variable).
The advantage of a special operation is that it gives you a way of pointing out precisely *which* name assignment we want to remember...
I don't know. I can't make a difference between = and ->. Just special characters. The one I know has a relatively clear meaning is =. So, I stick to it. Best, Sven

On 2016-06-01 06:29, Sven R. Kunze wrote:
Another possibility would be (requiring no syntax change):
name = function(args)
would always be expanded to
name = function(args) name.__name__ = "name"
I'm really against approaches like this that involve explicitly setting a special dunder attribute. What I want from this syntax is for the RHS expression to be able to use the string value of the variable name in whatever way it wants to; restricting it to setting a hard-coded dunder name is not solving the problem. What if, for instance, `function` is a factory function that defines a class at runtime, and that class has its own __name__, but the class (or `function`) wants to use the variable name for something else? -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

On 01.06.2016 20:05, Brendan Barnwell wrote:
On 2016-06-01 06:29, Sven R. Kunze wrote:
Another possibility would be (requiring no syntax change):
name = function(args)
would always be expanded to
name = function(args) name.__name__ = "name"
I'm really against approaches like this that involve explicitly setting a special dunder attribute. What I want from this syntax is for the RHS expression to be able to use the string value of the variable name in whatever way it wants to; restricting it to setting a hard-coded dunder name is not solving the problem. What if, for instance, `function` is a factory function that defines a class at runtime, and that class has its own __name__, but the class (or `function`) wants to use the variable name for something else?
Each of both approaches do have its own advantages and drawbacks. It seems, I need to repeat myself: the post you quoted explicitly said it does not care about the name of the dunder attribute. So, pick one appropriate, like "__to_be_assigned_lhs_name__", which should not clash with most usecases. Furthermore, the same argument could be used against all dunder names. Best, Sven

On Wed, Jun 01, 2016 at 11:10:19AM +1200, Greg Ewing wrote:
Steven D'Aprano wrote:
name -> Function(args)
will be expanded to:
name = Function('name', args)
Another possibility would be to expand it to
name = Function(args) name.__name__ = "name"
That priviledges one specific use of the name over all others. You'll note that I was very careful to describe the use-case as "objects that need to know their own name" but didn't specify what they did with the name. Maybe they bind it to self.__name__, but maybe they use self.name instead. Or they write it to a database. Who knows? I don't think we should specify what the object does with the name. -- Steve

On 01.06.2016 17:19, Steven D'Aprano wrote:
On Wed, Jun 01, 2016 at 11:10:19AM +1200, Greg Ewing wrote:
Steven D'Aprano wrote:
name -> Function(args)
will be expanded to:
name = Function('name', args) Another possibility would be to expand it to
name = Function(args) name.__name__ = "name" That priviledges one specific use of the name over all others. You'll note that I was very careful to describe the use-case as "objects that need to know their own name" but didn't specify what they did with the name. Maybe they bind it to self.__name__, but maybe they use self.name instead. Or they write it to a database. Who knows?
I don't think we should specify what the object does with the name.
A dunder method could help here. So, the object can decide what to do with it. Best, Sven

# Your proposal. def T as (lambda: Typevar)() def x as (lambda: sympy.Symbol)()
That's hardly fair. The two examples you quote are the ones that really *do* work with the "one-argument function" restriction:
Ah yes, you're right, I misread Alan's proposal. Typevar would work, because it is an expression that evaluates to a callable of one function. I read Alan as saying that the expression would be called.
I still have trouble with english :) My proposal is: def x as <expression-that-creates-a-callable> # single variable def x, y, z as <expression-that-creates-a-callable> # multiple variables Where <expression-that-creates-a-callable> should be evaluated and assigned to a temporary variable. E.g: def T as typing.TypeVar Where "typing.TypeVar" it's what I mean with <expression-that-creates-a-callable>. Then the statement def T as typing.TypeVar Could be semantically equivalent to _temporary = typing.TypeVar T = _temporary("T") This solves the complex use cases when you need to use more than one argument. For example, in sympy you can indicate that you want an integer symbolic variable: x = Symbol("x", integer=True) Whit my syntax you can do: SymbolicInteger = lambda name: Symbol(name, integer=True) def x as SymbolicInteger But for me, this is useless. When I use sympy, in all cases I assign more than one variable because the "Functions of Several Variables" are very common. If you go to the live interactive sympy console ( http://live.sympy.org) you will find this tree lines:
x, y, z, t = symbols('x y z t') k, m, n = symbols('k m n', integer=True) f, g, h = symbols('f g h', cls=Function)
That is the reason by which I propose the alternative whit multiple variables. Sympy have the "Symbol" class that creates one symbolic variable and the "symbols" function that creates multiple variables at once. The full proposal is: def x, y, z as <expression-that-creates-a-callable> That should be semantically equivalent to _temporary = <expression-that-creates-a-callable> x = _temporary("x") y = _temporary("y") z = _temporary("z") I think that my proposal solves more use cases, don't need new keywords and is more like english grammar.

2016-05-31 15:01 GMT+02:00 Sven R. Kunze <srkunze@mail.de>:
Anyway, what's the difference between:
a = <something>
and
def a = <something>
def a = <something> will bind name 'a' to <something>, there was proposal to make it equivalent for a = <something>.__def__('a', <module name>, <qual name>) -- 闇に隠れた黒い力 弱い心を操る

This is bikeshedding a bit, but a keyword that looks good to me: expose Typevar as T expose Symbol as X On May 31, 2016 2:06 AM, "Paul Moore" <p.f.moore@gmail.com> wrote:
On 31 May 2016 at 09:56, Stephen J. Turnbull <stephen@xemacs.org> wrote:
I know you don't think a keyword works for you, but either the recently reraised "def <name> = <type-expr>" or perhaps "type <name>: <type-expr>" make more sense to me right out of the box.
I was thinking along the lines of "name <some_keyword> callable", which I don't think works because it needs some "punctuation" to separate the name from the callable.
But "def name = callable" (or some other preceding keyword combined with =) might work. I don't like "type" though, as the point here (I thought - see below) is to come up with a construct useful for more than just types.
I'm +1 for stopping the bikeshedding until we've all got a lot of stubfile reading under our belts.
If this was simply about type definitions, I'd agree. But I thought the point of Guido's post was that having seen two examples (TypeVar and Symbol) is there a more general approach that might cover these two cases as well as others? So just looking at the problem in terms of stub files isn't really the point here.
Paul _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

On Tue, May 31, 2016 at 08:27:28AM -0700, David Mertz wrote:
This is bikeshedding a bit, but a keyword that looks good to me:
expose Typevar as T expose Symbol as X
"expose" sounds like it is taking something hidden or private and exposing it to the public. It doesn't give any hint that it takes the name T and uses it as an argument to Typevar. I would take it as meaning: take the (possibly internal or private) thing Typevar, and bind it to the name T with no hint that Typevar was being called. -- Steve

Bracketing for a moment what keyword might be used, a thing I like about this syntax is that it can work for multiple names naturally. E.g. expose Symbol as x, y, z Or with a different word: define Typevar as T, S, R For things like namedtuple that need more arguments, you'd need to use functools.partial to create the single argument callable to match the syntax. On May 31, 2016 8:44 AM, "Steven D'Aprano" <steve@pearwood.info> wrote:
On Tue, May 31, 2016 at 08:27:28AM -0700, David Mertz wrote:
This is bikeshedding a bit, but a keyword that looks good to me:
expose Typevar as T expose Symbol as X
"expose" sounds like it is taking something hidden or private and exposing it to the public. It doesn't give any hint that it takes the name T and uses it as an argument to Typevar. I would take it as meaning:
take the (possibly internal or private) thing Typevar, and bind it to the name T
with no hint that Typevar was being called.
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

David Mertz wrote:
define Typevar as T, S, R
I quite like this, but it should be the other way around: define T, S, R as TypeVar Yes, I *know* the name being bound comes after 'as' in other places, but I think consistency with the obvious English meaning is more important. One other thing, 'define' is so similar to 'def' that I think it would be needlessly confusing to have both, so just make it def T, S, R as TypeVar Also, yes, you would have to curry if you want the constructor to have arguments. I think that's a small price to pay for the benefits: less magic, no restrictions on the form of the constructor expression, and the ability to re-use it for multiple bound names. -- Greg

I agree, I saw this after I posted. If the keyword is 'define' the names should definitely come before 'as'. If the keyword is 'expose' as I first thought of, the names should come after 'as'. Other possible words with names after 'as': reveal, publish, reify, create, etc. If some word is intuitive enough, I'd prefer this order. It's closer to what 'import' and 'with' do in conjunction with 'as'. On May 31, 2016 4:58 PM, "Greg Ewing" <greg.ewing@canterbury.ac.nz> wrote:
David Mertz wrote:
define Typevar as T, S, R
I quite like this, but it should be the other way around:
define T, S, R as TypeVar
Yes, I *know* the name being bound comes after 'as' in other places, but I think consistency with the obvious English meaning is more important.
One other thing, 'define' is so similar to 'def' that I think it would be needlessly confusing to have both, so just make it
def T, S, R as TypeVar
Also, yes, you would have to curry if you want the constructor to have arguments. I think that's a small price to pay for the benefits: less magic, no restrictions on the form of the constructor expression, and the ability to re-use it for multiple bound names.
-- Greg _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

On Tue, May 31, 2016 at 10:35:01AM -0700, David Mertz wrote:
For things like namedtuple that need more arguments, you'd need to use functools.partial to create the single argument callable to match the syntax.
As far as I am concerned, limiting it to single-argument functions cripples this proposal to the point that it is of no interest. So far I've seen exactly four real-world use-cases, plus potentially Django. Only supporting a single name makes this useless for two of those use-cases, and I predict all of the Django examples[1]. Using partial is *more work than the status quo* which means nobody in their right mind is going to use it. # (1) status quo Record = namedtuple("Record", fields) # (2a) my suggestion Record -> namedtuple(fields) # (2b) or if you insist def Record = namedtuple(fields) # (3) if it only supports a single argument from functools import partial Record -> partial(namedtuple, field_names=fields)() That is significantly more annoying than the status quo. But it gets worse! Remember that partial() binds positional arguments from the left, which is the wrong side for what we need. Fortunately namedtuple supports keyword arguments, but what about those that don't? For the sake of the exercise, let's pretend that namedtuple doesn't support keyword arguments, just to get an idea of how to solve it: Record -> partial(lambda fields, name: namedtuple(name, fields), fields)() And let's just hope nobody wants to call namedtuple with the other arguments, verbose or rename. It is easy to wave your hands and say "just use partial", but not so easy to *actually do so*. [1] On the basis that very few objects need to know *only* their name. I expect that if Django uses this pattern, it will take a name plus at least one more argument. -- Steve

On 1 Jun 2016, at 17:59, Steven D'Aprano <steve@pearwood.info> wrote:
On Tue, May 31, 2016 at 10:35:01AM -0700, David Mertz wrote:
For things like namedtuple that need more arguments, you'd need to use functools.partial to create the single argument callable to match the syntax.
As far as I am concerned, limiting it to single-argument functions cripples this proposal to the point that it is of no interest.
So far I've seen exactly four real-world use-cases, plus potentially Django. Only supporting a single name makes this useless for two of those use-cases, and I predict all of the Django examples[1]. Using partial is *more work than the status quo* which means nobody in their right mind is going to use it.
# (1) status quo Record = namedtuple("Record", fields)
# (2a) my suggestion Record -> namedtuple(fields)
# (2b) or if you insist def Record = namedtuple(fields)
# (3) if it only supports a single argument from functools import partial Record -> partial(namedtuple, field_names=fields)()
That is significantly more annoying than the status quo. But it gets worse! Remember that partial() binds positional arguments from the left, which is the wrong side for what we need. Fortunately namedtuple supports keyword arguments, but what about those that don't?
For the sake of the exercise, let's pretend that namedtuple doesn't support keyword arguments, just to get an idea of how to solve it:
Record -> partial(lambda fields, name: namedtuple(name, fields), fields)()
And let's just hope nobody wants to call namedtuple with the other arguments, verbose or rename.
It is easy to wave your hands and say "just use partial", but not so easy to *actually do so*.
I'm working under the assumption that "eventually" the functions get reworked so as to return a 1-argument function. So (using the -> syntax) x -> Symbol Point -> namedtuple_wrapper(['x', 'y']) Becomes x = Symbol('x') Point = namedtuple_wrapper(['x', 'y'])('Point') Adding the name as a first argument looks even more hacky to me, to be honest. As for x, y, z -> expr Should raise a syntax-error for all I care. Unless everybody agrees that it should call the naming creator 3 times? I would find it confusing, to be honest. So it would be identifier -> expression Not identifier_list -> expression

The fundamental use-case here is "any object that needs to know its own name". Most objects need more than just a name.
A more general solution could be: def x, y, z as <expression> Where <expression> should return a callable that accept only one argument. So, you can do some like: def Type(bases, namespace): def get_name(name): return type(name, bases, namespace) return get_name def x, y, z as Type(bases, namespace) That is a syntax sugar of: dummy = Type(bases, namespace) x = dummy("x") y = dummy("y") z = dummy("z") 2016-05-31 12:27 GMT-03:00 David Mertz <mertz@gnosis.cx>:
This is bikeshedding a bit, but a keyword that looks good to me:
expose Typevar as T expose Symbol as X On May 31, 2016 2:06 AM, "Paul Moore" <p.f.moore@gmail.com> wrote:
On 31 May 2016 at 09:56, Stephen J. Turnbull <stephen@xemacs.org> wrote:
I know you don't think a keyword works for you, but either the recently reraised "def <name> = <type-expr>" or perhaps "type <name>: <type-expr>" make more sense to me right out of the box.
I was thinking along the lines of "name <some_keyword> callable", which I don't think works because it needs some "punctuation" to separate the name from the callable.
But "def name = callable" (or some other preceding keyword combined with =) might work. I don't like "type" though, as the point here (I thought - see below) is to come up with a construct useful for more than just types.
I'm +1 for stopping the bikeshedding until we've all got a lot of stubfile reading under our belts.
If this was simply about type definitions, I'd agree. But I thought the point of Guido's post was that having seen two examples (TypeVar and Symbol) is there a more general approach that might cover these two cases as well as others? So just looking at the problem in terms of stub files isn't really the point here.
Paul _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

On Tue, May 31, 2016 at 01:09:05PM -0300, Alan Cristhian wrote:
The fundamental use-case here is "any object that needs to know its own name". Most objects need more than just a name.
A more general solution could be:
def x, y, z as <expression>
Where <expression> should return a callable that accept only one argument.
You should consider how your suggestion will work with the existing use-cases already shown. We've got two dead-simple use-cases to consider. If this proposal makes those use-cases harder than the status quo, then this proposal is dead in the water. Nobody is going to use it. # Status quo. T = Typevar('T') x = sympy.Symbol('x') # My proposal. T -> Typevar() x -> sympy.Symbol() # Your proposal. def T as (lambda: Typevar)() def x as (lambda: sympy.Symbol)() You specify that the expression on the right must return a function that takes one variable, so you cannot use Typevar or Symbol directly. You have to call a function that returns the function you actually want. Syntactic sugar is supposed to make things easier, not harder. -- Steve

On 1 June 2016 at 17:14, Steven D'Aprano <steve@pearwood.info> wrote:
You should consider how your suggestion will work with the existing use-cases already shown.
We've got two dead-simple use-cases to consider. If this proposal makes those use-cases harder than the status quo, then this proposal is dead in the water. Nobody is going to use it.
There's almost no room for improvement in those two use cases, and yet people still like the idea of "not repeating the name". I think (but don't know for certain) that there are some users who would accept a cost of a few extra characters to avoid the duplication.
# Status quo. T = Typevar('T') x = sympy.Symbol('x')
# My proposal. T -> Typevar() x -> sympy.Symbol()
# Your proposal. def T as (lambda: Typevar)() def x as (lambda: sympy.Symbol)()
That's hardly fair. The two examples you quote are the ones that really *do* work with the "one-argument function" restriction: def T as Typevar def x as sympy.Symbol If you want to knock down the alternative proposal, you should use namedtuple or type. But for those, the people arguing for them are assuming (for better or worse) that additional helpers with a signature compatible with the new syntax would be added to the stdlib at the same time as the new syntax.
You specify that the expression on the right must return a function that takes one variable, so you cannot use Typevar or Symbol directly. You have to call a function that returns the function you actually want.
Syntactic sugar is supposed to make things easier, not harder.
The biggest problem I have with your proposal is how it would extend to more complex cases (or probably more realistically, how the language definition would disallow cases that can't be handled). Can you give the actual syntax of your proposal, in the form used in the Python language reference? The best I can come up with is IDENTIFIER '->' CALL but CALL (see https://docs.python.org/3/reference/expressions.html#grammar-token-call) allows the form function(x for x in something) and you can't inject a name at the front of that. So I guess you'd have to split CALL into two parts, and only allow one of them here? Paul

On Wed, Jun 01, 2016 at 05:33:19PM +0100, Paul Moore wrote:
# Your proposal. def T as (lambda: Typevar)() def x as (lambda: sympy.Symbol)()
That's hardly fair. The two examples you quote are the ones that really *do* work with the "one-argument function" restriction:
Ah yes, you're right, I misread Alan's proposal. Typevar would work, because it is an expression that evaluates to a callable of one function. I read Alan as saying that the expression would be called. -- Steve

On Wed, Jun 1, 2016 at 12:33 PM Paul Moore <p.f.moore@gmail.com> wrote:
On 1 June 2016 at 17:14, Steven D'Aprano <steve@pearwood.info> wrote:
We've got two dead-simple use-cases to consider.
There's almost no room for improvement in those two use cases, and yet people still like the idea of "not repeating the name".
And some people don't!
T = Typevar('T')
x = sympy.Symbol('x')
In fact, I often use namedtuple with a dynamic __name__ and fields, without changing the identifier. Record = namedtuple(name, fields) If I use that in half my code, but then in the other half, something like Record => namedtuple(fields) That's TIMTOWTDI. I'd rather standardize. Besides, the easy stuff is already easy. Let's focus on what's *hard* in Python. I haven't seen an explanation of how this proposal would simplify Django.

On 1 June 2016 at 17:54, Michael Selik <michael.selik@gmail.com> wrote:
Besides, the easy stuff is already easy. Let's focus on what's *hard* in Python. I haven't seen an explanation of how this proposal would simplify Django.
Django's been mentioned, but nobody has explained (or shown) the code that needs improving and would benefit from a proposal like this. Paul

On Wed, Jun 01, 2016 at 04:54:22PM +0000, Michael Selik wrote:
On Wed, Jun 1, 2016 at 12:33 PM Paul Moore <p.f.moore@gmail.com> wrote:
There's almost no room for improvement in those two use cases, and yet people still like the idea of "not repeating the name".
And some people don't! [...] In fact, I often use namedtuple with a dynamic __name__ and fields, without changing the identifier.
Record = namedtuple(name, fields)
If I use that in half my code, but then in the other half, something like
Record => namedtuple(fields)
That's TIMTOWTDI. I'd rather standardize.
That's fine. Nobody is going to force you to change. I'm not proposing any changes to namedtuple at all -- its others who want to change it to take just a single argument and have an wrapper function to specify the field names. I'm against that rather strongly. [...]
I haven't seen an explanation of how this proposal would simplify Django.
I fear I may have been mislead by Sven, who made comments about this being useful for Django, but hasn't given any examples yet. -- Steve

Steven D'Aprano wrote:
I'm not proposing any changes to namedtuple at all -- its others who want to change it to take just a single argument and have an wrapper function to specify the field names. I'm against that rather strongly.
I don't think this would be creating TMWTDI. Given the proposed feature, and a suitable helper, then def Foo as NamedTuple('x', 'y', 'z') would become the *obvious* way to define a named tuple type. The old way foo = namedtuple(something, 'x', 'y', 'z') would become something low-level that you only use in special circumstances -- like calling type() directly to create a class. -- Greg

I have a completely different idea here. Forget clunky syntax, overloading of 'def' and 'as', and all that. Introduce the magic parameter '$'. To wit: def func(*args, **kwargs, $): # positioning of the $ is discussed below ... # function code here $ would be a special-cased parameter that does not receive an explicit argument, and is thus ignored when calling the function (i.e. the function above would be called simply as 'func(*args, **kwargs)'). Instead, the $ parameter receives as its value a string representing the name that the function's return value will be bound to, or None if the function is called as a statement (where the return value is simply thrown away). Now any function (e.g. namedtuple) requiring the name can simply toss in a $ parameter, and it automatically receives the appropriate name through compiler magic. So assuming that namedtuple is patched to take a $ parameter instead of an explicit argument with the name, this common code today: Record = namedtuple('Record', ['name', 'address', 'phone', 'age']) becomes simply this: Record = namedtuple(['name', 'address', 'phone', 'age']) Some open questions here are where to require the $ to be placed in the argument list, and whether and how an advanced user can override it if needed. For the former, I propose to require it to be the last argument, after **kwargs. I considered making it the first argument, but that would create a competition for first argument in a class method or instance method (particularly __init__). Making it last also enables it to be overridden as a keyword-only argument. If the user really wanted to, they could change the above namedtuple call to: Record = namedtuple('name, address, phone, age', $='BusinessCard') and $ would receive the explicit argument provided without compiler magic being applied. Another concern is backward compatibility for namedtuple and other functions during the transition to the $ parameter. The easy solution would seem to be to just keep the explicit parameter and deprecate/ignore it, but there is then no way to transition to eliminating it completely since new code will have to continue providing a useless argument (in the common case that it is the first parameter), or else the function will have to give default values to every argument so that the other arguments can be passed as keyword arguments (which is not always desirable or practical), and sufficient time given for all users to switch to keywords, before the old first argument can be deleted in a subsequent release. Instead, I suggest that the recommended method of transition ought to be to simply provide new functions and deprecate the existing functions (except where they are already built to handle omitting the explicit parameter in the call, if such cases exist) in a minor release, and remove the deprecated functions in a subsequent major release. Thoughts? (I'm sunburned after spending six hours in a waterpark today on four hours of sleep last night, so if I sound crazy, that's why.)

2016-06-02 1:50 GMT+02:00 Jonathan Goble <jcgoble3@gmail.com>:
I have a completely different idea here. Forget clunky syntax, overloading of 'def' and 'as', and all that. Introduce the magic parameter '$'. To wit:
def func(*args, **kwargs, $): # positioning of the $ is discussed below ... # function code here
Why not a method similar to what is done with *args and **kwargs (I chose pretty arbitrarely /namearg) so for definition it would be def func(*args, **kwargs, /namearg): and for override it would be Record = namedtuple('name, address, phone, age', /'BusinessCard')

On 2016-06-01 16:50, Jonathan Goble wrote:
I have a completely different idea here. Forget clunky syntax, overloading of 'def' and 'as', and all that. Introduce the magic parameter '$'. To wit:
def func(*args, **kwargs, $): # positioning of the $ is discussed below ... # function code here
$ would be a special-cased parameter that does not receive an explicit argument, and is thus ignored when calling the function (i.e. the function above would be called simply as 'func(*args, **kwargs)'). Instead, the $ parameter receives as its value a string representing the name that the function's return value will be bound to, or None if the function is called as a statement (where the return value is simply thrown away).
I think that's way too general and dangerous. I don't want arbitrary objects to be able to do arbitrary things based on what name I'm assigning them to. I think the fact that Python doesn't allow overriding simple assignment, although it makes some things difficult, makes it simpler to reason about in many cases. If we do add something to handle this case, I'd rather it be something explicit, so you know when you're crossing the barrier between identifiers and string values. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

On Thu, Jun 02, 2016 at 09:01:03AM +1200, Greg Ewing wrote:
Steven D'Aprano wrote:
I'm not proposing any changes to namedtuple at all -- its others who want to change it to take just a single argument and have an wrapper function to specify the field names. I'm against that rather strongly.
I don't think this would be creating TMWTDI.
I'm not objecting to it because it creates more than one way to do it. I'm objecting to it because it needlessly doubles the number of callables needed. Instead of there being one namedtuple function, you need two: a single parameter version, and a wrapper that performs whatever magic is needed to crowbar the multiple parameter version into the constraint of a single parameter version. For example, we have int(), which takes either one or two arguments: int("23") => returns 23 int("23", 16) => returns 35 We don't have int, and int_wrapper: # Python doesn't do this. int_wrapper(16)("23") => returns 35
Given the proposed feature, and a suitable helper, then
def Foo as NamedTuple('x', 'y', 'z')
would become the *obvious* way to define a named tuple type.
I'm not really sure that the difference between namedtuple and NamedTuple is obvious, and I'm confident that the subtle difference between them will be a bug-magnet. But maybe we could solve that somehow. In any case, I agree that the new syntax would become the One Obvious Way. There's no real semantic difference between: def Record as namedtuple('x y z') Record -> namedtuple('x y z') and while I'm not a big fan of the def ... as syntax, I could live with it. One thing I dislike is that it reverses the usual sense of "as": import module as foo # module is bound to name "foo" with cm() as foo # context manager is bound to name "foo" try:... except E as foo # exception is bound to name "foo" All other uses have "as foo" set the name. But you have: def foo as thing_that_is_called which backwards is, as speaks Yoda. But there is a rather big semantic difference once you insist that the thing that is called must only accept a single argument. -- Steve

On 3 June 2016 at 07:05, Steven D'Aprano <steve@pearwood.info> wrote:
I'm objecting to it because it needlessly doubles the number of callables needed. Instead of there being one namedtuple function, you need two: a single parameter version, and a wrapper that performs whatever magic is needed to crowbar the multiple parameter version into the constraint of a single parameter version.
Thanks for clarifying your objection, I hadn't really understood what you had an issue with until now. OK, so the question is between needing extra callables, vs a somewhat "magical" process for injecting an argument. We still differ on which is the more acceptable answer, but I'm happy to offer both up for consideration, now that the differences are clear. Paul

2016-06-03 9:45 GMT+02:00 Paul Moore <p.f.moore@gmail.com>:
On 3 June 2016 at 07:05, Steven D'Aprano <steve@pearwood.info> wrote:
I'm objecting to it because it needlessly doubles the number of callables needed. Instead of there being one namedtuple function, you need two: a single parameter version, and a wrapper that performs whatever magic is needed to crowbar the multiple parameter version into the constraint of a single parameter version.
Thanks for clarifying your objection, I hadn't really understood what you had an issue with until now.
OK, so the question is between needing extra callables, vs a somewhat "magical" process for injecting an argument.
We still differ on which is the more acceptable answer, but I'm happy to offer both up for consideration, now that the differences are clear.
There is also third option, return normal object that have special dunder method (ex. __def__), which allow set name (and maybe module and qualname) for object.

On 3 June 2016 at 08:55, Piotr Duda <duda.piotr@gmail.com> wrote:
2016-06-03 9:45 GMT+02:00 Paul Moore <p.f.moore@gmail.com>:
On 3 June 2016 at 07:05, Steven D'Aprano <steve@pearwood.info> wrote:
I'm objecting to it because it needlessly doubles the number of callables needed. Instead of there being one namedtuple function, you need two: a single parameter version, and a wrapper that performs whatever magic is needed to crowbar the multiple parameter version into the constraint of a single parameter version.
Thanks for clarifying your objection, I hadn't really understood what you had an issue with until now.
OK, so the question is between needing extra callables, vs a somewhat "magical" process for injecting an argument.
We still differ on which is the more acceptable answer, but I'm happy to offer both up for consideration, now that the differences are clear.
There is also third option, return normal object that have special dunder method (ex. __def__), which allow set name (and maybe module and qualname) for object.
Sorry, yes, there may well be other options as well. I haven't been following all of the proposals in this thread. Paul

On 3 Jun 2016, at 09:55, Piotr Duda <duda.piotr@gmail.com> wrote:
2016-06-03 9:45 GMT+02:00 Paul Moore <p.f.moore@gmail.com>:
On 3 June 2016 at 07:05, Steven D'Aprano <steve@pearwood.info> wrote: I'm objecting to it because it needlessly doubles the number of callables needed. Instead of there being one namedtuple function, you need two: a single parameter version, and a wrapper that performs whatever magic is needed to crowbar the multiple parameter version into the constraint of a single parameter version.
Thanks for clarifying your objection, I hadn't really understood what you had an issue with until now.
OK, so the question is between needing extra callables, vs a somewhat "magical" process for injecting an argument.
We still differ on which is the more acceptable answer, but I'm happy to offer both up for consideration, now that the differences are clear.
There is also third option, return normal object that have special dunder method (ex. __def__), which allow set name (and maybe module and qualname) for object.
With the added benefit that in Python 3.something you can already emulate this for construction during class construction: in the __prepare__ of the metaclass, return a dictionary wrapper which does that. Of course, that's also what Django does now already (in a Python 2 compatible way) from metaclass.__new__.

On Thu, Jun 02, 2016 at 02:14:31AM +1000, Steven D'Aprano wrote:
# Your proposal. def T as (lambda: Typevar)() def x as (lambda: sympy.Symbol)()
This is wrong, I misunderstood Alan. I wrongly interpreted him as saying that the expression on the right would be *called* and must return a one-argument function, but that's not what he said. Apologies for the confusion. -- Steve

On 1 Jun 2016, at 18:14, Steven D'Aprano <steve@pearwood.info> wrote:
On Tue, May 31, 2016 at 01:09:05PM -0300, Alan Cristhian wrote:
The fundamental use-case here is "any object that needs to know its own name". Most objects need more than just a name.
A more general solution could be:
def x, y, z as <expression>
Where <expression> should return a callable that accept only one argument.
You should consider how your suggestion will work with the existing use-cases already shown.
We've got two dead-simple use-cases to consider. If this proposal makes those use-cases harder than the status quo, then this proposal is dead in the water. Nobody is going to use it.
# Status quo. T = Typevar('T') x = sympy.Symbol('x')
# My proposal. T -> Typevar() x -> sympy.Symbol()
# Your proposal. def T as (lambda: Typevar)() def x as (lambda: sympy.Symbol)()
Correct me if I'm wrong, but wouldn't it be def T as Typevar Unless you're being (rightfully) pedantic about "must return" implying that the expression will be called as a 0-argument function and that result called with the name?
You specify that the expression on the right must return a function that takes one variable, so you cannot use Typevar or Symbol directly. You have to call a function that returns the function you actually want.
Syntactic sugar is supposed to make things easier, not harder.
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

Steven D'Aprano wrote:
# Your proposal. def T as (lambda: Typevar)() def x as (lambda: sympy.Symbol)()
No, you would write those as def T as TypeVar def x as sympy.Symbol
Syntactic sugar is supposed to make things easier, not harder.
Which it does, because you have less parentheses to type! -- Greg

On Tue, May 31, 2016 at 05:56:11PM +0900, Stephen J. Turnbull wrote:
Paul Moore writes:
On 31 May 2016 at 04:08, Steven D'Aprano <steve@pearwood.info> wrote:
How do you feel about an arrow operator?
T -> TypeVar() x -> Symbol() T -> type(bases, ns) Record -> namedtuple(fields)
I like this.
Well, it's cuter than Hello Kitty, what's not to like? But ...
It give me no clue what it's supposed to mean.
That's harsh. No clue *at all*? It shouldn't be so difficult to see an arrow as assignment. Even if you can't *guess* what it means, at least it should be easy enough to understand and remember once explained. The arrow operator represents a value "moving into" a name, or a name becoming a value: x <- 999 # move 999 into the slot called "x" x -> 999 # x becomes 999 As you yourself pointed out, R uses <- as the assignment operator (as do OCaml, F#, but not Pascal, as it uses := instead). I've seen plenty of pseudo-code using <- as the assignment operator, and *lots* of whiteboard discussions using arrows in either direction for assignment. I've never had somebody claim to be perplexed or confused by what I mean if I write `foo -> value`. So I think there are plenty of clues, even if the arrow points the wrong way for R users. There's another analogy here that will help: x -> spam() # copy 'x' to the RHS I don't expect anyone to intuit what -> does, but I think that it will be easy to learn and memorable -- more so than @ for decorators, ^ for bitwise xor, or {} for dicts. Of course Python cannot use <- because it's ambiguous with (less than) (unary minus), e.g.: `x<-f()` could mean either `x <- f()` or `x < -f()`.
In math the forward arrow is often used to name a function type (including the domain preceding the arrow and the codomain following it in the name), although what the parentheses and their contents mean, I don't know. The codomain type is the value of TypeVar()? What's that?
I don't think that the average Python programmer is going to be thinking of domains and codomains when they see an arrow :-) Do you think that was a problem for C++ ? But if it makes you happy, perhaps => instead :-)
In Pascal and R, the reverse arrow
T <- TypeVar()
is used to mean assignment (which you could read as "T receives TypeVar()", but the implicit argument on the RHS is a double-take for me in both syntaxes -- the argument to TypeVar is not optional!
And neither is the `self` argument to methods. But we learn that self is implicitly provided as if by magic :-) Another similar example of magic is decorator syntax: def decorator(func): ... which again is not optional and gets provided implicitly by the interpreter. The thing is, regardless of whether we use a symbol or a keyword, the functionality here is going to be magical. That's its point: it is to avoid the need to write the name twice.
IIRC, we have two syntaxes in Python itself that take the name of an identifier and reify it as a string (ie, inject it into a namespace): def and class.
Plus: - import spam - from module import spam - for spam in ... - @decorator - and of course regular assignment. So there's a nice mix of keywords and symbols.
I know you don't think a keyword works for you, but either the recently reraised "def <name> = <type-expr>" or perhaps "type <name>: <type-expr>" make more sense to me right out of the box.
Don't think of the RHS as necessarily a type expression. Guido has already given one other example: x = sympy.Symbol('x') This is a proposal to solve the general problem of passing a name from the left hand side to the right just prior to doing an assignment, regardless of what the purpose of that name is. So "type" is right out: the motivating use-case might be TypeVar, but this isn't limited to types by any means. I could probably learn to live with def x = sympy.Symbol() if I really needed to, put it looks strange to use def for defining something which isn't necessarily a function. And it's a tad wordy: four extra keypresses, in order to avoid three. But most critically, "def x =" doesn't really suggest copying the "x" onto the RHS in any way.
Another problem is that none of these syntaxes (including the keyword- based ones) provides a clue as to whether the type is a distinct type or a type alias.
The type will be whatever TypeVar returns. That's no different from the status quo `T = TypeVar('T')`. -- Steve

Steven D'Aprano writes:
It give me no clue what it's supposed to mean.
That's harsh. No clue *at all*?
What harsh? Frank, yes. As for the question, of course not "*at all*", obviously it's an operator that does something. But what and to which, no clue, except from the context of what you're trying to accomplish -- not from the notation itself. The variety of examples and hints you give just makes it worse.
And neither is the `self` argument to methods. But we learn that self is implicitly provided as if by magic :-)
No, it's explicitly provided by the attribute reference syntax that binds a positional parameter when the attribute is a method. Binding positional parameters is a concept familiar from all programming languages I know of (except Haskell, which has only one parameter so "position" is a vacuous concept). I will remind you that ".attribute" syntax to avoid typing self in method definitions and similar anaphoric syntaxes for avoiding typing the name of a object whose attributes are repeatedly accessed have been repeatedly vetoed.
Another similar example of magic is decorator syntax:
I can't explain why that doesn't bother me, but it doesn't. It's also much more powerful. Not the syntax itself, but what decorators can do.
The thing is, regardless of whether we use a symbol or a keyword, the functionality here is going to be magical. That's its point: it is to avoid the need to write the name twice.
Yes, I could learn this magic. No, I find the syntaxes proposed so far unintuitive, by which I mean quite arbitrary and lacking connotations that lead to the intended meaning. So, not good enough. -1 on forward arrow syntax: as you point out, mention of a variable injects that name into the appropriate namespace, and there's important. Extremely concise notation for variable evaluation is incredibly important, and having that string in locals() or globals() is necessary for introspection. Thus variable *mention* does the injection. But the other syntaxes that inject names of identifiers into namespaces are all statements. -0.5 on keywords: this doesn't clear the bar for keyword introduction yet AFAICS. Not even if it's only used in stubfiles.

On 31 May 2016 at 19:58, Stephen J. Turnbull <stephen@xemacs.org> wrote:
-0.5 on keywords: this doesn't clear the bar for keyword introduction yet AFAICS. Not even if it's only used in stubfiles.
If anything that comes from this is only for stubfiles, I'm -1 on it. To be worth implementing, any new feature here would have to have a much wider applicability than one small area to get my vote. Paul

On 2016-05-31 09:56, Stephen J. Turnbull wrote: [snip]
It give me no clue what it's supposed to mean. In math the forward arrow is often used to name a function type (including the domain preceding the arrow and the codomain following it in the name), although what the parentheses and their contents mean, I don't know. The codomain type is the value of TypeVar()? What's that? In Pascal and R, the reverse arrow
T <- TypeVar()
is used to mean assignment (which you could read as "T receives TypeVar()", but the implicit argument on the RHS is a double-take for me in both syntaxes -- the argument to TypeVar is not optional!
FYI, in Pascal the assignment operator is ":=". The assignment operator in APL is "←".

On 31 May 2016 at 04:08, Steven D'Aprano <steve@pearwood.info> wrote:
T -> TypeVar() x -> Symbol() T -> type(bases, ns) Record -> namedtuple(fields)
The arrow seems back to front. It should point towards the name being assigned. T <- TypeVar() x <- Symbol() T <- type(bases, ns) Record <- namedtuple(fields) Also, if the RHS is going to be called as part of the <- operation, shouldn't the first two just be T <- TypeVar x <- Symbol The main drawback is that '<-' doesn't suggest in any way what's going on. An alternative might be def x = Symbol since 'def' has the precedent of attaching the name being bound to the object being created. -- Greg

On Wed, Jun 01, 2016 at 01:02:57AM +1200, Greg Ewing wrote:
On 31 May 2016 at 04:08, Steven D'Aprano <steve@pearwood.info> wrote:
T -> TypeVar() x -> Symbol() T -> type(bases, ns) Record -> namedtuple(fields)
The arrow seems back to front. It should point towards the name being assigned.
See my response to Stephen, but in a nutshell, no, we can't use <- because that's already legal for (less than) (unary minus). Besides, the concepts I'm hinting at with -> are: (1) "x becomes the value of the right hand side" (2) "copy the name 'x' over to the right hand side"
T <- TypeVar() x <- Symbol() T <- type(bases, ns) Record <- namedtuple(fields)
Also, if the RHS is going to be called as part of the <- operation, shouldn't the first two just be
T <- TypeVar x <- Symbol
No, because in the general case they might require more than one argument. Besides, I want to make it clear that the right hand side is being called. And I don't want to make the same mistake as decorator syntax. I'm not saying that decorator syntax is bad (it's been really successful!) but the choice to leave off the parens does make things a bit more complicated in some cases. E.g. if your decorator wants to take extra arguments, you have to write a decorator factory: def factory(args): process(args) def decorator(func): @wraps(func) def inner(*args, **kwargs): ... return inner return decorator @factory(args) def spam(): ... That's not awful, but it does lead to a trap: what if you make the args optional? Then you can leave them out when calling the factory: @factory() def spam(): ... but in practice that tends to be a bug magnet, because people invariably forget the parens. And *that* leads to spam being bound to the decorator itself, which is wrong, but you don't find out until you actually call spam. I don't think this suggested syntax will be used as often as decorators, so I don't think it's worth the risk of complicating matters by making the parens optional. Keep them explicit and then you never need to worry about whether they are needed or not, they're always needed.
The main drawback is that '<-' doesn't suggest in any way what's going on.
Well, apart from the languages and pseudo-code that already uses <- as an assignment operator :-)
An alternative might be
def x = Symbol
since 'def' has the precedent of attaching the name being bound to the object being created.
I never thought of it like that before. To me, I don't think def is a good match because def doesn't have a left and right hand side. Assignment does. I know that *technically* def is an assignment (a name binding) but it doesn't look like one. It looks like a definition or declaration. -- Steve

Steven D'Aprano wrote:
I know that *technically* def is an assignment (a name binding) but it doesn't look like one. It looks like a definition or declaration.
Well, the use cases we've been considering are effectivly definitions, they just don't look like it because we don't have a syntax that generalises the definitioniness of 'def' and 'class'.
To me, I don't think def is a good match because def doesn't have a left and right hand side. Assignment does.
Maybe that means assignment isn't a good model for the new syntax? Maybe it should be more like: def Symbol x If you want to give it args, you do def Symbol x(args) The idea is that this is modelled after class name(args): with the keyword 'class' replaced by another keyword together with the type of thing you're defining. -- Greg

On 01/06/16 00:31, Greg Ewing wrote:
Steven D'Aprano wrote:
I know that *technically* def is an assignment (a name binding) but it doesn't look like one. It looks like a definition or declaration.
Well, the use cases we've been considering are effectivly definitions, they just don't look like it because we don't have a syntax that generalises the definitioniness of 'def' and 'class'.
To me, I don't think def is a good match because def doesn't have a left and right hand side. Assignment does.
Maybe that means assignment isn't a good model for the new syntax?
Maybe it should be more like:
def Symbol x
If you want to give it args, you do
def Symbol x(args)
The idea is that this is modelled after
class name(args):
with the keyword 'class' replaced by another keyword together with the type of thing you're defining.
This is the first syntax I've seen in this thread that seems reasonably intuitive and pythonic. I don't mind particularly the keyword used (def or something else), but this structure draws the right parallels in my mind. Regards, Ian F

On 01.06.2016 01:46, Ian Foote wrote:
This is the first syntax I've seen in this thread that seems reasonably intuitive and pythonic. I don't mind particularly the keyword used (def or something else), but this structure draws the right parallels in my mind.
Still nobody explained why it needs a special syntax at all. Normal assignment could do it as well as long as the assignment tells the RHS under what name it will be referred to. Best, Sven

On 01.06.2016 15:19, Sven R. Kunze wrote:
On 01.06.2016 01:46, Ian Foote wrote:
This is the first syntax I've seen in this thread that seems reasonably intuitive and pythonic. I don't mind particularly the keyword used (def or something else), but this structure draws the right parallels in my mind.
Still nobody explained why it needs a special syntax at all.
Normal assignment could do it as well as long as the assignment tells the RHS under what name it will be referred to.
Right. Essentially, we'd only need a way to tell the compiler to invoke e.g. a method on the RHS object which gets called with the name of the variable it binds (and perhaps the line number to allow for e.g. recording the order of definitions). The current byte code for: x = obj reads like this: 2 0 LOAD_GLOBAL 0 (obj) 3 STORE_FAST 0 (x) so if we could get the compiler to generate a method call right after the assignment, e.g. obj.recordbinding('x', 2) (taking as arguments the name of the variable and the line number) we could do lots of interesting stuff. This could be done via a decorator: @recordbinding x = obj to result in the compiler generating the following code: x = obj obj.recordbinding('x', 2) Alternativey, you could do all this without any changes to the interpeter by using a trace function which traces the execution of a block: start_recordbingings() # enable trace function x = obj # trace function detects assignment and calls # obj.recordbinding('x', 2) stop_recordbindings() # disable trace function However, this is very slow. A second option would be to place the above into a function definition and have a decorator apply the byte code manipulations to implement the implicit call: @recordbindings def code(): x = obj but this would create problems with the local/global namespaces. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Jun 01 2016)
Python Projects, Coaching and Consulting ... http://www.egenix.com/ Python Database Interfaces ... http://products.egenix.com/ Plone/Zope Database Interfaces ... http://zope.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

On 01.06.2016 15:53, M.-A. Lemburg wrote:
On 01.06.2016 15:19, Sven R. Kunze wrote:
On 01.06.2016 01:46, Ian Foote wrote:
This is the first syntax I've seen in this thread that seems reasonably intuitive and pythonic. I don't mind particularly the keyword used (def or something else), but this structure draws the right parallels in my mind. Still nobody explained why it needs a special syntax at all.
Normal assignment could do it as well as long as the assignment tells the RHS under what name it will be referred to. Right. Essentially, we'd only need a way to tell the compiler to invoke e.g. a method on the RHS object which gets called with the name of the variable it binds (and perhaps the line number to allow for e.g. recording the order of definitions).
I think that's the way I would think of it. Although I don't know if I would prefer a two-step solution (that's what you've described), or a one-step solution (like an implicitly set magic variable); cf. my reply to Paul): https://mail.python.org/pipermail/python-ideas/2016-June/040677.html In terms of usability, I think the magic variable is easier but it might be harder to optimize.
The current byte code for:
x = obj
reads like this:
2 0 LOAD_GLOBAL 0 (obj) 3 STORE_FAST 0 (x)
so if we could get the compiler to generate a method call right after the assignment, e.g. obj.recordbinding('x', 2) (taking as arguments the name of the variable and the line number) we could do lots of interesting stuff.
This could be done via a decorator:
@recordbinding x = obj
to result in the compiler generating the following code:
x = obj obj.recordbinding('x', 2)
Alternativey, you could do all this without any changes to the interpeter by using a trace function which traces the execution of a block:
start_recordbingings() # enable trace function x = obj # trace function detects assignment and calls # obj.recordbinding('x', 2) stop_recordbindings() # disable trace function
However, this is very slow.
A second option would be to place the above into a function definition and have a decorator apply the byte code manipulations to implement the implicit call:
@recordbindings def code(): x = obj
but this would create problems with the local/global namespaces.
I hope that's not the only way to optimize it. :( I am not that deep into CPython development in order to find a better solution but I think it should be possible for the compiler to find out whether a callee actually USES that information or not without any special decoration or something. Sven

On 01.06.2016 17:17, Sven R. Kunze wrote:
... I hope that's not the only way to optimize it. :(
Well, if you restrict yourself to functions and objects, you can implement the assigning from within the code: Instead of writing: x = func('x') you'd write func('x') and have func register itself with the globals under the given name: def func(bindto): value = 'Hello World !' globals()[bindto] = value return value func(bindto='x') >>> x 'Hello World !'
I am not that deep into CPython development in order to find a better solution but I think it should be possible for the compiler to find out whether a callee actually USES that information or not without any special decoration or something.
I don't see how that could work without being more explicit about the intention. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Jun 01 2016)
Python Projects, Coaching and Consulting ... http://www.egenix.com/ Python Database Interfaces ... http://products.egenix.com/ Plone/Zope Database Interfaces ... http://zope.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

On Jun 01, 2016, at 03:53 PM, M.-A. Lemburg wrote:
This could be done via a decorator:
@recordbinding x = obj
to result in the compiler generating the following code:
x = obj obj.recordbinding('x', 2)
I like the idea of using a decorator because it's familiar syntax and we all already (think we) know what it means. However, in this case, wouldn't @recordbinding x = obj translate to x = obj recordbinding(x) ? The way you've written it, obj must be a type that implements the recordbinding method, but that's not what I'd expect. Cheers, -Barry

On 07.06.2016 00:32, Barry Warsaw wrote:
On Jun 01, 2016, at 03:53 PM, M.-A. Lemburg wrote:
This could be done via a decorator:
@recordbinding x = obj
to result in the compiler generating the following code:
x = obj obj.recordbinding('x', 2)
I like the idea of using a decorator because it's familiar syntax and we all already (think we) know what it means. However, in this case, wouldn't
@recordbinding x = obj
translate to
x = obj recordbinding(x)
?
The way you've written it, obj must be a type that implements the recordbinding method, but that's not what I'd expect.
This would work as well and indeed reads better, but you'd need to have the compiler generate: x = obj recordbinding(obj, 'x', 2) ie. pass in the object, the bound name and the line number and recordbinding would then have to decide what to do with the parameters. I used the method variant, because a very common use case is to let the object know about the name under which it is now known to Python. This can be used to eg. define records, forms, mappings, etc. I just wonder how we could tell the compiler to special case this decorator in a clean way. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Jun 07 2016)
Python Projects, Coaching and Consulting ... http://www.egenix.com/ Python Database Interfaces ... http://products.egenix.com/ Plone/Zope Database Interfaces ... http://zope.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

On Jun 07, 2016, at 12:53 AM, M.-A. Lemburg wrote:
This would work as well and indeed reads better, but you'd need to have the compiler generate:
x = obj recordbinding(obj, 'x', 2)
ie. pass in the object, the bound name and the line number and recordbinding would then have to decide what to do with the parameters.
+1 although I'd bikeshed on the order of the arguments.
I used the method variant, because a very common use case is to let the object know about the name under which it is now known to Python. This can be used to eg. define records, forms, mappings, etc.
Yep. So given the above, `recordbinding(obj, 'x', 2)` would of course be free to delegate to `obj.recordbinding('x', 2)`, but it would be up to the decorator (and its author), not the compiler to do that delegation.
I just wonder how we could tell the compiler to special case this decorator in a clean way.
That's the rub, but I do think you're on to a good general solution to a common set of problems. Cheers, -Barry

On Mon, Jun 6, 2016 at 4:14 PM, Barry Warsaw <barry@python.org> wrote:
On Jun 07, 2016, at 12:53 AM, M.-A. Lemburg wrote:
This would work as well and indeed reads better, but you'd need to have the compiler generate:
x = obj recordbinding(obj, 'x', 2)
ie. pass in the object, the bound name and the line number and recordbinding would then have to decide what to do with the parameters.
+1 although I'd bikeshed on the order of the arguments.
I used the method variant, because a very common use case is to let the object know about the name under which it is now known to Python. This can be used to eg. define records, forms, mappings, etc.
Yep. So given the above, `recordbinding(obj, 'x', 2)` would of course be free to delegate to `obj.recordbinding('x', 2)`, but it would be up to the decorator (and its author), not the compiler to do that delegation.
I just wonder how we could tell the compiler to special case this decorator in a clean way.
That's the rub, but I do think you're on to a good general solution to a common set of problems.
I think we're on to something here. Maybe. Perhaps. -- --Guido van Rossum (python.org/~guido)

On 07.06.2016 01:20, Guido van Rossum wrote:
On Mon, Jun 6, 2016 at 4:14 PM, Barry Warsaw <barry@python.org> wrote:
On Jun 07, 2016, at 12:53 AM, M.-A. Lemburg wrote:
This would work as well and indeed reads better, but you'd need to have the compiler generate:
x = obj recordbinding(obj, 'x', 2)
ie. pass in the object, the bound name and the line number and recordbinding would then have to decide what to do with the parameters.
+1 although I'd bikeshed on the order of the arguments.
:-)
I used the method variant, because a very common use case is to let the object know about the name under which it is now known to Python. This can be used to eg. define records, forms, mappings, etc.
Yep. So given the above, `recordbinding(obj, 'x', 2)` would of course be free to delegate to `obj.recordbinding('x', 2)`, but it would be up to the decorator (and its author), not the compiler to do that delegation.
Right, your approach is more generic.
I just wonder how we could tell the compiler to special case this decorator in a clean way.
That's the rub, but I do think you're on to a good general solution to a common set of problems.
I think we're on to something here. Maybe. Perhaps.
One approach would be to define what a decorator means in front of an expression (since that's currently a SyntaxError), without adding any new syntactic sugar. Here's a sketch... @decorator x = obj compiles to: x = obj decorator('x', obj, lineno) (I'm using obj in the calls to decorator here - those would not be new lookups, but instead work using DUP_TOP in the byte code) Things get a bit tricky when you use tuples on the left hand side: @decorator a, b, c = obj compiles to: a, b, c = obj decorator('a, b, c', obj, lineno) Not sure whether this would be useful. Perhaps it's better not to allow for such a use. Same problem for star assignments: @decorator a, *b, c = obj Multiple assignments would work, though: @decorator a = b = c = obj compiles to: a = b = c = obj decorator('a', obj, lineno) decorator('b', obj, lineno) decorator('c', obj, lineno) Augmented assigns are tricky, but would work as well: @decorator a += obj compiles to: a += obj decorator('a', value, lineno) (here value stands for the result of the += operation; in byte code this would be a DUP_TOP before the STORE_FAST to 'a') More complex right hand sides should not pose a problem, since it's clear that only the result matters (which is pushed to the stack and can be DUP_TOPed as necessary). Looking at https://docs.python.org/3/reference/grammar.html, I think those are all cases to consider, unless I missed one. The grammar would need to be extended with: """ decorated: decorators (classdef | funcdef | async_funcdef | assign_expr) assign_expr: NAME (augassign (yield_expr|testlist) | ('=' (yield_expr|assign_expr))*) """ The above disallows testlist or star assignments. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Jun 07 2016)
Python Projects, Coaching and Consulting ... http://www.egenix.com/ Python Database Interfaces ... http://products.egenix.com/ Plone/Zope Database Interfaces ... http://zope.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

On 7 June 2016 at 11:29, M.-A. Lemburg <mal@egenix.com> wrote:
One approach would be to define what a decorator means in front of an expression (since that's currently a SyntaxError), without adding any new syntactic sugar. Here's a sketch...
@decorator x = obj
compiles to:
x = obj decorator('x', obj, lineno)
As a possible guide to designing the signatures for binding decorators, it's probably worth asking what would be needed to make: @bindfunction f = lambda : None equivalent to: def f(): pass Since the interpreter already sets __module__ and __globals__ correctly on lambda functions, the main considerations would be to get f.__name__ and f.__qualname__ set correctly, which means just the immediate target would be insufficient - you'd also want the scope naming information that gets included in __qualname__, but is omitted from __name__. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, Jun 7, 2016 at 4:13 PM Nick Coghlan <ncoghlan@gmail.com> wrote:
As a possible guide to designing the signatures for binding decorators, it's probably worth asking what would be needed to make:
@bindfunction f = lambda : None
equivalent to:
def f(): pass
Since the interpreter already sets __module__ and __globals__ correctly on lambda functions, the main considerations would be to get f.__name__ and f.__qualname__ set correctly, which means just the immediate target would be insufficient - you'd also want the scope naming information that gets included in __qualname__, but is omitted from __name__.
The ``bindfunction`` function could look up the __module__ and use that to assign the __qualname__. Would that satisfy?

On 7 June 2016 at 15:51, Michael Selik <michael.selik@gmail.com> wrote:
On Tue, Jun 7, 2016 at 4:13 PM Nick Coghlan <ncoghlan@gmail.com> wrote:
As a possible guide to designing the signatures for binding decorators, it's probably worth asking what would be needed to make:
@bindfunction f = lambda : None
equivalent to:
def f(): pass
Since the interpreter already sets __module__ and __globals__ correctly on lambda functions, the main considerations would be to get f.__name__ and f.__qualname__ set correctly, which means just the immediate target would be insufficient - you'd also want the scope naming information that gets included in __qualname__, but is omitted from __name__.
The ``bindfunction`` function could look up the __module__ and use that to assign the __qualname__. Would that satisfy?
Not really, due to class and function nesting: >>> class A: ... class B: ... def f(self): ... def g(): pass ... return g ... >>> A.B().f.__qualname__ 'A.B.f' >>> A.B().f().__qualname__ 'A.B.f.<locals>.g' That's why I brought it up as a design problem worth considering - it's *very* easy to inadvertently design namebinding feature that only do the right thing at module scope, and misbehave at class and function scope. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Problem is to remove the duplication of 'name' in expressions like this: name = cls( 'name', arg, kw=val ) Would using a builtin that gives the name of the variable being assigned to be enough? A builtin like lhs_variable_name() - not a great name. name = cls( lhs_variable_name(), arg, kw=val ) This escapes from the problem of knowing the name space that name is in and needing to understand the details of the expression. It does not need a decorator with the complexity of needing a break down of the RHS expression. Now I do not need the 'name' to be the first argument: name = function( arg, kw=lhs_variable_name() ) I'm not sure what the behavior should be for multiple LHS names. name1, name2 = cls( lhs_variable_name(), arg ) Is this an error and an exception is raised? Always return the first name, 'name1'? Does lhs_variable_name() return a tuple of ('name1','name2')? Does lhs_variable_name() take an argument to choose? Barry

On 07.06.2016 21:49, Nick Coghlan wrote:
On 7 June 2016 at 11:29, M.-A. Lemburg <mal@egenix.com> wrote:
One approach would be to define what a decorator means in front of an expression (since that's currently a SyntaxError), without adding any new syntactic sugar. Here's a sketch...
@decorator x = obj
compiles to:
x = obj decorator('x', obj, lineno)
As a possible guide to designing the signatures for binding decorators, it's probably worth asking what would be needed to make:
@bindfunction f = lambda : None
equivalent to:
def f(): pass
Since the interpreter already sets __module__ and __globals__ correctly on lambda functions, the main considerations would be to get f.__name__ and f.__qualname__ set correctly, which means just the immediate target would be insufficient - you'd also want the scope naming information that gets included in __qualname__, but is omitted from __name__.
Well, in your example it would still be enough:
f = lambda : None f.__qualname__ '<lambda>' f.__name__ '<lambda>' def f(): pass ... f.__qualname__ 'f' f.__name__ 'f'
and even at deeper levels, you could base the new .__qualname__ on the one that is set on the lambda function:
class C: ... f = lambda : None ... C.f <function C.<lambda> at 0x7f5f838736a8> C.f.__qualname__ 'C.<lambda>' C.f.__name__ '<lambda>'
The .__qualname__ is set at the time the lambda is built, so it is known even correct after making the assignment in the class definition:
class C: ... f = lambda : None ... print(f.__qualname__) ... C.<lambda>
so I guess this all works - much to my own surprise, because I wouldn't have expected e.g. the last experiment to actually succeed. Someone did a good job there :-) -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Jun 08 2016)
Python Projects, Coaching and Consulting ... http://www.egenix.com/ Python Database Interfaces ... http://products.egenix.com/ Plone/Zope Database Interfaces ... http://zope.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

On Tue, Jun 07, 2016 at 12:53:23AM +0200, M.-A. Lemburg wrote:
On 07.06.2016 00:32, Barry Warsaw wrote:
On Jun 01, 2016, at 03:53 PM, M.-A. Lemburg wrote:
This could be done via a decorator:
@recordbinding x = obj
to result in the compiler generating the following code:
x = obj obj.recordbinding('x', 2)
I don't understand the purpose of the second argument, given as 2. Earlier you say that it's the line number. Why is that needed? I don't see how this actually solves the problem. Apply it to namedtuple: Record = namedtuple('Record', fields) If you naively try the decorator: @recordbinding Record = namedtuple(fields) that gets converted to: Record = namedtuple(fields) # TypeError Record.recordbinding('Record') # AttributeError There's no point in calling a special method on the RHS object, as that requires the object to have been constructed before the method can be called. And that requires the name. So your recordbinding decorator gets called too late. Even if it worked, I strongly dislike that this turns a one-line expression into a two line statement. # Status quo: x = sympy.Symbol('x') # Becomes: @recordbinding x = sympy.Symbol() which doesn't seem like an improvement to me. [...]
This would work as well and indeed reads better, but you'd need to have the compiler generate:
x = obj recordbinding(obj, 'x', 2)
ie. pass in the object, the bound name and the line number and recordbinding would then have to decide what to do with the parameters.
Again, this is called too late. Your namedtuple needs its name when it is constructed, it can't be constructed first and then the name injected in, even if recordbinding() knew where to inject the name. -- Steve

On 6/7/2016 10:40 PM, Steven D'Aprano wrote:
On Tue, Jun 07, 2016 at 12:53:23AM +0200, M.-A. Lemburg wrote:
On 07.06.2016 00:32, Barry Warsaw wrote:
On Jun 01, 2016, at 03:53 PM, M.-A. Lemburg wrote:
This could be done via a decorator:
@recordbinding x = obj
to result in the compiler generating the following code:
x = obj obj.recordbinding('x', 2)
I don't understand the purpose of the second argument, given as 2. Earlier you say that it's the line number. Why is that needed?
I don't see how this actually solves the problem. Apply it to namedtuple:
Record = namedtuple('Record', fields)
If you naively try the decorator:
@recordbinding Record = namedtuple(fields)
that gets converted to:
Record = namedtuple(fields) # TypeError Record.recordbinding('Record') # AttributeError
I think we might need some helpers, and a slight change to the specifics. I'd have: @binding_method x = obj result in: x = binding_method('x', obj) Then you could just say: @namedtuple Point = 'x y z' Which would become: Point = namedtuple('Point', 'x y z') (or equivalently in this case: @namedtuple Point = ['x', 'y', 'z'] ) As has been pointed out, it's possible a different signature would be needed (not sure about line numbers, but qualname seems desirable). In that case, "namedtuple" above might not be "collections.namedtuple", but some helper. The question for me is: do we want to have something that tells the compiler that "binding_method" or "namedtuple" above are special, or is this just what the compiler does for all uses of what looks like a decorated assignment statement? Eric.
There's no point in calling a special method on the RHS object, as that requires the object to have been constructed before the method can be called. And that requires the name. So your recordbinding decorator gets called too late.
Even if it worked, I strongly dislike that this turns a one-line expression into a two line statement.
# Status quo: x = sympy.Symbol('x')
# Becomes: @recordbinding x = sympy.Symbol()
which doesn't seem like an improvement to me.
[...]
This would work as well and indeed reads better, but you'd need to have the compiler generate:
x = obj recordbinding(obj, 'x', 2)
ie. pass in the object, the bound name and the line number and recordbinding would then have to decide what to do with the parameters.
Again, this is called too late. Your namedtuple needs its name when it is constructed, it can't be constructed first and then the name injected in, even if recordbinding() knew where to inject the name.

On Tue, Jun 07, 2016 at 11:17:26PM -0400, Eric V. Smith wrote:
I think we might need some helpers, and a slight change to the specifics. I'd have:
@binding_method x = obj
result in: x = binding_method('x', obj)
That's a bit more promising. Disadvantage: - what was one line is now two; - confusing to pass more than a single argument (plus the implicit name) to the function; - fails the principle "things that look similar should be similar". Status quo: Record = namedtuple('Record', fields) would become: @namedtuple Record = fields which doesn't look awful. I'm sad that it needs two lines. But what if you want to pass more than one argument? @namedtuple Record = fields, True That will be equivalent to namedtuple('Record', (fields, True)) which is not what is wanted. And it gets worse if you use a keyword argument: Record = fields, verbose=True I don't really like the way the @ syntax is being used for two completely different things. @function def spam(): ... does one thing, but @function spam = ... does a completely different and unrelated thing. I'm not saying that @ cannot be used for anything but decorators, but I think it is confusing to use something which looks so close to decorator syntax for something that is nothing like a decorator.
The question for me is: do we want to have something that tells the compiler that "binding_method" or "namedtuple" above are special, or is this just what the compiler does for all uses of what looks like a decorated assignment statement?
I'm surprised you ask that question :-) What does the Zen say about special cases? I don't think it is desirable to have developers have to go cap in hand to the core devs and say "Please sir, I have a function that needs to know its name, can you please add it to the privileged list of special functions that work with @ please?" *wink* It should either work for any name after the @ or not at all. Hard coding support for just namedtuple would be bad. -- Steve

On 06/08/2016 11:19 AM, Steven D'Aprano wrote:
On Tue, Jun 07, 2016 at 11:17:26PM -0400, Eric V. Smith wrote:
I think we might need some helpers, and a slight change to the specifics. I'd have:
@binding_method x = obj
result in: x = binding_method('x', obj)
That's a bit more promising.
Disadvantage: - what was one line is now two; - confusing to pass more than a single argument (plus the implicit name) to the function; - fails the principle "things that look similar should be similar".
Status quo:
Record = namedtuple('Record', fields)
would become:
@namedtuple Record = fields
which doesn't look awful. I'm sad that it needs two lines.
All true!
But what if you want to pass more than one argument?
@namedtuple Record = fields, True
That will be equivalent to
namedtuple('Record', (fields, True))
which is not what is wanted. And it gets worse if you use a keyword argument:
Record = fields, verbose=True
I'd say it would be: namedtuple('Record', fields, True) But I'm just thinking out loud.
I don't really like the way the @ syntax is being used for two completely different things.
@function def spam(): ...
does one thing, but
@function spam = ...
does a completely different and unrelated thing. I'm not saying that @ cannot be used for anything but decorators, but I think it is confusing to use something which looks so close to decorator syntax for something that is nothing like a decorator.
I agree. I'm just riffing on someone else's proposal.
The question for me is: do we want to have something that tells the compiler that "binding_method" or "namedtuple" above are special, or is this just what the compiler does for all uses of what looks like a decorated assignment statement?
I'm surprised you ask that question :-) What does the Zen say about special cases?
I don't think it is desirable to have developers have to go cap in hand to the core devs and say "Please sir, I have a function that needs to know its name, can you please add it to the privileged list of special functions that work with @ please?"
*wink*
It should either work for any name after the @ or not at all. Hard coding support for just namedtuple would be bad.
I completely agree about special cases! But what I was really thinking about here was that maybe you'd have to tell the compiler in advance that you're defining a special type of "decorator", something along the lines of: ############ # somehow tell the compiler that namedtuple is special __special_assignment_psuedo_decorators__.append(namedtuple) @namedtuple Record = fields ############ Eric.

On 8 Jun 2016, at 17:19, Steven D'Aprano <steve@pearwood.info> wrote:
On Tue, Jun 07, 2016 at 11:17:26PM -0400, Eric V. Smith wrote:
I think we might need some helpers, and a slight change to the specifics. I'd have:
@binding_method x = obj
result in: x = binding_method('x', obj)
That's a bit more promising.
Disadvantage: - what was one line is now two; - confusing to pass more than a single argument (plus the implicit name) to the function; - fails the principle "things that look similar should be similar".
Status quo:
Record = namedtuple('Record', fields)
would become:
@namedtuple Record = fields
which doesn't look awful. I'm sad that it needs two lines.
But what if you want to pass more than one argument?
@namedtuple Record = fields, True
That will be equivalent to
namedtuple('Record', (fields, True))
which is not what is wanted. And it gets worse if you use a keyword argument:
Record = fields, verbose=True
TypeError, can't iterate over boolean @namedtuple(verbose=True) Record = fields Although for namedtuple in particular, I'd rather have namedtuple be a class-generator: class Record(namedtuple(fields)): pass Or class Record(metaclass=namedtuple): fields = fields And namedtuple could abuse all the g(l)ory details of metaclasses and/of eval to do its job. And that is the right solution for namedtuple. The cases that interest me more are typevar/symbol/Django modelfields/sqlalchemy declarative fields and all the cases where you're not constructing a class(like) thingy.
I don't really like the way the @ syntax is being used for two completely different things.
@function def spam(): ...
does one thing, but
@function spam = ...
does a completely different and unrelated thing. I'm not saying that @ cannot be used for anything but decorators, but I think it is confusing to use something which looks so close to decorator syntax for something that is nothing like a decorator.
The question for me is: do we want to have something that tells the compiler that "binding_method" or "namedtuple" above are special, or is this just what the compiler does for all uses of what looks like a decorated assignment statement?
I'm surprised you ask that question :-) What does the Zen say about special cases?
I don't think it is desirable to have developers have to go cap in hand to the core devs and say "Please sir, I have a function that needs to know its name, can you please add it to the privileged list of special functions that work with @ please?"
*wink*
It should either work for any name after the @ or not at all. Hard coding support for just namedtuple would be bad.
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

So for namedtuple: foo = namedtuple('foo', ['a', 'b']) Would become: @recordbinding foo = namedtuple(['a', 'b']) ? This doesn't look like an improvement. Le 07/06/2016 00:32, Barry Warsaw a écrit :
On Jun 01, 2016, at 03:53 PM, M.-A. Lemburg wrote:
This could be done via a decorator:
@recordbinding x = obj
to result in the compiler generating the following code:
x = obj obj.recordbinding('x', 2)
I like the idea of using a decorator because it's familiar syntax and we all already (think we) know what it means. However, in this case, wouldn't
@recordbinding x = obj
translate to
x = obj recordbinding(x)
?
The way you've written it, obj must be a type that implements the recordbinding method, but that's not what I'd expect.
Cheers, -Barry
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

Steven D'Aprano <steve@...> writes:
How do you feel about an arrow operator?
T -> TypeVar() x -> Symbol() T -> type(bases, ns) Record -> namedtuple(fields)
In general:
name [, name, ...] -> callable(...)
where "callable" is some expression, e.g. a name, a dot lookup, etc.
It could work with indexing, like: key -> my_dict[] instead of key = my_dict[key] Or even attribute lookup with a trailing dot: attr -> my_object. instead of attr = my_object.attr Joseph

On Tue, May 31, 2016 at 02:12:05PM +0000, Joseph Martinot-Lagarde wrote:
It could work with indexing, like:
key -> my_dict[]
instead of
key = my_dict[key]
That would have to be key = my_dict['key'] With the possible exception of the typing module, I don't see when that would be useful. There are proven uses for passing a name to a function. Any time that an object needs to know its own name, you have to pass it to the constructor. But I can't think of any cases where you would want to do something similar with item lookup.
Or even attribute lookup with a trailing dot:
attr -> my_object.
That fails the "grit on Uncle Timmy's monitor" test. The trailing dot is easy to miss. -- Steve

On 31 May 2016 at 15:24, Steven D'Aprano <steve@pearwood.info> wrote:
On Tue, May 31, 2016 at 02:12:05PM +0000, Joseph Martinot-Lagarde wrote:
It could work with indexing, like:
key -> my_dict[]
instead of
key = my_dict[key]
That would have to be
key = my_dict['key']
With the possible exception of the typing module, I don't see when that would be useful.
It's reminiscent of the recent "dictionary unpacking" discussion. But it's doable without special-casing [] anyway: key -> my_dict.get [I still prefer "call the RHS with the name of the LHS as the argument" over "inject the name of the LHS as the first argument of the call present on the RHS", btw - mostly because it has fewer restrictions and special cases to define] Paul

On Tue, May 31, 2016 at 03:39:14PM +0100, Paul Moore wrote:
[I still prefer "call the RHS with the name of the LHS as the argument" over "inject the name of the LHS as the first argument of the call present on the RHS", btw -
Do I understand that you think that the RHS should be limited to callables that take a single argument only? So far we have four real use-cases: T = TypeVar('T') x = sympy.Symbol('x') cls = type('cls', bases, ns) Record = nametuple('Record', fields) You would only support the first two cases and leave the others with the status quo? That seems like a strange restriction to make. What problem do you think the other two use-cases would cause that the first two don't? The fundamental use-case here is "any object that needs to know its own name". Most objects need more than just a name. If you're worried that it will be confusing that we define a callable to take (say) three arguments: class Widget: def __init__(self, name, foo, bar): ... but then call it with only two: spanner -> Widget(foo=1, bar=2) I think you're worrying over nothing. In fact, I played a trick on you... I said we define a callable that takes three arguments, but in fact I defined it with FOUR. We're constantly writing methods that take an extra argument, self, that is provided by the compiler. A similar argument applies to decorators: # defined with a single argument def decorator(func): ... # called with no explicit argument (or even parens!) @decorator And both cases can be used without compiler magic, using an unbound method versus a bound method, decorator syntax versus calling the decorator by hand. So while this is magic, it is the same sort of magic we deal with frequently in Python. So frequently that we forget just how magical it is that the "self" param is provided automatically for us. -- Steve

On 31 May 2016 at 16:08, Steven D'Aprano <steve@pearwood.info> wrote:
On Tue, May 31, 2016 at 03:39:14PM +0100, Paul Moore wrote:
[I still prefer "call the RHS with the name of the LHS as the argument" over "inject the name of the LHS as the first argument of the call present on the RHS", btw -
Do I understand that you think that the RHS should be limited to callables that take a single argument only?
Yes. Or more specifically, expressions that evaluate to a callable. Because as you said in your original post, once you start allowing injection into anything more complicated than a "simple call" the whole thing gets messy. It's possible you could somehow restrict what's allowed, but not easy - IIRC, the complications around what was originally allowed in the position of a decorator were similar. Also, needing to have the "wrong" number of arguments on the RHS seems like it would lead to confusion: T => TypeVar() x => sympy.Symbol() cls => type(bases, ns) Record => nametuple(fields) Compare the calls on the RHS to the documentation. It'd probably also play havoc with IDEs that do signature introspection.
So far we have four real use-cases:
T = TypeVar('T') x = sympy.Symbol('x') cls = type('cls', bases, ns) Record = nametuple('Record', fields)
You would only support the first two cases and leave the others with the status quo? That seems like a strange restriction to make. What problem do you think the other two use-cases would cause that the first two don't?
The idea was that the second pair can easily be handled using functools.partial. So there's no loss of functionality, just a (small) inconvenience. Having said that, I've just checked functools.partial, and it doesn't appear to allow for inserting an argument at the *start* of the argument list. Bah. But you could still do def mktype(bases, ns): return lambda cls: type(cls, bases, ns) def mknamedtuple(fields) return lambda name: namedtuple(name, fields) cls => mktype(bases, ns) Record => mknamedtuple(fields) ... and if this were a language feature, there's nothing stopping mknamedtuple being added to the collections module (and mktype somewhere, maybe operator). But conceded, it's a limitation. My feeling is that the benefit in terms of simpler semantics is worth it, but I can see your POV as well. Paul

Steven D'Aprano wrote:
If you're worried that it will be confusing that we define a callable to take (say) three arguments ... but then call it with only two ... I think you're worrying over nothing.
The situation with "self" is a bit different. When you do something like foo.method(args) it's obvious that something is being done *before* the call, i.e. looking up the 'method' attribute of foo, and that step has the opportunity to manufacture and return a callable having the appropriate number of args. However, with x -> TypeVar() it looks very much like whatever is bound to TypeVar is being called directly, before anything else happens. For that not to be the case would be a rather higher order of weirdness, IMO. -- Greg

On May 31 2016, Steven D'Aprano <steve-iDnA/YwAAsAk+I/owrrOrA@public.gmane.org> wrote:
How do you feel about an arrow operator?
T -> TypeVar() x -> Symbol() T -> type(bases, ns) Record -> namedtuple(fields)
Solves the problem nicely, but difficult to memorize. I'd always expect the RHS to be evaluated first, and *then* somehow be connected to the LHS. How about a special placeholder? Record -> namedtuple($me, fields) Record := namedtuple($me, fields) or maybe even Record = namedtuple($me, fields) *duck* (yeah, I guess I'd like to see $ get semantics in Python) Best, -Nikolaus -- GPG encrypted emails preferred. Key id: 0xD113FCAC3C4E599F Fingerprint: ED31 791B 2C5C 1613 AF38 8B8A D113 FCAC 3C4E 599F »Time flies like an arrow, fruit flies like a Banana.«

I think that the idea of implicit call or magically inserting items at the front (or any arbitrary position, for that matter) isn't going to work. For it to work, we need to address cases such as those: x -> foo("bar")("baz") # is this foo("x", "bar")("baz") or foo("bar")("x", "baz") ? x -> foo() # is this foo("x") or foo()("x") ? x -> foo # hmm, are we doing foo("x") here, or should this be a syntax error? I think that whatever we go for, we need to be explicit where the name goes - but that completely breaks the purpose of the proposal to begin with. Or we could get rid of ambiguity and something like this instead: x -> foo("bar")("baz") # actually foo("x", "bar")("x", "baz") x -> foo() # wait, this is actually foo("x")("x") Yuck! I tend to like implicit black magic in some of my code, but that's pushing it too far. Or wait, maybe we can settle for a syntax like this (hypothetic pseudo-code): x -> foo(?, "bar") # this is foo("x", "bar") x -> foo(?) # foo("x") x -> foo(?, "bar", ?) # is this foo("x", "bar", "x") or a syntax error? This looks fine. What about this, though? a, b, c -> foo(?) # probably foo(("a", "b", "c")), right? a, b, c -> foo(?, "bar", ?, "baz", ?) # is this foo("a", "bar", "b", "baz", "c") or a syntax error? In the face of ambiguity, refuse the temptation to guess and throw a syntax error in all ambiguous cases. Oh wait, that's how Python works right now - every case is ambiguous for that. The main point is that there's no way this is going to fly if there isn't an explicit way to declare the implicitness - at that point I'd rather hand-roll my own function (which I can probably customize to fit the needs of my module). Something like: def set_global(func, name, *args, **kwargs): globals()[name] = func(name, *args, **kwargs) In fact, I would go for some decorator magic (I love decorators): def call_with_name(func): fn, args, kwargs = func() return fn(func.__name__, *args, **kwargs) @call_with_name def x(): return namedtuple, (fields,), {} There you go, and you only typed the name once, in the function definition. Magic enough for you? I love syntactic sugar and Python's magic, and this would be just fine for me. And, as everyone knows, code is read much more often than it is written. As such, x -> foo() is easier to write, but much harder to read, turning the fun of writing something that works using compiler magic into a maintenance hell. While x = foo("x") takes more keystrokes to type, it's exactly in line with what I'm used to see in Python, doesn't require any magic, is very easy to get right and makes maintaining the module straightforward for everyone involved. ---- I agree that something like T = TypeVar("T") isn't pretty, but explicit is better than implicit here. If someone comes with an obviously unambiguous syntax for this, then I'll be in favour. Until then, count me -1 on the whole thing. Finding the appropriate syntax for this isn't bikeshedding here, it's the most important part of the whole thing. As I stated above, most of the ideas mentioned here fall into the "obviously unobvious" category. I also think that adding new syntax might become an attractive nuisance, where people use it all the time everywhere and code quickly becomes unreadable. It also makes one new concept for newbies to learn, possibly quite early if most start to adopt this. All in all, while I see and feel the need, I don't think the syntax change is a good idea, sadly. - Emanuel

On Tue, May 31, 2016 at 02:08:08PM -0400, Émanuel Barry wrote:
I think that the idea of implicit call or magically inserting items at the front (or any arbitrary position, for that matter) isn't going to work. For it to work, we need to address cases such as those:
I thought I had.
x -> foo("bar")("baz") # is this foo("x", "bar")("baz") or foo("bar")("x", "baz") ?
Syntax error.
x -> foo() # is this foo("x") or foo()("x") ?
foo('x')
x -> foo # hmm, are we doing foo("x") here, or should this be a syntax error?
Syntax error.
x -> foo(?, "bar") # this is foo("x", "bar") x -> foo(?) # foo("x") x -> foo(?, "bar", ?) # is this foo("x", "bar", "x") or a syntax error?
This looks fine. What about this, though?
Not too keen on a placeholder, mostly because I think YAGNI. Until somebody points out some real-world use-cases where the callable expects the name to be given in something other than the first position, I'm not too worried about supporting arbitrary positions. All the actual use-cases I've seen have the name as the first argument.
a, b, c -> foo(?) # probably foo(("a", "b", "c")), right? a, b, c -> foo(?, "bar", ?, "baz", ?) # is this foo("a", "bar", "b", "baz", "c") or a syntax error?
Another YAGNI.
In the face of ambiguity, refuse the temptation to guess and throw a syntax error in all ambiguous cases. Oh wait, that's how Python works right now - every case is ambiguous for that.
Under my proposal, there's no ambiguity. Any ambiguous cases will be a syntax error. The rule is simple: the right hand side must be a "simple" expression that ends with being called, similar to the rules for @decorator syntax. And then the left hand name is substituted for the first parameter. If you don't like that, okay, but I don't think there's any ambiguity, any more than: @functools.wraps(func) def function(x, y): ... is ambiguous.
The main point is that there's no way this is going to fly if there isn't an explicit way to declare the implicitness
I'm afraid I don't know what that means.
- at that point I'd rather hand-roll my own function (which I can probably customize to fit the needs of my module). Something like:
def set_global(func, name, *args, **kwargs): globals()[name] = func(name, *args, **kwargs)
What makes you think the variables are always globals?
In fact, I would go for some decorator magic (I love decorators):
def call_with_name(func): fn, args, kwargs = func() return fn(func.__name__, *args, **kwargs)
@call_with_name def x(): return namedtuple, (fields,), {}
There you go, and you only typed the name once, in the function definition. Magic enough for you?
Are you suggesting this actually works? When I try it, I get: NameError: name 'fields' is not defined so I think there's something you've forgotten. Besides, I *really* don't think that: @call_with_name def T: return Typevar, (), {} is an improvement over: T = Typevar('T') but feel free to use it in your own code.
I love syntactic sugar and Python's magic, and this would be just fine for me. And, as everyone knows, code is read much more often than it is written. As such,
x -> foo()
is easier to write, but much harder to read
Harder to read than the decorator you give above? I bet that if Python didn't have a multiplication operator, and somebody suggested adding * for multiplication, there would be a flood of rejections saying "Oh sure, 10*x is easier to write, but it's much harder to read than x+x+x+x+x+x+x+x+x+x. * is too confusing and magical." Heaven help us if somebody suggested ** for exponentiation... *wink*
turning the fun of writing something that works using compiler magic into a maintenance hell.
Maintenance hell. Right. Because that's not even slightly a gross exaggeration. [...]
I also think that adding new syntax might become an attractive nuisance, where people use it all the time everywhere
Yeah, I can see it now. y -> x + 1 # Oh wait, that's a syntax error print(x -> 'Hello world!') # Oh, that's a syntax error too I cannot imagine how you think people will be able to use this syntax "all the time everywhere". If anything, this syntax could be rejected as being too limited and not useful enough, rather than because it can be applied everywhere. -- Steve

Steven D'Aprano writes:
If anything, this syntax could be rejected as being too limited and not useful enough,
+1 on that sentiment. I'm willing to bet that all of these cases are optimizations in any case. Otherwise you'd be using "class". People expect optimized code to be somewhat uglier than the textbook "teach the idiom" version. Case in point: the partition exchange sort (more commonly called "quicksort"). It's actually very easy to understand, it's just the optimization of in-place sort (well, OK, C don't help) that makes it look hard. Don't believe me? Check it out![1] https://www.youtube.com/watch?v=bSfe5M_zG2s?start=1895 I don't have anything against nice syntax for optimized code, but I don't consider your syntax an improvement over the status quo. Not that it's outright ugly, just not an improvement. Steve Footnotes: [1] The start parm is if you're in a hurry. The whole keynote is highly recommended!

On 2016-05-30 20:08, Steven D'Aprano wrote:
How do you feel about an arrow operator?
T -> TypeVar() x -> Symbol() T -> type(bases, ns) Record -> namedtuple(fields)
In general:
name [, name, ...] -> callable(...)
where "callable" is some expression, e.g. a name, a dot lookup, etc.
# okay name -> sympy.Symbol()
I don't like this that much, because it looks odd to have the expression on the right without the argument. The syntax doesn't make it at all clear how the T gets used on the RHS.
Using the arrow operator with arbitrary expressions on the right will be a SyntaxError:
x -> y + 1 my_name -> spam*f() my_name -> f() or g()
I reject that third example as it is unclear whether both f and g, or only f, get the name as first argument.
But this is allowed:
my_name -> f(g()) # like f('my_name', g())
So. . . what exactly will be allowed? Above you said the RHS would be "a name, a dot lookup, etc.". If you can't do it with at least dot lookups I don't see much point; you'd definitely want to be able to do this to create names from method calls. But it could get confusing to start splitting the expression syntax into portions that are allowed in this new "name-passing" syntax and portions that aren't.
The hard case -------------
Assignment target(s) is not a plain name.
spam[2].eggs().attr -> Symbol()
I'm going to take the cowards way out and just say "we don't allow that".
That seems like the best way out. :-) It seems like the main purpose of this is as a shortcut to create an object with a simple, readable name. That goal isn't served by trying to assign some complicated expression. Anyway, I still have a problem with solutions like this that separate the name from the expression in which it will be used. It seems crude to restrict this syntax to cases where the outermost callable takes the name as its first positional argument only. What if we want to pass the name by keyword or something? Or what if you want to do `f(g())` as in your example, but pass the name argument to g instead of f? Because of that, I'm more inclined towards something that involves a placeholder in the actual expression (as someone else suggested elsewhere on this thread). Something like: Symbol(def x) An expression could contain at most one "def name" token, and the result of the entire expression would be assigned to that name. I acknowledge that this particular syntax isn't ideal, because the "def x" could be difficult to see in a complicated expression (although syntax highlighting would mitigate that). The construct could perhaps be introduced by a keyword to call out its unusual behavior: assign Symbol(def x) I'm just making up "assign" right now, not actually saying that's what should be used. Maybe some existing keyword could be repurposed. The upshot is that, to my eye, any solution that pulls the "name" argument out of the expression where it's used is to going to A) be confusing; and B) painfully restrict the use cases to those where the RHS is a very simple expression into which the name can be inserted at an easily-definable place. So I incline towards solutions that "tag" the name in-place in the expression, rather than pulling it out. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

On Tue, May 31, 2016 at 11:13:30AM -0700, Brendan Barnwell wrote:
# okay name -> sympy.Symbol()
I don't like this that much, because it looks odd to have the expression on the right without the argument. The syntax doesn't make it at all clear how the T gets used on the RHS.
*shrug* You have to learn it, just like you have to learn decorator syntax. It's literally a *one sentence explanation*: "The name on the left hand side is passed to the function on the right hand side, as a string, as the first positional argument." [...]
But this is allowed:
my_name -> f(g()) # like f('my_name', g())
So. . . what exactly will be allowed?
Guido suggested that we restrict it similar to the restrictions on decorator syntax. I'd be okay with that. Without looking up the syntax, from memory: @decorate @name.decorate @factory() # no arguments, returns a decorator @factory(args, keyword=foo) and possibly a few others. So by analogy: x -> Function() x -> module.Function() x -> module.Function(args, keyword=foo) etc. -- Steve

On 2016-06-01 11:30, Steven D'Aprano wrote:
On Tue, May 31, 2016 at 11:13:30AM -0700, Brendan Barnwell wrote:
# okay name -> sympy.Symbol()
I don't like this that much, because it looks odd to have the expression on the right without the argument. The syntax doesn't make it at all clear how the T gets used on the RHS. *shrug*
You have to learn it, just like you have to learn decorator syntax. It's literally a*one sentence explanation*:
"The name on the left hand side is passed to the function on the right hand side, as a string, as the first positional argument."
It is simple, but if the functionality is that limited, I don't think I'm in favor of adding it. Your proposal would handle essentially nothing but the two or three mentioned usecases, and would not be generalizable to others, because of the restrictions on the kinds of expressions allowed on the RHS. That's a fair amount of magic to accomplish relatively little. Decorators are different, because the decorator applies to the *result* of the thing it wraps. That is, the normal function definition results in a function object, and the decorator acts on the resulting object. Since the decorator is only getting one thing, there isn't much choice about "where" that thing should go in the call expression. But in the name-passing case, the name is meant to actually be used within the expression on the right, so it has to get inserted before that expression is evaluated, and then you have questions about where to insert it. Your proposal gets around this by restricting the form of the RHS so that there are fewer possible insertion points. But that's also not parallel to decorators, because decorators apply to functions (and classes), and there's no way to define more than one function (or class) "at the same time", so stipulating that a decorator can apply only to a single definition doesn't lose anything. In other words, there's nothing you can do in a normal function definition that you can't do in a decorated function definition. But with this new syntax, there is a great deal you could do in a normal expression that you can't do in a name-passing expression. I agree that your proposal is more simple and straightforward than some of the others that have been proposed. But for my money, it's not worth adding new syntax just to handle simple cases like `x = Symbol('x')`, and as I see it, that's essentially all this new syntax would handle. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

On Mon, May 30, 2016 at 11:08 PM Steven D'Aprano <steve@pearwood.info> wrote:
On Mon, May 30, 2016 at 06:16:26PM -0700, Guido van Rossum wrote:
[...]
T = TypeVar('T') x = Symbol('x')
I'm sure this is not a new idea, but so far I've always thought that this is pretty esoteric and the approach here is good enough.
This comes up a lot and it would be nice to clean it up.
T = type('T', bases, ns) Record = namedtuple('Record', fields)
I'm not enthusiastic about cleaning it up. It feels appropriately awkward. I don't want my colleagues having too easy a time dynamically creating types or functions. You brought up decorators as an example, but I think that was more about how decorators tend to be used essentially as part of the function definition. They might dramatically alter the behavior of the function and yet appear as an afterthought without the decorator syntax. From the PEP 318, "This becomes less readable with longer [functions]". The motivation said nothing about avoiding typing the function name twice.

On Wed, Jun 01, 2016 at 12:52:58AM +0000, Michael Selik wrote:
You brought up decorators as an example, but I think that was more about how decorators tend to be used essentially as part of the function definition. They might dramatically alter the behavior of the function and yet appear as an afterthought without the decorator syntax. From the PEP 318, "This becomes less readable with longer [functions]". The motivation said nothing about avoiding typing the function name twice.
Three times, not twice, and the PEP does mention it, right at the beginning: "It also seems less than pythonic to name the function three times for what is conceptually a single declaration." https://www.python.org/dev/peps/pep-0318/#motivation -- Steve

On Wed, Jun 1, 2016 at 2:35 PM Steven D'Aprano <steve@pearwood.info> wrote:
the PEP does mention it, right at the beginning:
"It also seems less than pythonic to name the function three times for what is conceptually a single declaration."
That's what I get for reading too fast. Still, it does come after the sentence "This becomes less readable with longer methods," making the retyping-avoidance feel like an afterthought.

On May 31, 2016, at 01:08 PM, Steven D'Aprano wrote:
This comes up a lot and it would be nice to clean it up.
T = type('T', bases, ns) Record = namedtuple('Record', fields)
It came up in the discussion on dict unpacking:
a, b, c = **dict # unpacks keys 'a', 'b', 'c'
which was rejected as too magical. But maybe it will seem less magical if we have a special assignment operator that takes the left hand symbol(s) and copies them to the right?
It also comes up in @public for constants, which I currently support as: public(a=SomeA()) Cheers, -Barry

On 31 May 2016, at 02:16, Guido van Rossum <guido@python.org> wrote:
In mypy we have a need for type variables, which are created like this:
from typing import TypeVar
T = TypeVar('T')
I just saw a lightning talk about sympy where they define symbols to be used in mathematical equations, like this:
from sympy import Symbol
x = Symbol('x')
I'm sure this is not a new idea, but so far I've always thought that this is pretty esoteric and the approach here is good enough. But maybe we can actually do better, and whatever solution we come up with might also be useful for extracting attributes from an object or values from a mapping?
Looks like the C #define is what is being asked for. #define SYM( name ) name = Symbol( #name ) SYM( x ) Does python need macro preprocessor? Barry
participants (29)
-
Alan Cristhian
-
Barry Scott
-
Barry Warsaw
-
Brendan Barnwell
-
Bruce Leban
-
cs@zip.com.au
-
David Mertz
-
Eric V. Smith
-
Greg Ewing
-
Guido van Rossum
-
Ian Foote
-
Jonathan Goble
-
Joseph Martinot-Lagarde
-
Joshua Morton
-
M.-A. Lemburg
-
marky1991 .
-
Michael Selik
-
Michel Desmoulin
-
MRAB
-
Nick Coghlan
-
Nikolaus Rath
-
Paul Moore
-
Piotr Duda
-
Sjoerd Job Postmus
-
Stephen J. Turnbull
-
Steven D'Aprano
-
Sven R. Kunze
-
Xavier Combelle
-
Émanuel Barry