Backtick expression: similar to a shorter lambda syntax

Backtick expressions work exactly like lambdas, except that they are bound to the instance they are created in every time that class is used to create one. To illustrate, this “percent” property is bound to the instance, not to the class. class Example: percent = property(`self.v*self.v2/100`) And a few more examples for clarity. def example(): locals()['a'] = 1 expr = `a+1` return expr() # error: one variable is required Any variable names that exist when the backtick expression is created are bound to the expression, and the reference to the expression is stored within the expression. Names that do not exist when the expresssion is created must be passed in as parameters. Such names can also be passed in as keyword arguments. Backtick expressions are created when their scope is created. Variable names that are declared but have not been assigned to will be considered to exist for the purposes of the backtick expression. Directly calling a backtick expression as soon as it’s created is forbidden: `v+1`(a) But this is technically allowed but discouraged, like how := works: (`v+1`)(a) Use Cases This can be used anywhere a lambda would feel “heavy” or long. Here are a few use cases where using a backtick expression would allow code to be significantly mote readable: If/else chains that would be switch statements. Creating decorators. Passing in logging hooks. Writing design-by-contract contracts. (See icontract on GitHub for an example of what DBC looks like in Python.) Tests and assertions. Additionally, the instance binding enables: A shorthand way to create a class that wraps an API to a better or more uniform code interface. Previously you’d need to make defs and @property, now each wrapped property and method is a single, readable line of code. Appendix I propose syntax highlighters show a backtick expression on a different background color, a lighter shade of black for a dark theme; dirty white for a light thing. I also propose the following attributes on the backtick expression. __str__(): the string [parameter names separated by commas] => [the string of the backtick expression] __repr__(): the original string of the backtick expression, surrounded by backticks. I secondarily propose that backtick expressions are only bound to their instances when defined within a class when the following syntax is used: def a = <expression> — Now, let’s bikeshed.

On Sun, Jan 20, 2019 at 07:21:50PM -0500, James Lu wrote:
Sorry, that example is not clear to me. What does it do? There is no instance "self" to bind to at this point.
Still not clear to me. It might help if you showed expected input and output, rather than expecting us to guess.
Any variable names that exist when the backtick expression is created are bound to the expression,
I don't know what you mean by binding a name to an expression.
and the reference to the expression is stored within the expression.
So it forms a reference loop? The expression stores a reference to itself? Why?
Names that do not exist when the expresssion is created must be passed in as parameters.
That's different behaviour from regular functions, where names are only resolved when the function is called.
Created when their scope is created? So not when the line containing the expression is executed?
Variable names that are declared but have not been assigned to will be considered to exist for the purposes of the backtick expression.
Python doesn't have variable declarations, so I don't know what this means.
Directly calling a backtick expression as soon as it’s created is forbidden:
Why?
How is that different? You're still directly calling the expression. [...]
Nothing to do with us, or you for that matter. Editors are free to use whatever syntax highlighting they like, including none at all, and to allow users to customise that highlighting. It disturbs me that you believe you get to tell everyone what syntax highlighting they should use for this feature. That's pretty dictatorial, and not in a good BDFL way. -- Steve

On Mon, Jan 21, 2019 at 05:56:17PM +1100, Steven D'Aprano wrote: [...]
My comment there is excessively terse and I should explain, my apologies. Assigning to the ``locals()`` dictionary is not guaranteed to create or modify the equivalent local variable. Inside a function, ``locals()['a'] = 1`` is NOT the same as ``a = 1``. In CPython, such assignments don't work, although many people don't realise that. In other implementations, they might. So I'm not sure if this is meant to just be a fancy way of assigning to ``a``, or a fancy way of NOT assigning to ``a``, which gives me two possible interpretations of that example depending on whether or not James is aware that writing to locals() may or may not create a local variable. # Backtick expressions don't resolve locals. def example(): a = 1 expr = `a+1` return expr() # error: one variable is required The alternative is a bit harder to guess what it does, since we don't know whether there is or isn't a global variable ``a``. But given that apparently we are required to pass ``a`` as an argument to the expression object, I guess that the second interpretation is: # Backtick expressions don't look for globals. a = 1 # or not, the behavious doesn't change (or does it?) def example(): expr = `a+1` return expr() # error: one variable is required Personally, *either* behaviour seems so awful to me that I can hardly credit that James Lu intends either. The second one means that expression objects can't call builtin or global level functions, unless you pass them in as arguments: obj = `len([]) + 1` obj() # Fails with NameError obj(len=len) # Returns 1 which seems ludicrous. But that appears to be the consequence of requiring variables that aren't in the local scope to be passed as arguments. Since I cannot believe that James actually intends either of these behaviours, I can only repeat my request for clarification. What is this example supposed to show? -- Steve

I’m a little busy recently, so I’ll reply to as much as I can now and reply to the rest later. Scratch the stuff I said about scope. Backtick expressions should inherit the scope normally like any other nested function.
That's different behaviour from regular functions, where names are only resolved when the function is called.
What benefits are there to the late name lookup of normal functions? I’m looking to have backtick expressions raise early, not late. We can relax the names to be optional if a ternary expression is used within the backtick expression. I realized that the behavior of Backtick Expressions can be silently affected by global variables. Example: x = 1 def increment_each(l): return map(`x+1`, l) ## Explicit expression, implicit usage Explicit backtick expressions are ones that, for all parameters that the created function produces, it uses a caret before the name of the parameter. The other names must exist when the backtick expression is evaluated. Example: parameter = 0 is_integer = `int(^parameter) == ^parameter` # arity: 1 despite the global definition self = 'James' str = `^self.__class__.__str__(^self)` # arity: 1 despite the global definition str(type(lambda: 1)) # use our custom str function on the function type; output: <type 'function' at 0x84910> ## Implicitly Created Backtick Expression Implicit baktick expressions, ones that mention undefined parameters without using the caret mark are generally discouraged. However they create UncastBacktickExpression, which must be cast using the .to_safe(*names) method to be used, which takes in a list of parameter names and outputs a normal backtick expression. Even if the variable is defined on a global level, it can be explicitly overridden in to_safe. Example 1 `x+1`.to_safe('x')(1) # output: 2 Example 2 x = 0 `x+1`.to_safe('x')(1) # output: 2 If a backtick expression has no unspecified names and has no carets, it is an implicit backtick expression. This allows developers to safely omit the ^ when the code that is using the resulting backtick expression is aware of the parameters to be used, given that it's obvious to the developer which names are parameters.

On Mon, Jan 21, 2019 at 05:56:17PM +1100, Steven D'Aprano wrote: [...]
Somebody emailed me off-list, and reminded me about type hints: var: int but that's not a declaration in the usual sense of the word. It is officially an annotation. As PEP 526 states: "Type annotations should not be confused with variable declarations in statically typed languages." https://www.python.org/dev/peps/pep-0526/#non-goals At a global level, such type hints have no effect beyond recording the annotation for introspection purposes. Inside a function, they don't do that, but do instruct the compiler to treat the name as a local rather than global. What they certainly don't do is create the variable. If people want to argue that such variable annotations are declarations "but not the same as in statically typed languages", that becomes a matter of argument over definitions. In any case, whether they are declarations or annotations, at least now I think that I understand the intent of the quoted paragraph. As I understand it: - backtick objects raise at creation-time if they refer to a variable that doesn't exist at that moment; - but if the variable has been annotated using the "var: int" syntax, it will be deemed to exist even if it doesn't. To be precise, by "variable" I mean specifically a name-binding. In the interactive interpreter I frequently create functions that refer to a global variable or another function, before I've created that variable or other function. Sometimes this happens in code in modules as well. def spam(): return eggs() + 1 # But eggs doesn't exist yet! def eggs(): return 999 If I change ``spam`` to a backtick object, it is (I presume) an error, because ``eggs`` doesn't exist. What a drag. Especially since (unlike statically typed languages) this inconvenience doesn't even buy me any runtime efficiency. The name lookup for eggs will still have to be done at runtime every time I call spam(). -- Steve

On Sun, Jan 20, 2019 at 9:43 PM James Lu <jamtlu@gmail.com> wrote:
I don't overall hate the idea, but I do have a few negatives to list I see. 1) While backticks are a free syntax now, they used to be a repr() expression in older versions of Python! I'm not keen on re-using them, it'll look real weird to us old people who remember that being common. 2) As a syntax it is pretty lightweight and could be easy to overlook. I like that you thought of highlighters, but you can't depend on them to make this syntax easier to notice. Instead, it is likely that the expressions in the backticks will just blend in with the rest of the code around them. The one positive I see is that because there is no open and closing pair of backticks, like parens or brackets, you can't easily nest this syntax and I actually like how it inherently discourages or makes that impossible! I can't say if I'm -0 or +0 but it is one of those.
-- CALVIN SPEALMAN SENIOR QUALITY ENGINEER cspealma@redhat.com M: +1.336.210.5107 <https://red.ht/sig> TRIED. TESTED. TRUSTED. <https://redhat.com/trusted>

Calvin Spealman wrote:
Perhaps surprisingly, the backtick syntax in Python 2 actually is nestable, despite beginning and ending with the same character. Python 2.7 (r27:82500, Oct 15 2010, 21:14:33) [GCC 4.2.1 (Apple Inc. build 5664)] on darwin Type "help", "copyright", "credits" or "license" for more information.
`'a'+`2+3`` "'a5'"
-- Greg

Later today I will send a working implementation of backtick expressions as a function call.

Backtick expressions work exactly like lambdas, except that they are bound to the instance they are created in every time that class is used to create one.
I would if possible very much like to see some real world examples of Python code, that would benefit by being rewritten to use the new syntax. I'm particularly interested in examples that were written before this idea was posted to this list. And extra points for links with line numbers to Python code in a public git repository. -- Jonathan

On Mon, Jan 21, 2019 at 8:47 AM Jonathan Fine <jfine2358@gmail.com> wrote:
This has come up a few times before on Python-Ideas. Here are a few examples from 2015 (but please read the full thread for discussion): https://mail.python.org/pipermail/python-ideas/2015-March/032758.html https://mail.python.org/pipermail/python-ideas/2015-March/032822.html

Jonathan Fine writes:
Note: the usual way of doing this is to find examples in the standard library. It's not perfect, but the stdlib is generally pretty good code to start with, and is written with a fairly consistent style. Yes, those two features makes finding syntax that makes the stdlib more readable a pretty high barrier. Steve -- Associate Professor Division of Policy and Planning Science http://turnbull.sk.tsukuba.ac.jp/ Faculty of Systems and Information Email: turnbull@sk.tsukuba.ac.jp University of Tsukuba Tel: 029-853-5175 Tennodai 1-1-1, Tsukuba 305-8573 JAPAN

Going back to the original post: On Sun, Jan 20, 2019 at 6:43 PM James Lu <jamtlu@gmail.com> wrote:
?!? bound every time that instance is used to create one -- I have no idea what that means, or why it's useful. As I understand it, Python has expressions and statements -- expressions evaluate to a value that can be referenced in various ways, statements can do other stuff. A lambda is an expression -- it evaluates to an function object, which can then be bound to a name, stored in a container, passed to another function, etc. Is this proposal somehow different? does a backtick expression evaluate to a function? It seems from other parts of this thread that is does somethign different with namespaces and globals and locals, or ??? To illustrate, this “percent” property is bound to the instance, not to the
I do not understand this -- "property" is a decorator, or even more generally, a class which, when called, creates a "property" instance, that follows the descriptor protocol: https://docs.python.org/3/howto/descriptor.html As it is currently written, property expects a "getter" method as it's first argument. So the above would be written as: @property def percent(self): return self.v * self.v2/100 or, without the decoration: def percent(self): return self.v * self.v2/100 percent = property(percent) or, with a lambda: percent = property(lambda self: self.v * self.v2 / 100)) In any case, property expects a callable as it's first argument that will take an instance as its first argument. So there is no place for "binding to an instance". As far as I can tell, other than saving the six characters of lambda, the other thing that this does is provide some implicit argument calling -- how does that work exactly? in the above example, remember that "self" is a convention, so your backtick example could jsut as easily be: class Example: percent = property(`thing.v * thing.v2 / 100`) so when the function object is created, how does it know what arguments it takes? In this case, there is only one name used in the expression, so I guess we could assume that that's an argument, but what if it were: class Example: percent = property(`thing.v * thing.v2 / x`) Now we have both "self" and "x" as names to be resolved -- and the function will be called with an instance as the first argument -- so is that first argument "thing" or "x", and what namespace is the other one to be looked for? You do try to explain this here: Any variable names that exist when the backtick expression is created are
bound to the expression,
what is "the expression"? -- as a rule expressions can't be bound to. and the reference to the expression is stored within the expression. it sure sounds like you are using the same work in two ways here...
Names that do not exist when the expression is created must be passed in as parameters.
so if I have a block of code like: a = 5 bt = `a + b` Then bt will be a function that takes one positional argument, so this is the equivalent of: bt = lambda b: a + b Which means that if I go and add a "b" above that line of code later on, suddenly the meaning of this expression changes? that sure seems fragile to me! or is it? bt = lambda b, a=a: a + b that is, the function gets the VALUE of a at teh time the function is created? Such names can also be passed in as keyword arguments. Backtick expressions
are created when their scope is created.
no it getting really, really confusing if there are more than a couple names involved: a = 5 b = 7 bt = `c * d / a + b *e *f / d * f` so c, d, e, and f are not defined, so the function needs 4 parameters? are they positional? in what order? if keyword, what is the default? This seems like a LOT of magic -- and for what? just to save typing? -CHB Use Cases
We're really going to need to examples for these -- I can imagine examples where the code would be shorter, but NOT more readable! -CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

On Sun, Jan 20, 2019 at 6:43 PM James Lu <jamtlu@gmail.com> wrote:
First, if there is a useful procedure I am strongly against using backticks because (1) it's been used in the past with an entirely different meaning and (2) it looks ugly and is not visually suggestive at all of what it does, especially not the subtle difference between other function definitions. Second, I don't understand exactly what this difference or why it would be useful. It would help for you to give examples comparing lambda and this variation. Third, you mention using ^ in "explicit" expressions to refer to parameters of the "created function" and I do not know what function you are referring to or what the exact semantics of this are. Again, a comparison of two expressions with and without that ^ would help. An expression is not a function and not all expressions are written inside functions. (And as to the specific proposed syntax, there already is the ^ xor operator and the most expected meaning of ^value is ~value. just as the unary + and - operators corresponds to the binary operators. The only thing that I can think of is that you want `foo + ^bar` to be another way of writing lambda bar: foo + bar with some under-specified behavior for evaluating foo and different under-specified behavior for evaluating bar . Finally, if there is some other useful semantics for references inside a function definition, then I would think the best way to do that is to implement that, not add a new function difference. For example, lambda foo: foo + $bar def sample(foo): return foo + $foo where I'm arbitrarily using $ to represent the new semantics whatever they are (no point in bikeshedding syntax when semantics are yet to be defined). --- Bruce

Backtick expressions (now) use the same scoping and same binding rules as other functions. The only difference is that class Class: stacticmethod = `...` staticmethod = lambda: ... def instancemethod = `...` # an instancemethod that's called with self passed in def property = property(`...`) # an instancemethod that's called with self passed in
That is what `lambda bar: foo + ^bar` means. A caret in a backtick expression indicates that the name after the caret is a parameter. All names with the same name must have a caret before them. Mandatory parameters can be passed in as keyword arguments or as positional ones. As for the under-specification, I've been working on an example implementation I'll send soon for backtick expressions. I've also been doing the "look for use cases in stdlib" thing that Johnathan and Steve mentioned. On Wed, Jan 23, 2019 at 3:02 AM Bruce Leban <bruce@leban.us> wrote:

Now you have a lambda inside a back tick expression?!?
So are all the ^ names positional parameters? And only them? And are they on the order they first appear in the expression? How about when there are parentheses, so the order evaluated my not be the same as the order written? Anyway, IIUC, this is a way to write a lambda with less typing, and harder to read. And fewer features— any way to do keyword (with default) parameters? “Explicit is bettter than implicit” So -1 from me. -CHB
-- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

On Wed, Jan 23, 2019 at 1:07 PM James Lu <jamtlu@gmail.com> wrote:
Backtick expressions (now) use the same scoping and same binding rules as other functions.
What do you mean by "now"?? There are no backtick expressions in Python anymore and they were never functions.
You seem to be inventing new syntax as you go. And you haven't told us how the first two above differ.
I have no idea what this means. You're giving syntax without semantics. The word "that" in your sentence is an unbound reference.
In a word, ick. So to find the parameters for a function, I need to scan through the entire text of the function looking for ^? And you think that's an improvement? You've given no explanation of how this is better and saving typing the word lambda isn't enough. All in all this sounds like "but these go to 11" justification and that really is insufficient. I'm -11. --- Bruce

On Sun, Jan 20, 2019 at 07:21:50PM -0500, James Lu wrote:
Sorry, that example is not clear to me. What does it do? There is no instance "self" to bind to at this point.
Still not clear to me. It might help if you showed expected input and output, rather than expecting us to guess.
Any variable names that exist when the backtick expression is created are bound to the expression,
I don't know what you mean by binding a name to an expression.
and the reference to the expression is stored within the expression.
So it forms a reference loop? The expression stores a reference to itself? Why?
Names that do not exist when the expresssion is created must be passed in as parameters.
That's different behaviour from regular functions, where names are only resolved when the function is called.
Created when their scope is created? So not when the line containing the expression is executed?
Variable names that are declared but have not been assigned to will be considered to exist for the purposes of the backtick expression.
Python doesn't have variable declarations, so I don't know what this means.
Directly calling a backtick expression as soon as it’s created is forbidden:
Why?
How is that different? You're still directly calling the expression. [...]
Nothing to do with us, or you for that matter. Editors are free to use whatever syntax highlighting they like, including none at all, and to allow users to customise that highlighting. It disturbs me that you believe you get to tell everyone what syntax highlighting they should use for this feature. That's pretty dictatorial, and not in a good BDFL way. -- Steve

On Mon, Jan 21, 2019 at 05:56:17PM +1100, Steven D'Aprano wrote: [...]
My comment there is excessively terse and I should explain, my apologies. Assigning to the ``locals()`` dictionary is not guaranteed to create or modify the equivalent local variable. Inside a function, ``locals()['a'] = 1`` is NOT the same as ``a = 1``. In CPython, such assignments don't work, although many people don't realise that. In other implementations, they might. So I'm not sure if this is meant to just be a fancy way of assigning to ``a``, or a fancy way of NOT assigning to ``a``, which gives me two possible interpretations of that example depending on whether or not James is aware that writing to locals() may or may not create a local variable. # Backtick expressions don't resolve locals. def example(): a = 1 expr = `a+1` return expr() # error: one variable is required The alternative is a bit harder to guess what it does, since we don't know whether there is or isn't a global variable ``a``. But given that apparently we are required to pass ``a`` as an argument to the expression object, I guess that the second interpretation is: # Backtick expressions don't look for globals. a = 1 # or not, the behavious doesn't change (or does it?) def example(): expr = `a+1` return expr() # error: one variable is required Personally, *either* behaviour seems so awful to me that I can hardly credit that James Lu intends either. The second one means that expression objects can't call builtin or global level functions, unless you pass them in as arguments: obj = `len([]) + 1` obj() # Fails with NameError obj(len=len) # Returns 1 which seems ludicrous. But that appears to be the consequence of requiring variables that aren't in the local scope to be passed as arguments. Since I cannot believe that James actually intends either of these behaviours, I can only repeat my request for clarification. What is this example supposed to show? -- Steve

I’m a little busy recently, so I’ll reply to as much as I can now and reply to the rest later. Scratch the stuff I said about scope. Backtick expressions should inherit the scope normally like any other nested function.
That's different behaviour from regular functions, where names are only resolved when the function is called.
What benefits are there to the late name lookup of normal functions? I’m looking to have backtick expressions raise early, not late. We can relax the names to be optional if a ternary expression is used within the backtick expression. I realized that the behavior of Backtick Expressions can be silently affected by global variables. Example: x = 1 def increment_each(l): return map(`x+1`, l) ## Explicit expression, implicit usage Explicit backtick expressions are ones that, for all parameters that the created function produces, it uses a caret before the name of the parameter. The other names must exist when the backtick expression is evaluated. Example: parameter = 0 is_integer = `int(^parameter) == ^parameter` # arity: 1 despite the global definition self = 'James' str = `^self.__class__.__str__(^self)` # arity: 1 despite the global definition str(type(lambda: 1)) # use our custom str function on the function type; output: <type 'function' at 0x84910> ## Implicitly Created Backtick Expression Implicit baktick expressions, ones that mention undefined parameters without using the caret mark are generally discouraged. However they create UncastBacktickExpression, which must be cast using the .to_safe(*names) method to be used, which takes in a list of parameter names and outputs a normal backtick expression. Even if the variable is defined on a global level, it can be explicitly overridden in to_safe. Example 1 `x+1`.to_safe('x')(1) # output: 2 Example 2 x = 0 `x+1`.to_safe('x')(1) # output: 2 If a backtick expression has no unspecified names and has no carets, it is an implicit backtick expression. This allows developers to safely omit the ^ when the code that is using the resulting backtick expression is aware of the parameters to be used, given that it's obvious to the developer which names are parameters.

On Mon, Jan 21, 2019 at 05:56:17PM +1100, Steven D'Aprano wrote: [...]
Somebody emailed me off-list, and reminded me about type hints: var: int but that's not a declaration in the usual sense of the word. It is officially an annotation. As PEP 526 states: "Type annotations should not be confused with variable declarations in statically typed languages." https://www.python.org/dev/peps/pep-0526/#non-goals At a global level, such type hints have no effect beyond recording the annotation for introspection purposes. Inside a function, they don't do that, but do instruct the compiler to treat the name as a local rather than global. What they certainly don't do is create the variable. If people want to argue that such variable annotations are declarations "but not the same as in statically typed languages", that becomes a matter of argument over definitions. In any case, whether they are declarations or annotations, at least now I think that I understand the intent of the quoted paragraph. As I understand it: - backtick objects raise at creation-time if they refer to a variable that doesn't exist at that moment; - but if the variable has been annotated using the "var: int" syntax, it will be deemed to exist even if it doesn't. To be precise, by "variable" I mean specifically a name-binding. In the interactive interpreter I frequently create functions that refer to a global variable or another function, before I've created that variable or other function. Sometimes this happens in code in modules as well. def spam(): return eggs() + 1 # But eggs doesn't exist yet! def eggs(): return 999 If I change ``spam`` to a backtick object, it is (I presume) an error, because ``eggs`` doesn't exist. What a drag. Especially since (unlike statically typed languages) this inconvenience doesn't even buy me any runtime efficiency. The name lookup for eggs will still have to be done at runtime every time I call spam(). -- Steve

On Sun, Jan 20, 2019 at 9:43 PM James Lu <jamtlu@gmail.com> wrote:
I don't overall hate the idea, but I do have a few negatives to list I see. 1) While backticks are a free syntax now, they used to be a repr() expression in older versions of Python! I'm not keen on re-using them, it'll look real weird to us old people who remember that being common. 2) As a syntax it is pretty lightweight and could be easy to overlook. I like that you thought of highlighters, but you can't depend on them to make this syntax easier to notice. Instead, it is likely that the expressions in the backticks will just blend in with the rest of the code around them. The one positive I see is that because there is no open and closing pair of backticks, like parens or brackets, you can't easily nest this syntax and I actually like how it inherently discourages or makes that impossible! I can't say if I'm -0 or +0 but it is one of those.
-- CALVIN SPEALMAN SENIOR QUALITY ENGINEER cspealma@redhat.com M: +1.336.210.5107 <https://red.ht/sig> TRIED. TESTED. TRUSTED. <https://redhat.com/trusted>

Calvin Spealman wrote:
Perhaps surprisingly, the backtick syntax in Python 2 actually is nestable, despite beginning and ending with the same character. Python 2.7 (r27:82500, Oct 15 2010, 21:14:33) [GCC 4.2.1 (Apple Inc. build 5664)] on darwin Type "help", "copyright", "credits" or "license" for more information.
`'a'+`2+3`` "'a5'"
-- Greg

Later today I will send a working implementation of backtick expressions as a function call.

Backtick expressions work exactly like lambdas, except that they are bound to the instance they are created in every time that class is used to create one.
I would if possible very much like to see some real world examples of Python code, that would benefit by being rewritten to use the new syntax. I'm particularly interested in examples that were written before this idea was posted to this list. And extra points for links with line numbers to Python code in a public git repository. -- Jonathan

On Mon, Jan 21, 2019 at 8:47 AM Jonathan Fine <jfine2358@gmail.com> wrote:
This has come up a few times before on Python-Ideas. Here are a few examples from 2015 (but please read the full thread for discussion): https://mail.python.org/pipermail/python-ideas/2015-March/032758.html https://mail.python.org/pipermail/python-ideas/2015-March/032822.html

Jonathan Fine writes:
Note: the usual way of doing this is to find examples in the standard library. It's not perfect, but the stdlib is generally pretty good code to start with, and is written with a fairly consistent style. Yes, those two features makes finding syntax that makes the stdlib more readable a pretty high barrier. Steve -- Associate Professor Division of Policy and Planning Science http://turnbull.sk.tsukuba.ac.jp/ Faculty of Systems and Information Email: turnbull@sk.tsukuba.ac.jp University of Tsukuba Tel: 029-853-5175 Tennodai 1-1-1, Tsukuba 305-8573 JAPAN

Going back to the original post: On Sun, Jan 20, 2019 at 6:43 PM James Lu <jamtlu@gmail.com> wrote:
?!? bound every time that instance is used to create one -- I have no idea what that means, or why it's useful. As I understand it, Python has expressions and statements -- expressions evaluate to a value that can be referenced in various ways, statements can do other stuff. A lambda is an expression -- it evaluates to an function object, which can then be bound to a name, stored in a container, passed to another function, etc. Is this proposal somehow different? does a backtick expression evaluate to a function? It seems from other parts of this thread that is does somethign different with namespaces and globals and locals, or ??? To illustrate, this “percent” property is bound to the instance, not to the
I do not understand this -- "property" is a decorator, or even more generally, a class which, when called, creates a "property" instance, that follows the descriptor protocol: https://docs.python.org/3/howto/descriptor.html As it is currently written, property expects a "getter" method as it's first argument. So the above would be written as: @property def percent(self): return self.v * self.v2/100 or, without the decoration: def percent(self): return self.v * self.v2/100 percent = property(percent) or, with a lambda: percent = property(lambda self: self.v * self.v2 / 100)) In any case, property expects a callable as it's first argument that will take an instance as its first argument. So there is no place for "binding to an instance". As far as I can tell, other than saving the six characters of lambda, the other thing that this does is provide some implicit argument calling -- how does that work exactly? in the above example, remember that "self" is a convention, so your backtick example could jsut as easily be: class Example: percent = property(`thing.v * thing.v2 / 100`) so when the function object is created, how does it know what arguments it takes? In this case, there is only one name used in the expression, so I guess we could assume that that's an argument, but what if it were: class Example: percent = property(`thing.v * thing.v2 / x`) Now we have both "self" and "x" as names to be resolved -- and the function will be called with an instance as the first argument -- so is that first argument "thing" or "x", and what namespace is the other one to be looked for? You do try to explain this here: Any variable names that exist when the backtick expression is created are
bound to the expression,
what is "the expression"? -- as a rule expressions can't be bound to. and the reference to the expression is stored within the expression. it sure sounds like you are using the same work in two ways here...
Names that do not exist when the expression is created must be passed in as parameters.
so if I have a block of code like: a = 5 bt = `a + b` Then bt will be a function that takes one positional argument, so this is the equivalent of: bt = lambda b: a + b Which means that if I go and add a "b" above that line of code later on, suddenly the meaning of this expression changes? that sure seems fragile to me! or is it? bt = lambda b, a=a: a + b that is, the function gets the VALUE of a at teh time the function is created? Such names can also be passed in as keyword arguments. Backtick expressions
are created when their scope is created.
no it getting really, really confusing if there are more than a couple names involved: a = 5 b = 7 bt = `c * d / a + b *e *f / d * f` so c, d, e, and f are not defined, so the function needs 4 parameters? are they positional? in what order? if keyword, what is the default? This seems like a LOT of magic -- and for what? just to save typing? -CHB Use Cases
We're really going to need to examples for these -- I can imagine examples where the code would be shorter, but NOT more readable! -CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

On Sun, Jan 20, 2019 at 6:43 PM James Lu <jamtlu@gmail.com> wrote:
First, if there is a useful procedure I am strongly against using backticks because (1) it's been used in the past with an entirely different meaning and (2) it looks ugly and is not visually suggestive at all of what it does, especially not the subtle difference between other function definitions. Second, I don't understand exactly what this difference or why it would be useful. It would help for you to give examples comparing lambda and this variation. Third, you mention using ^ in "explicit" expressions to refer to parameters of the "created function" and I do not know what function you are referring to or what the exact semantics of this are. Again, a comparison of two expressions with and without that ^ would help. An expression is not a function and not all expressions are written inside functions. (And as to the specific proposed syntax, there already is the ^ xor operator and the most expected meaning of ^value is ~value. just as the unary + and - operators corresponds to the binary operators. The only thing that I can think of is that you want `foo + ^bar` to be another way of writing lambda bar: foo + bar with some under-specified behavior for evaluating foo and different under-specified behavior for evaluating bar . Finally, if there is some other useful semantics for references inside a function definition, then I would think the best way to do that is to implement that, not add a new function difference. For example, lambda foo: foo + $bar def sample(foo): return foo + $foo where I'm arbitrarily using $ to represent the new semantics whatever they are (no point in bikeshedding syntax when semantics are yet to be defined). --- Bruce

Backtick expressions (now) use the same scoping and same binding rules as other functions. The only difference is that class Class: stacticmethod = `...` staticmethod = lambda: ... def instancemethod = `...` # an instancemethod that's called with self passed in def property = property(`...`) # an instancemethod that's called with self passed in
That is what `lambda bar: foo + ^bar` means. A caret in a backtick expression indicates that the name after the caret is a parameter. All names with the same name must have a caret before them. Mandatory parameters can be passed in as keyword arguments or as positional ones. As for the under-specification, I've been working on an example implementation I'll send soon for backtick expressions. I've also been doing the "look for use cases in stdlib" thing that Johnathan and Steve mentioned. On Wed, Jan 23, 2019 at 3:02 AM Bruce Leban <bruce@leban.us> wrote:

Now you have a lambda inside a back tick expression?!?
So are all the ^ names positional parameters? And only them? And are they on the order they first appear in the expression? How about when there are parentheses, so the order evaluated my not be the same as the order written? Anyway, IIUC, this is a way to write a lambda with less typing, and harder to read. And fewer features— any way to do keyword (with default) parameters? “Explicit is bettter than implicit” So -1 from me. -CHB
-- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

On Wed, Jan 23, 2019 at 1:07 PM James Lu <jamtlu@gmail.com> wrote:
Backtick expressions (now) use the same scoping and same binding rules as other functions.
What do you mean by "now"?? There are no backtick expressions in Python anymore and they were never functions.
You seem to be inventing new syntax as you go. And you haven't told us how the first two above differ.
I have no idea what this means. You're giving syntax without semantics. The word "that" in your sentence is an unbound reference.
In a word, ick. So to find the parameters for a function, I need to scan through the entire text of the function looking for ^? And you think that's an improvement? You've given no explanation of how this is better and saving typing the word lambda isn't enough. All in all this sounds like "but these go to 11" justification and that really is insufficient. I'm -11. --- Bruce
participants (10)
-
Barry Scott
-
Bruce Leban
-
Calvin Spealman
-
Christopher Barker
-
Greg Ewing
-
James Lu
-
Jonathan Fine
-
Stephan Hoyer
-
Stephen J. Turnbull
-
Steven D'Aprano