Modern language design survey for "assign and compare" statements

Background: While the previous discussions about assignment-expressions (PEP 572) (abbreviated AE below) have been raging one thing that was noticeable is that folks have been looking back to C for a solution. But how are newer languages solving the problem today? Believe Ryan brought this up first on the list, but it had been in the back of my mind as well. Finally have compiled my research, corrections welcome. In alphabetical order: Dart, 2011: Design goal: Less confusing, conservative replacement for JavaScript. - Assignment is a statement, not an expression. - JS allows AE to used freely, Dart doesn't support them. - Only option is to write simple, mildly redundant code. Details: https://www.dartlang.org/guides/language/language-tour#assignment-operators https://github.com/dart-lang/sdk/issues/55 Golang, 2009: Design goal: Simple, maintainable replacement for C++/Java/C#. - Assignment is a statement, not an expression. - Precursors allowed AE to be used freely - Golang doesn't them but allows assignment inside the if statement: if x := f(); x < y { … } - No assignment in while (spelled for) currently allowed. Use mildly redundant code in other locations. Details: https://stackoverflow.com/questions/13127929/assign-and-compare-in-gos-while... https://clipperhouse.com/statements-are-statements-and-expressions-are-expre... Kotlin, 2011: Design goal: Terse, modern replacement for Java with high interop. - Assignment is a statement, not an expression. - Java allows AE to used freely, Kotlin doesn't support them. - Main option is to write simple, mildly redundant code. (Or use std lib functionality such as forEachLine, etc.) Details: https://blog.kotlin-academy.com/kotlin-programmer-dictionary-statement-vs-ex... https://discuss.kotlinlang.org/t/assignment-not-allow-in-while-expression/33... https://stackoverflow.com/questions/41537638/assignment-not-allowed-in-while... Rust, 2010: Design goal: Safe replacement for C/C++, etc. - Assignment is a statement, not an expression. - C/C++ allow AE - Rust doesn't, but allows an assignment clause in if/while. if let Some(3) = some_u8_value { … } while let Some(byte) = input.next() { … } Details: https://doc.rust-lang.org/book/second-edition/ch06-03-if-let.html https://doc.rust-lang.org/stable/rust-by-example/flow_control/while_let.html Swift, 2014: Design goal: Modern replacement for ObjectiveC with high interop. - Assignment returns Void - ObjC allows AE - Swift doesn't, but allows an assignment clause in if/while statements: if let NAME = … { … } if var NAME = … { … } while let line = aStreamReader.nextLine() { … } Details: https://stackoverflow.com/questions/34173084/what-was-the-reason-for-swift-a... https://developer.apple.com/library/content/documentation/Swift/Conceptual/S... Conclusions ------------ It appears assignment expressions are no longer the favored solution for the "assign and compare" use case. Not one of these five newer languages supports them fully, as the language generations from C to C# did. Of those that have recognized the use case to be large enough—the solution has been rather more limited to the "if" and "while" statements only. Several folks here have expressed the same desire to confine AE there. Since Python's design goals are similar—to be safe and maintainable I'd recommend a similar strategy, with the addition of the list comprehension case. Going back to the C-style solution seems like the wrong direction. Since that would mean this special assignment functionality is not allowed to be used everywhere, it alleviates the pressure to make it fit into with/import/except statements. Furthermore, that frees up the keyword "as" again, which is Pythonic, short, memorable and has a history of being used for various assignment purposes, not to mention a prominent feature of SQL. In short, extend the "if/elif", "while", and comprehension to: if pattern.search(data) as match: … while read_next_item() as value: … May be best to disallow multiple assignment/conditions for now, but up for discussion. That leaves comprehensions, which could support a EXPR as NAME target also: filtered_data = [f(x) as y, x/y for x in data] or perhaps reuse of the if…as statement to keep it simpler: filtered_data = [y, x/y for x in data if f(x) as y] That variant might need an extra test if f(x) returns a falsey value, perhaps "is not None" on the end. Thoughts? -Mike

On Sat, May 19, 2018 at 10:54 AM, Mike Miller <python-ideas@mgmiller.net> wrote:
The bit that you tag on as an afterthought is actually critically important here. You have two options: 1) The 'as' is part of the syntax of the 'if' and 'while' statements; or 2) The 'as' is part of the definition of an expression. The first case would be grammar like this: if_stmt: 'if' test ['as' NAME] ':' suite ('elif' test ':' suite)* ['else' ':' suite] The second is permitting 'as' name-bindings in arbitrary expressions, but then saying "except that they're only allowed in 'if' statements". As you've noted, the first one isn't sufficient. You can't use the restricted syntax for more than a small handful of conditions (including re.match(), but not including anything that might return None and might return other falsey values). So if this is valid: if (f(x) as y) is not None: then why isn't this valid: print(f(x) as y) or this: f(x) as y # identical to "y = f(x)" or even this: with (f(x) as y): ? If you say they should all be valid, have a read of the PEP - the consequences are pretty dangerous, especially the last one. If not, then where do you draw the line, and which consequences do you want to take? The 'as' syntax has been hammered out in great detail and is no longer recommended due to its negative interactions with existing constructs. ChrisA

Chris Angelico wrote:
The 'as' syntax has been hammered out in great detail and is no longer recommended due to its negative interactions with existing constructs.
Allowing it in arbitrary expressions has been ruled out on the grounds that the difference between "with x as y:" and "with (x as y):" would be too subtle. But that argument doesn't apply if "as" becomes part of the syntax of "if" and "while". Do we think that's a bad idea as well? From the survey of other modern languages that was just posted, it seems we'd be in good company if adopted something like that. -- Greg

On 2018-05-18 19:14, Chris Angelico wrote:
Yes, largely because it's insufficient for all but a small handful of situations. I mentioned that in the previous email.
I'd argue that they are the bulk of occurrences, that's why they've been chosen as the compromise in those languages. Once there is an assignment and two compares, the time to break up the statement is approaching quickly, no? -Mike

On Sun, May 20, 2018 at 8:56 AM, Mike Miller <python-ideas@mgmiller.net> wrote:
But you can't put a comparison after the assignment, if it's part of the syntax of the 'if' statement. That's not how grammar works. So you have two options: either the ONLY thing you can capture is the condition value (which you already know to be truthy), or it's part of a more general expression, and is independent of if/while. You said yourself that the first one isn't enough. So it HAS to be an expression feature, not a statement feature. ChrisA

On 2018-05-19 16:00, Chris Angelico wrote:
Sorry, may have not written clearly. But the gist of the thread is several languages found the simple form (one assignment and compare) adequate. Believe the examples we've looked at in the various threads have been the simple form. So I argue it is enough in most cases. -Mike

On Sat, 19 May 2018, Greg Ewing wrote:
I don't quite get what's so subtle about it, am I missing something? The "with" keyword calls "__enter__", and "as" gives it a name. Just like "-x + y" is different from "-(x + y)", each part does it's thing, and you control the order with parens. /Paul

Paul Svensson wrote:
I think the difference is that mentally one already tends to think of "with x as y" being grouped "with (x as y)" rather than "(with x) as y". So, if "x as y" becomes a legal expression all by itself, it will *seem* as though the meaning of "as" is changed simply by adding explicit parentheses around an expression that was already implicitly parenthesised. Whether this is too subtle or not is a matter of opinion, but it was raised as one of the objections to using "as", so some people obviously thought so. -- Greg

On Sat, May 19, 2018 at 4:12 PM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
The reason it's dangerously subtle is that this will work: with (open("filename") as f): And this will work: with ( open("filename") as f ): And this will fail: with (threading.Lock() as lock): Do you know why? Do you want to try to debug that? I can guarantee you that a lot of people will go "hey, cool, now I can use parentheses in 'with' statements to wrap them across lines", and will do so very happily for a long time, wrapping their file-open blocks without an issue. But the semantics have changed *and they will not even know*. The current meaning of "as" is never "bind the expression on the left to the target on the right". Oh and here's another one: Should the target be required to be a name, or should it be allowed to be more complicated (eg "blah blah as self.spam")? Because Python isn't consistent on that point. Do you know which uses of "as" allow other targets than names and which don't? Again, do you want to have to debug situations where parentheses don't seem to matter for simple names, but with another kind of target, they're mandatory / forbidden? (Depending on which way the semantics are defined.) Using "as" in this way will NOT be consistent with the rest of Python, and it will introduce many subtleties for both the compiler and for humans. ChrisA

On Sat, May 19, 2018 at 02:11:38PM +1200, Greg Ewing wrote:
Yes. What is so special about "if" and "while" that they and they alone can make use of assignment expressions? The `and` and `or` short-cut operators are only useful in `if` and `while` statements, so we ought to prohibit them outside of `if` and `while` statements. Do you agree? Either assignment expressions are useful, and they should be allowed anywhere an expression is allowed, or they aren't, and they shouldn't be allowed at all. Breaking the rules for a special case is rarely the right decision.
/s/good/bad/ Compared to the Zen of Python, Go seems to break all the rules. Or at least most of them -- the beauty of the Zen is that it is open to interpetation :-) Go doesn't have exceptions. It requires the use of the tedious old anti-pattern of flag, result = function() if flag: do what you want else: # Now how the hell do I handle this? as a fundamental design principle, apparently because ugly and tedious boilerplate is a good thing, giving us all the verbosity of EAFP at its worst without any of the benefits. Similarly, its 2018 and the designers of Go turned their back on about 4/5th of the world, insisting that strings are ASCII and Unicode is an afterthought. I'm sure Rob Pike has his reasons for prohibiting exceptions, and network engineers probably think text is just another data format. Go might be an awesome language for what it is designed to do, but Forth is an awesome language for what it does best too, and we don't copy Forth's way of doing things. Before we copy Go, whether we copy a feature or a lack of feature, we ought to carefully consider how well that fits Python. (Sorry to just pick on Go, but it is the one I'm most familiar with.) -- Steve

On 2018-05-18 17:54, Mike Miller wrote:
Thanks, it is really great to see that comparison. That definitely pushes me even more towards disliking the assignment-expression proposals and preferring a more specific change to the syntax of particular constructs (if, while, maybe comprehensions) --- or no change at all. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

Thanks for writing up the summary, but you have picked a very narrow subset of new languages. One might even say a biased subset. How about these new languages? Elixir, Elm, TypeScript, Hack, Julia, Perl6, Ring, LiveScript, Ballerina, Crystal, Opa, Red, Ceylon TL;DR At least 10 out of these 13 include some form of assignment expressions. If we include the five you selected, 13 out of 18, or 72% of the sample, include some form of assignment expression. Details follow below. Even in the limited set of five languages you chose, 3 out of the 5 allow some form of assignment expressions: - Go allows assignment expressions in "if" statements, but not "while"; - Rust allows assignment expressions in both "if" and "while"; - as does Swift; - Swift also allows assignment in arbitrary expressions, but it returns Void (equivalent to returning None). As for the other languages I found: Elixir (2011): - everything is an expression, including assignment if current_user = Plug.Conn.get_session(conn, :current_user) do Logger.info "User #{current_user.id} logged out" end https://stackoverflow.com/questions/42682961/assign-variable-and-test-in-if-... Elm (2012) - has "let expressions" http://elm-lang.org/docs/syntax#let-expressions TypeScript (2012) - a strict superset of Javascript, including assignment expressions Hack (2014) - a (not quite full) superset of PHP, including assignment expressions Julia (2012) - includes assignment expressions julia> x = 1 1 julia> y = (x = 2) + 1 3 julia> x 2 Perl 6 a.k.a. Rakudo (2015) - assignment is an expression: steve@orac ~ $ perl6 -e "say (my \$x = 32)+100; say \$x" 132 32 (the backslashes are escaping the dollar signs from the shell, they're not part of the Perl syntax) - also allows any(?) statement to be used as an expression with the `do` prefix Ring (2016) Documentation seems fairly poor, more concerned with singing the praises of the language than explaining the behaviour in detail, but maybe that's just me. But I *think* assignment is not an expression. Using the Ring interpreter here: http://ring-lang.net/ I tried running this: x = 55 x = (x=99) see x and got the output 0. My interpretation of this is that the x=99 expression is being interpreted as equals, not assignment. LiveScript (2011) - assignment is an expression which returns the value assigned http://livescript.net/#assignment Ballerina (2017) - as far as I can tell, assignment is purely a statement https://ballerina.io/res/Ballerina-Language-Specification-WD-2015-05-01.pdf Crystal (2014) - assignment is an expression which returns the value assigned I don't know if this link will work: https://play.crystal-lang.org/#/r/43fq but if not, try running this: x = 99; y = (x = 33) + 1; print x; print y; and the output ought to be 3334. Opa (2011) - assignment is an expression; the following two declarations are equivalent: two = { one = 1 // semicolon and newline are equivalent one + one } two = { one = 1; one + one // the exact same thing as above } https://github.com/MLstate/opalang/wiki/The-core-language Red (2011) (not to be confused with the US DOD "RED" language from 1979). I can't get the documentation to Red to display in a readable form in my browser, but it claims to be nearly identical to Rebol. Wikipedia describes Rebol (and presumably Red) as not having either expressions or statements in usual sense. Based on my reading, it is kinda-sorta like Forth except without the stack or the postfix syntax. The creator of Red describes it: "About the syntax and semantics, in a nutshell, it's a Lisp without parentheses and with infix operator support." which suggests that assignment could be an expression. There's also a "set" function which can assign values, but frankly the Rebol programming model confuses me and so I'll count this as a "Possibly, but I can't tell for sure so let's say No" for the question of assignment expressions. Ceylon (2011) - Ceylon has "let" assignment expressions. https://ceylon-lang.org/documentation/1.3/tour/attributes-control-structures... - while loops support assignment https://ceylon-lang.org/documentation/1.3/tour/attributes-control-structures... - and switch statements also support a named assignment https://ceylon-lang.org/documentation/1.3/tour/attributes-control-structures... -- Steve

On 2018-05-19 06:41, Steven D'Aprano wrote:
Certainly. I chose basically on whether it was well used (popular), I'd heard about it, and it was easy to find documentation/discussion on. Then I ran out of energy around number six, haha. But I think this is representative of industry trends. -Mike

Also meant to mention, I didn't pick them specifically in advance to match a goal. I was mildly surprised at their similar design on that front. -Mike

On 2018-05-19 06:41, Steven D'Aprano wrote:
Overall your list is fair, and you're right that more languages could be considered. However, you also argued elsewhere in this thread that Go had an unpythonic design so we shouldn't put too much weight on what it does. Given that, I think it's fair to add that, in my opinion, JavaScript and PHP are abysmally designed languages, so we should put little weight on any precedent they set. What would be nice is to know how much people like these various languages, and how productive people are with these languages, and then correlate that with their various features. However, as discussed on another thread, getting hard data about such matters is not easy. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

On 2018-05-19 06:41, Steven D'Aprano wrote:
Details follow below.
Thanks for this, had some more time to read it more closely. Correct me if I'm probably wrong, but most of these are not used by many. Except perhaps: - Typescript, which is constrained by a compatibility goal with JavaScript. - Julia, - Possibly Elixir, have heard good things about it. My focus was on "industry standard" languages however, not pushing the envelope too far but rather on humdrum bug reduction and maintainability, an area Python aspires to, I believe. There was a stack overflow survey recently, should probably dig that up. -Mike

On Sat, May 19, 2018 at 04:28:10PM -0700, Mike Miller wrote:
According to the latest survey on TIOBE (May 2018), *none* of the languages either of us surveyed are used by more than a small niche. I realise that the TIOBE ranking is not the only available ranking, nor is their methodology necessarily the best, but if other people want to look at other language surveys they are free to do so. According to TIOBE, the rankings of your five languages are: Go #14 (up two places) 0.970% Swift #19 (down six places) 0.907% Dart #26 0.859% Kotlin #49 0.292% Rust #51-100 (percentages too small to differentiate) So even the most popular of your languages are still very niche. Go and Swift have a lot of industry "buzz" about them, but that's not translating to jobs or Google searches yet. The thirteen languages I surveyed are in a similar position: the highest ranked of them is Julia at #46. (I don't think TIOBE distinguishes between Perl at #18 and Perl 6.) Almost by definition, any new language is going to only be used by a small subset of programmers. [...]
My focus was on "industry standard" languages however,
I'm sorry, but no it wasn't. If we want *industry standard* languages, none of them are going to be *new*, and we need to look at those at the top of the TIOBE rankings: Java C C++ Python C# VB .Net PHP Javascript SQL Ruby R Delphi/Object Pascal Go and Swift are backed by large comporations and may some day be "industry standard", but they aren't yet. [Aside: it is sobering to realise that according to at least one objective ranking, "ancient and obsolete" Pascal is still more popular than "cool" new languages like Go and Swift.] I've somewhat arbitrarily cut the list off at "languages ranked above 1% on TIOBE", but we have to cut the list of somewhere. And of course in certain specific industries the standard languages may be very different, e.g. there are still tens of millions of lines of COBOL code being maintained in the banking industry. Out of those industry standard languages (as ranked by TIOBE, other methodology may result in other rankings) we find: 8/12 have some form of assignment expressions; (Java, C, C++, C#, PHP, Javascript, Ruby, R) 4/12 do not (Python, VB .Net, SQL, Delphi). -- Steve

On Sun, May 20, 2018 at 11:43 AM, Steven D'Aprano <steve@pearwood.info> wrote:
SQL isn't really comparable here. To figure out whether Python is in better company *with* or *without* assignment expressions, we need to compare with languages that have a concept of "assignment" and "expression". SQL itself most certainly has expressions, but it doesn't have assignment per se. There are named sub-expressions in SELECT statements, but that's more of a temporary view on a table than anything like a Python variable / name binding; I don't think standard SQL has any way to fetch up a scalar value and then reuse it, other than burying it in a table and selecting twice from that table. Some database engines have an SQL-based procedural language, but it's non-standard. FWIW, PostgreSQL's "PL/pgSQL" has assignment, but it is a statement and not an expression, probably because its expression evaluator is defined in terms of SQL's SELECT statement. So there are really eleven: eight that do, two that don't, and one that currently doesn't, but its BDFL is discussing the possibility of adding it. ChrisA

[Steven D'Aprano <steve@pearwood.info>]
I was an early REBOL user, and my head still hurts ;-) It was ... different, for sure. The syntax is in some sense extremely simple, hoping to make it easy to define problem-specific layers of syntax on top of it. But I gave up before they got that far. In any case, everything is "an expression" there, including assignment. That's spelled: WORD ":" WHITESPACE+ EXPRESSION although that's lying a bit. Here from a Red shell:
That last one shows one of the challenges for people coming from "normal" languages: this does the same:
(x: 12) (print (add (y: 88) x)) 100
showing that it really is a lot like "Lisp without parentheses". Without the parens, it's impossible to tell where function calls begin and end without knowing which names denote functions and how many arguments each function requires! That's "a feature", they insist ;-) To be fair, you really do get used to it quickly for the heavily used builtin functions. One more:
Yup! "i1+2=3*88" is a variable name there :-) Short course: definitely "yes" on assignment expressions for Red. But I'm going to out on a limb and guess that the typical Python programmer wouldn't find "but Red does it!" persuasive ;-)

On Sun, May 20, 2018 at 12:21:16AM -0500, Tim Peters wrote:
Yeah, to me it looks more like a prefix version of Forth than Lisp. Complete with "anything can be a name":
Yup! "i1+2=3*88" is a variable name there :-)
but maybe when languages are weird enough they all look the same :-)
True :-) Thanks for doing the testing. -- Steve

On Sun, May 20, 2018 at 1:35 AM, Steven D'Aprano <steve@pearwood.info> wrote:
but maybe when languages are weird enough they all look the same :-)
https://www.dreamsongs.com/WorseIsBetter.html Look at what happened with PERL... IMPORTANT NOTE: Enabling "as" in "if" and "while" doesn't have to be at the expense of some form of assignment expression. We've been having this discussion as if has to be either one or the other, and there's no reason we can't have both (as mentioned before, "reduce()" is live and well in the libraries, for people who need to use it). I in particular would not like it at all if something like ":=" was at the expense of "as", and I think that others on the side of expanding "if" and "while" may feel the same way. As a reminder, some of the arguments in favor of "as" are around the patterns exemplified by the "re" module; patterns which seem correct and useful, and that are used by other standard and 3rd-party modules. if os.fork() as child_pid: parent(child_pid) else: child() Cheers! -- Juancarlo *Añez*

On Mon, May 21, 2018 at 1:19 AM, Juancarlo Añez <apalala@gmail.com> wrote:
Let's suppose that the := syntax already existed in Python - that you could write this: if child_pid := os.fork(): parent(child_pid) else: child() What would be the benefit of adding the "as child_pid" syntax? The full assignment expression syntax completely covers all the jobs that you can do with a capturing if statement. ChrisA

The "as" syntax over "if" and "while" may go in with all the restrictions and caveats already discussed. The discussion about the semantics of how ":=" should affect the enclosing context when used in generators, etc. may continue for as long as it must. And there are some of us who like the consistency of "us", and would shy away from ":=". For example, Should this be valid? if child_pid := os.fork(): parent(child_pid) else: child() print(child_pid) This shouldn't be: if os.fork() as child_pid: parent(child_pid) else: child() print(child_pid) # child_pid is undefined -- Juancarlo *Añez*

print(child_pid) # child_pid is undefined
Why on earth would it be undefined?
Indeed, users would expect the new uses of "as" to behave as the previous ones. The problem is that "with" and "except" do things differently: In [*1*]: *import* *os* In [*2*]: *with* open(os.path.expanduser('~/tmp/xx')) *as* f: ...: *pass* ...: In [*3*]: print(f) <_io.TextIOWrapper name='/Users/apalala/tmp/xx' mode='r' encoding='UTF-8'> In [*4*]: *try*: ...: print('ok') ...: *raise* *Exception*() ...: *except* *Exception* *as* e: ...: *pass* ...: In [*5*]: print(e) --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-5-bb2ea196f768> in <module>() ----> 1 print(e) NameError: name 'e' is not defined
Anyway, if you want to propose an alternative to PEP 572, you ought to write your own competing PEP.
I don't take that as a challenge. It would be good to have different, maybe somewhat competing PEPs. It's been done before. -- Juancarlo *Añez*

[Tim]
I was an early REBOL user, and my head still hurts ;-) It was ... different, for sure.
[Steven D'Aprano <steve@pearwood.info>]
Yeah, to me it looks more like a prefix version of Forth than Lisp. Complete with "anything can be a name":
The example I gave just strung "words" together, but just as fundamental is the notion of "block": a series of words and/or blocks enclosed in square brackets. That's akin to Lisp's "S expressions", but Rebol/Red _don't_ "evaluate" blocks by default. Blocks can hold data or code, or a mix of both, and the syntax doesn't care. That's the real reason "(almost) anything can be a name (word)". Here I'll build to a simple example where _some_ Pythoneers will experience envy rather than revulsion ;-)
[x y] == [x y]
The block isn't evaluated. Depending on how it's _used_, it may be treated as data (perhaps you want to think of it as being a sequence of two symbols, or strings). If you evaluate it, it blows up (because I don't happen to have any variables with those names defined):
do [x y] *** Script Error: x has no value
Give the names some values, and _then_ it can be evaluated; and evaluating a block returns the value of the last expression in the block:
If you want a block with all the expressions' values, use `reduce` instead:
reduce [x y] == [12 13]
Code? Data? No difference. Here's the part where some Pythoneers will get jealous:
`func` is just another function that happens to build an anonymous function. It takes two blocks as arguments: a block containing the formal argument names, and a block with the code to execute. Both blocks are essentially "data" to `func`. It doesn't look much like Forth anymore ;-) Note that the following does exactly the same:
In practice, slinging Rebol/Red most feels like working in a functional language. One of their real goals, though, is to enable writing functions that can be called like so: schedule "Change furnace filter" [ starting today then every 3 months ] There's an elaborate `parse` function built in that supports many ways of applying user-supplied rules to parse blocks like the one in that example. Of course you can emulate much the same in Python by, e.g., passing triple-quoted strings instead. In that specific example. Rebol/Red provide a minimum of syntax shared by all such applications, and when the sub-language gets elaborate enough that _nesting_ blocks makes good sense, nesting triple-quoted strings sucks ;-) All languages have some good things going for them. But I'm not sure I've ever seen Rebol/Red code that _used_ the value of an assignment expression; that it has them at all seems much more to follow from that everything is an expression. As in most functional languages, if you want initialized local variables, you're more likely to invoke an anonymous spelled-inline function instead. Which is what, e.g., Haskell's "let PILE_OF_BINDINGS in EXPRESSION" is syntactic sugar for doing.

[Tim, on Rebol/Red]
[Greg Ewing <greg.ewing@canterbury.ac.nz>]
How does scoping work? If you pass a block to a function which evaluates it, how are names in the block resolved?
Too involved, but basically a form of lexical scoping. Rebol struggled with this, and details vary by version. Here are the last docs for version 2: http://www.rebol.com/r3/docs/concepts/funcs-scope.html Details changed again for version 3. Best I can tell, Red hasn't written up its own rules yet. The model did _not_ support closures naturally (in the sense of local bindings persisting beyond a function's return). Version 3 added an alternative to `func`, named `closure`, which could be used when you wanted a closure: http://www.rebol.com/r3/docs/datatypes/closure.html But I noticed just now that Red doesn't have `closure` built in, and its funcs don't support closures naturally either:
adder: func [x] [func [y] [x + y]] == func [x][func [y] [x + y]]
add3: adder 3 == func [y][x + y]
That's not dynamic scoping, either - it's still unhappy if `x` is defined at top level:

You seem determined to throw out requirements when it suits. One of which was for newer languages. While this thread's original post was not so specific on that, in a previous post (I believe you read) defined that as created in the "last decade or so". The ones created to address shortcomings in the previous generation. The point being to find momentum in design of newer languages. Your list of older languages is therefore not pertinent. -Mike On 2018-05-19 18:43, Steven D'Aprano wrote:

On Sun, May 20, 2018 at 10:57:39AM -0700, Mike Miller wrote:
You seem determined to throw out requirements when it suits. One of which was for newer languages.
YOU are the one who changed the requirements: first you said "newer languages", then you changed it to industry standard. I showed that a broad sample of new languages by far favours assignment expressions (14 out of 18 including Red, which I wrongly guessed didn't) and even the narrow subset that you wrote up included some form of assignment expressions in 3 out of the 5 cases. So you shifted the goal posts from "newer languages" to "industry standard". That was YOUR choice, not mine. Based on the TIOBE language popularity rankings, there is no overlap between "industry standard" and "newer" languages. Not even Go or Swift. Delphi/Pascal is more popular than Go, unless you happen to work for Google. If you disagree about using TIOBE, feel free to choose your own objective and neutral ranking system, so long as it is better than "I've heard of these five languages, so they're industry standard". In 2016 Liefehacker came up with this list, based on combining rankings from TIOBE, PYPL and various job sites: Java, C, Python, C++, JavaScript, C#, PHP, Swift, Objective-C, R https://www.lifehacker.com.au/2016/12/the-most-popular-programming-languages... only one of which is in your list of new languages, and that one (Swift) is one of those which includes assignment as an expression. Looking at job rankings: https://www.codingdojo.com/blog/9-most-in-demand-programming-languages-of-20... https://www.codingdojo.com/blog/7-most-in-demand-programming-languages-of-20... and you won't find any new languages. [...]
The point being to find momentum in design of newer languages. Your list of older languages is therefore not pertinent.
Then you shouldn't have raised "industry standard" as relevant. If we limit the list to new AND "industry buzz", then we get a sample space of *two*: Swift and Go, both of which have some form of assignment expressions: - Swift has assignment expressions; - Go allows assignment in if statements, but not while statements. That's not a big sample to go by, and it is subject to Rob Pike's idiosyncratic design (not necessary bad, but bucking language trends). -- Steve

To clarify there were three main criteria, and one minor. Newer, popular/becoming industry standard, and designed to address shortcomings in previous generations. Finally, the limit of my energy when already working on a project. I also should have provided the link to the previous discussion. When writing the first message I indeed thought a wiki might be better, because I'd undoubtedly forget to mention, or inaccurately state something. Meta discussion: While I do prefer the "as" version of assignment, I didn't enter into the inquiry to prove it better, but rather to find industry momentum, and had only some minor exposure to go/kotlin via their tutorials beforehand. I did a similar thing when "f-string" design was debated. At first I wasn't in favor of them handling expressions, but after doing a survey of other modern/popular languages found it was working well elsewhere and changed my opinion. So it goes both ways, and I'm open to being convinced against my first preference and not heavily invested in it. -Mike

On Tue, May 22, 2018 at 2:43 AM, Mike Miller <python-ideas@mgmiller.net> wrote:
Note how not one of your criteria says that the language has to have made the right choice - only that it's made a DIFFERENT choice to languages in a previous generation. So you're heavily selecting in favour of languages that say "hey look, we know better than everyone else does", without actually proving that it's better. There's no shortage of language designers who say "oh look how terrible C is", and when a language designer has a lot of clout behind him (like "apps for this new mobile phone should be written using this language"), it can get extremely popular among people who don't actually have much choice of language - and certainly don't have the option to use C. Much more useful would be to look at languages that (a) work in a field where programmers have ample freedom to choose between languages, and (b) have been around long enough to actually demonstrate that people want to use them. Look through the Stack Overflow Developer Survey's report on languages: https://insights.stackoverflow.com/survey/2018/#most-loved-dreaded-and-wante... A "Wanted" language is one that many developers say "I don't currently use, but I would like to". (It may also be a language that has murdered semicolons. I believe the bounty on JavaScript's head is quite high now.) Go through that list and you'll get an idea of what people wish they could use; then toss out anything that hasn't been around for at least 10 years, because there's a tendency for new technologies to be over-represented in a "Wanted" listing (partly because fewer programmers already know them, and partly because people want to try the latest toys). That may give you a better list of languages to compare against. ChrisA

Chris makes a lot of good points regarding *which* languages to look at, but it seems like that line of enquiry is unlikely to suggest anything more than it has so far, especially if we're limiting it to languages everyone has heard of. They either use a keyword, an operator, don't support the feature or they're wacky. If anything, the survey says we need to think outside the box. -- Carl Smith carl.input@gmail.com On 21 May 2018 at 20:11, Chris Angelico <rosuav@gmail.com> wrote:

If the ability to capture a subexpression is given up on, then we are are down to just capturing the value of the predicate in if statements. In loops, it is only the predicate and iteration index. If you mashup `if` and `def`, you end up with this mess: if get_something() def (value): do_something(value) And `while` and `def` is no prettier: while get_something() def (value, index): do_something(value) Both ugly, and totally misleading, but being able to accept the values as params is nice, and the parenthesis make things clearer and more Pythonic IMO, so maybe just use `as` but require the names be parenthesised: if predicate as (value): use(value) while predicate as (value, index): use(value) The next options may be a bit too functional-looking, when the suite is not a function body, but I thought they read better: while predicate pass (value, index): use(value) if predicate -> (value): use(value) while predicate yield (value, index): use(value) The main idea is to reuse comma separated params in parens to make the assignment clearer and to look more like the `def` and `class` grammar. -- Carl Smith carl.input@gmail.com On 21 May 2018 at 22:37, Carl Smith <carl.input@gmail.com> wrote:

On 2018-05-21 12:11, Chris Angelico wrote:
I'd say that also has limited usefulness. The problem is that people may "want" to learn a language for many reasons, and "the language made good design choices" is only one such reason. A lot of people may want to use JavaScript because it's hip or in demand or because they can (or think they can) make money with it. But I'm not so interested in that. What interests me is: what are the languages that people specifically believe are superior to other languages *in design*? (Even better would be what are the languages that actually ARE superior, in some reasonably nonsubjective, definable, way, but we have even less data on that.) -- 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 22, 2018 at 11:45 AM, Brendan Barnwell <brenbarn@brenbarn.net> wrote:
What you want is virtually impossible to obtain, so we go for whatever approximations we can actually get hold of. There are a few reasons someone might want to use a language. One is "I hate this language but it'll earn me money", yes, but the way the Stack Overflow survey is worded, those ones won't come up. IIRC the wording is "which languages do you desire to be using next year", after removing the ones for which you also said that you're using them now. So there are a good few reasons that you might wish you could be using a language: 1) You believe it's a good language, worth using, but just have never gotten around to starting with it 2) You think it's really fun and awesome, and wish your employer would let you use that instead of what you currently use 3) It represents a particular coding arena that you want to get into (eg you want to get into iOS development, so you pick "Swift") 4) It's an absolutely awesome language and you have plans to learn it, but haven't executed on them yet 5) It's a new language, and you want the shinies 6) Etc, etc, etc. Generally speaking, for a language to show up in the "Most Wanted", a lot of developers have to think it's something worth knowing. After cutting out the youngest languages (which I defined as "released within the last ten years") to remove their overrepresentation, you're left with languages that, in the opinions of people who don't yet program in them, are worth learning. That's far from an exact answer to the question we really want to ask, but it's reasonably concrete (to the extent that surveys ever are). But as Guido says, this is not a popular vote among languages. It's interesting to notice what other languages are doing, but harder to pin down what's good or bad about what they're doing. ChrisA

I thought this thread did a good job of establishing that looking at other languages is not going to help with introducing assignment expressions into Python. It was still interesting to read. If we can't copy from other languages (or even agree on *which* languages to copy), Python will have to do something novel or give up on this. I thought, if the thread's dead, it'd be nice to do a bit of bike-shedding on the end, but there was no appetite for that. I'm not the most sensitive guy, and don't really have a sense of how my posts are being received, but would appreciate being told if my contribution isn't especially welcome. Best, -- Carl Smith carl.input@gmail.com On 22 May 2018 at 02:58, Chris Angelico <rosuav@gmail.com> wrote:

On Mon, May 21, 2018 at 09:43:45AM -0700, Mike Miller wrote:
To take your criteria in reverse order: 1. Why should the limit on *your* energy be a deciding factor? I was willing to spend a few hours doing a more complete sample of new languages. Should we ignore those because you ran out of energy? If anyone else wants to extend it even further, and survey more languages, we should welcome an even more extensive survey. 2. By definition, EVERY new language is designed to "address shortcomings in previous generations" of languages. If the designer thought previous languages were ideal, they wouldn't invent a new language. 3. None of the languages you surveyed are "popular/becoming industry standard" according to the closest thing we have to an objective measure of popularity: rankings like those provided by TIOBE. If you don't like TIOBE's methodology, feel free to propose a different, neutral, ranking. If you want a subjective opinion based on "industry buzz", then I would say that out of the five languages you listed, only two (Go and Swift) are anything close to "becoming industry standard", a tiny sample indeed, but nevertheless one where both languages have some form of assignment expressions (Go only in "if" statements, Swift everywhere). 4. What was your criteria for "newer"? I must admit I assumed it was languages invented since 2010, but I see Go was invented in 2009. If your cutoff is 2009, then we ought to include Coffeescript, which also has assignment expressions. -- Steve

On 2018-05-21 16:40, Steven D'Aprano wrote: Consider the link Chris sent above: https://insights.stackoverflow.com/survey/2018/#most-loved-dreaded-and-wante... The top six coincide with my list, plus TypeScript (superset of JS) and Python. I'm pretty happy with those chosen, considering. -Mike

On Mon, May 21, 2018 at 7:40 PM, Steven D'Aprano <steve@pearwood.info> wrote:
What acknowledgement do you want him to make? That your list is better? I'm not sure what's the point of continuing this line of questioning. Personally, I don't think we should restrict the survey to newer languages. New changes to older languages are also relevant. C++17 is adding initialization syntax to `if`. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0305r0.html Why they're doing that, when C++ has assignment expressions, may or may not be relevant to this discussion.
It should not. But it is. He's simply admitting it.
Perhaps Mark wants to say that these languages were meant to replace particular languages. Each of his examples listed languages that they were meant to replace, and those replaced languages aren't exactly esoteric, academic, or whatever Perl is. They're languages by industry giants.
"Industry _standard_" may be the wrong term. Except for maybe Kotlin, the languages are created and backed by major players in the industry: Apple, Mozilla, and Google.
Current top 50 programming languages, filtered by year >= 2000. (Years are from quick Google and Wikipedia lookups.) 6 Visual Basic .NET 2001 14 Go 2009 19 Swift 2014 20 Scala 2004 21 Apex 2006 26 Dart 2011 31 D 2001 33 Scratch 2002 (2007) 37 Clojure 2007 41 OpenCL 2009 46 Julia 2012 49 Kotlin 2011 Possible removals: - Some might argue that Visual Basic .NET is just a rebranding of Visual Basic, an older language. - Scratch looks like it's a language for teaching, not using, and I'm not sure why it's on the list. - I don't know whether to remove Apex, since it's specific to one platform and it sounds like it's almost Java. - Similarly, OpenCL sounds like it's just a fork of C/C++ for GPUs. With those languages removed, and reverse-sorted by year: 19 Swift 2014 46 Julia 2012 26 Dart 2011 49 Kotlin 2011 14 Go 2009 37 Clojure 2007 20 Scala 2004 31 D 2001 Mark's list captures four of the first five, while Rust is nowhere to be seen. Without Julia, "publicized in the last ten years" describes the split pretty well. Clojure and Scala may be outside of Mark's experience. They are functional languages, with at least a lean toward pure functions. Clojure, being a Lisp, probably uses `let` for name-binding, and I assume you'd make a macro for assign-and-compare if you want it, so it might not be informative for Python's decision. In Scala, pretty much everything is an expression (as it is in Lisp), so it might also be uninformative . D is much older than the others, but given its design goals, it might be interesting to look at anyway. That leaves Julia.
If your cutoff is 2009, then we ought to include Coffeescript, which also has assignment expressions.
CoffeeScript, like Scala, favors making things expression, so its decision might not be informative. CoffeeScript's grammar notes say, "[E]verything that can be an expression is one." http://coffeescript.org/v2/annotated-source/grammar.html

I don't know what to do with this thread. I enjoyed reading Mike's survey of what other languages do. I also enjoyed Chris's survey of what some other languages do. Then the thread veered off into several unproductive directions at once: a mini-tutorial for Rebol (or Red?), and endless bickering about which list of languages to allow in the survey. But that's not how this stuff gets decided. It's not the case that if we find that 90% of languages surveyed don't have inline assignment that means Python should not have it either. So changing the criteria for inclusion in the survey is entirely unproductive (except in that it may create a strong bonding experience between participants in the discussion -- though in this case perhaps the forces involved may be better described as repulsion than attraction :-). *Perhaps* it would help if we could ask those languages' designers for their rationale (most of them are still alive :-). But nobody proposed that. And it probably wouldn't help either, because the answers of actual language designers for such questions are typically a mixture of strongly held opinion on issues that aren't decidable based on hard facts alone, and hard but incredibly subtle facts related to the rest of the language's design and implementation (including the state of tooling and the structure of the compiler used as a reference implementation). So let's just end this thread. -- --Guido van Rossum (python.org/~guido)

For more background, this is the thread that inspired this one: https://mail.python.org/pipermail/python-dev/2018-April/153071.html -Mike

2018-05-19 3:54 GMT+03:00 Mike Miller <python-ideas@mgmiller.net>:
Thank you Mike this is a very useful analysis. It would be also great to know when they introduced this feature - from the beginning or after a while. Your results are completely consistent with all the examples from the numerous threads - that this feature should be added only in `while` and `if` statements. [Chris] The bit that you tag on as an afterthought is actually critically
I think I have a very strong argument "why are not others valid" - Because already three months have passed and among 1300+ messages there was not a single real example where assignment expression would be convenient or useful outside `while` and `if` statements. If you have a counterargument (with an example), I would be glad to see. This thread is about the design choices which were made in other modern languages. And from Mike's analysis it is evident that other languages have chosen to allow the assignment as an expression only where it is practical - in `while` and `if` statements, or not to allow it at all. I find this at least absolutely not constructive to push `print(f(x) as y)` or `print(y:=f(x))` in every thread. Is this really the only compelling example outside `while` and `if`? Also if we consider only the usage in `while` and `if` statements, what is the point of discussing differences between `with (f(x) as y)` and `with f(x) as y`? The first one will be a ` SyntaxError`, period. Can you also explain why the assignment expression should be valid outside `while` and `if` statements? - for dummy consistency? The `as` variant was _hammered_ only in the general case, which in itself does not make sense (which is confirmed by 1300+ messages without examples). Some time ago Guido said that he is not _impressed_ with `as` variant. But I think he does not like this form because it does not play well in general case. There are several reasons why. The main one is if `as` will be accepted it will prevent the use of this form in theoretically future statements (for example match statement). Another point that it looks ugly for general case. As it seems to me, the intersection with `with`, `except` is also important but can be overcomed. The use of any system requires some study and understanding. BUT I also think that Guido will not so strongly object if only `while` and `if` statements were under consideration, because there is no other choice. Examples from other languages are very important and their choice should be taken into account. Those languages are also designed by smart guys and I'm sure that they were thinking about where the assignement expression should be allowed and 1300+ messages only confirm their choice. Therefore, it may be worthwhile to focus only on actual use cases - in `while` and `if` statements??? and stop to flood every thread with dummy examples. p.s.: I deliberately did not mention the pseudo _usefulness_ of assignment expression in generators/comprehensions because the benefits are very questionable. Therefore, to begin with, we should start with `while` and `if` statements, and possibly in the future add it to comprehensions/generators. With kind regards, -gdg

On Sun, May 20, 2018 at 4:07 AM, Kirill Balunov <kirillbalunov@gmail.com> wrote:
Okay. Let me put it to you this way, then: Write up a simple and easy-to-explain set of rules for exactly what is valid and what is not. Make an actual proposal here. Be sure to demonstrate that the new construct is useful, with examples (like you're asking me for), and be sure that you're thorough enough in your definitions that this could actually be coded. I think you'll find that 1300+ messages aren't enough to adequately define what you're asking for. Or, alternatively: Give up on the 'as' syntax, because it was dropped from the PEP for a reason. ChrisA

JavaScript and PHP are abysmally designed languages, so we should put little weight on any precedent they set.
JavaScript has issues, due to its unique history, but to dismiss the entire language as too poorly designed to take seriously... Many clever people prefer JS to Python. -- Carl Smith carl.input@gmail.com On 19 May 2018 at 19:07, Kirill Balunov <kirillbalunov@gmail.com> wrote:

On Sat, May 19, 2018, 11:07 Kirill Balunov <kirillbalunov@gmail.com> wrote:
comprehensions. I don't think you read any of the messages. about 1000 were solely about comprehensions. How about every example brought up in 1300+ messages that assignment expressions could handle that otherwise required far more code? Some including imports? Did you miss where _every_ counter example was either doing what assignment expressions do with more code, or where outright misunderstandings of how they will work? Did you read Chris' responses where he clearly refused each example? Really hard to see how you got that take away if you read those messages. Basically you are not backed up at all by citing that. assignment expressions handle many cases with ONE syntax. They are the "one way to do it" where "it" covers every example brought up so far.

On Mon, May 21, 2018, 03:58 Rhodri James <rhodri@kynesim.co.uk> wrote:
I haven't seen a convincing counter argument that doesn't axiomatically depend on personal opinion. Can you define your "shouldn't" in a way that isn't just your personal taste? I can't know from your statement here.

On 19/05/18 01:54, Mike Miller wrote:
Thanks for the analysis, but I'm afraid I must disagree with your recommendation. It was the thought I first had when Chris came out with his first draft of the PEP several months ago, but it's not enough to cope with my usual use cases. What I normally want is the Python equivalent of: while ((v = get_something()) != INCONVENIENT_SENTINEL) do_something(v); The condition expression itself is not what I want to capture; I need a subexpression, which the "as" syntax won't give me. -- Rhodri James *-* Kynesim Ltd

On 21 May 2018 at 12:05, Rhodri James <rhodri@kynesim.co.uk> wrote:
That use case should be covered by for v in iter(get_something, INCOVENIENT_SENTINEL): do_something(v) -- <https://www.machinalis.co.uk> Daniel Moisset UK COUNTRY MANAGER A: 1 Fore Street, EC2Y 9DT London <https://goo.gl/maps/pH9BBLgE8dG2> P: +44 7398 827139 <+44+7398+827139> M: dmoisset@machinalis.com <dmoisset@machinalis.com> | S: dmoisset <http://www.linkedin.com/company/456525> <http://www.twitter.com/machinalis> <http://www.facebook.com/machinalis> <https://www.instagram.com/machinalis.life/> Machinalis Limited is a company registered in England and Wales. Registered number: 10574987.

for v in iter(get_something, INCOVENIENT_SENTINEL): do_something(v)
There are many ways round my use case, all of them inelegant. That has to be one of the less comprehensible alternatives.
In for-loops (because they include an assignment already) we can improve this with more indicatively named functions in practice. -- Carl Smith carl.input@gmail.com On 21 May 2018 at 14:14, Rhodri James <rhodri@kynesim.co.uk> wrote:

On 5/21/2018 9:14 AM, Rhodri James wrote:
On 21/05/18 12:29, Daniel Moisset wrote:
The following might be slightly clearer. item_iterator = iter(get_something, INCONVENIENT_SENTINEL) for item in item_iterator: do_something(item) I think iter is the right thing to use here. I think your contrary response is a least partly due to unfamiliarity with the two-argument form of iter and how it abbreviates and encapsulates the alternatives you already know. The essential idea of 'iter' is to produce iterators from Python objects. When the input is a function, sentinel pair, iter returns an instance of a internal callable_iterator class, equivalent to class callable_iterator def __init__(self, function, sentinel) self.function = function self.sentinel = sentinel def __iter__(self): return self def __next__(self): item = self.function() if item == self.sentinel: raise StopIteration return item If iter were actually written in Python, it might instead return the generator returned by a generator function encapsulating one of well-know while loop idioms. # Double call form. def function_sentinel(function, sentinel) item = function() while item != sentinel yield item item = function() # Loop and a half form; the loop body is essentially the same # as the body of callable_iterator.__next__, above def function_sentinel(function, sentinel): while True: item = function() # No 'self.' if item == sentinel: # No 'self.' break # Instead of 'raise StopIteration yield item # Instead of 'return item' The essential idea of for-loops is to cleanly separate sequential production of items to be processed, in the header, from processing of each item, in the body. It always calls iter(iterable) for you. If you need to pass two arguments to iter, you must do it explicitly. The while alternatives for this case intermix getting and processing items in the body. -- Terry Jan Reedy

while ((v = get_something()) != INCONVENIENT_SENTINEL) do_something(v);
The current pattern in Python would be something like: v = get_something() while v != INCONVENIENT_SENTINEL: do_something(v) v = get_something() With "as" allowed in "while", they pattern might be: while get_something() as v: if v == INCONVENIENT_SENTINEL: break do_something(v) The discussion isn't over, so it could also be: while (get_something() as v) != INCONVENIENT_SENTINEL: do_something(v) Cheers, -- Juancarlo *Añez*

v = get_something() while v != INCONVENIENT_SENTINEL: do_something(v) v = get_something() I'd personally go with: while True: v = get_something() if v != INCONVENIENT_SENTINEL: break do_something(v) But it's not much different. I'd really like to be able to use jump statements in ternary expressions, like: do_something(v) But that's another story. -- Carl Smith carl.input@gmail.com On 21 May 2018 at 13:22, Juancarlo Añez <apalala@gmail.com> wrote:

On 21/05/18 13:22, Juancarlo Añez wrote:
Actually more usually while True: v = get_something() if v == INCONVENIENT_SENTINEL: break do_something(v) Inelegant and, as has been pointed out, frankly misleading about the nature of the loop, but at least you can tell what's going on fairly straightforwardly.
These two are somewhat different things. -- Rhodri James *-* Kynesim Ltd

On 2018-05-21 05:22, Juancarlo Añez wrote:
This is a good summary of the choices. I think the second one is a good compromise. More elegant, yet avoiding the problems with assignment-expressions available everywhere. It is true that := handles more (perhaps less-common) use cases, but subjectively it is not as "Pythonic." Also doesn't appear to be the direction the surveyed languages are going. YMMV, -Mike

On Mon, May 21, 2018 at 10:00:01AM -0700, Mike Miller wrote:
Your conclusion does not follow even from the limited sample of languages you looked at. Out of the five languages you surveyed, three include some form of assignment expressions. It completely collapses if we look at a more extensive sample of languages dating back to 2009. If we limit ourselves to the three clear "winners" since 2009 (languages which have significant, if still niche, popularity/relevance according to TIOBE), we find all three: Coffeescript, Go, Swift have some form of assignment expressions. Go's is very limited: only in "if" statements. Coffeescript and Swift allow assignment expressions anywhere. -- Steve

On 2018-05-21 16:50, Steven D'Aprano wrote:
Your conclusion does not follow even from the limited sample of
They're not going in the direction of assignment-expressions everywhere, but rather one built in to the if/while statement. Coffeescript could certainly be a good candidate, though falling out of favor to typescript. Swift may allow them but are not useful when returning Void. -Mike

On Sat, May 19, 2018 at 10:54 AM, Mike Miller <python-ideas@mgmiller.net> wrote:
The bit that you tag on as an afterthought is actually critically important here. You have two options: 1) The 'as' is part of the syntax of the 'if' and 'while' statements; or 2) The 'as' is part of the definition of an expression. The first case would be grammar like this: if_stmt: 'if' test ['as' NAME] ':' suite ('elif' test ':' suite)* ['else' ':' suite] The second is permitting 'as' name-bindings in arbitrary expressions, but then saying "except that they're only allowed in 'if' statements". As you've noted, the first one isn't sufficient. You can't use the restricted syntax for more than a small handful of conditions (including re.match(), but not including anything that might return None and might return other falsey values). So if this is valid: if (f(x) as y) is not None: then why isn't this valid: print(f(x) as y) or this: f(x) as y # identical to "y = f(x)" or even this: with (f(x) as y): ? If you say they should all be valid, have a read of the PEP - the consequences are pretty dangerous, especially the last one. If not, then where do you draw the line, and which consequences do you want to take? The 'as' syntax has been hammered out in great detail and is no longer recommended due to its negative interactions with existing constructs. ChrisA

Chris Angelico wrote:
The 'as' syntax has been hammered out in great detail and is no longer recommended due to its negative interactions with existing constructs.
Allowing it in arbitrary expressions has been ruled out on the grounds that the difference between "with x as y:" and "with (x as y):" would be too subtle. But that argument doesn't apply if "as" becomes part of the syntax of "if" and "while". Do we think that's a bad idea as well? From the survey of other modern languages that was just posted, it seems we'd be in good company if adopted something like that. -- Greg

On 2018-05-18 19:14, Chris Angelico wrote:
Yes, largely because it's insufficient for all but a small handful of situations. I mentioned that in the previous email.
I'd argue that they are the bulk of occurrences, that's why they've been chosen as the compromise in those languages. Once there is an assignment and two compares, the time to break up the statement is approaching quickly, no? -Mike

On Sun, May 20, 2018 at 8:56 AM, Mike Miller <python-ideas@mgmiller.net> wrote:
But you can't put a comparison after the assignment, if it's part of the syntax of the 'if' statement. That's not how grammar works. So you have two options: either the ONLY thing you can capture is the condition value (which you already know to be truthy), or it's part of a more general expression, and is independent of if/while. You said yourself that the first one isn't enough. So it HAS to be an expression feature, not a statement feature. ChrisA

On 2018-05-19 16:00, Chris Angelico wrote:
Sorry, may have not written clearly. But the gist of the thread is several languages found the simple form (one assignment and compare) adequate. Believe the examples we've looked at in the various threads have been the simple form. So I argue it is enough in most cases. -Mike

On Sat, 19 May 2018, Greg Ewing wrote:
I don't quite get what's so subtle about it, am I missing something? The "with" keyword calls "__enter__", and "as" gives it a name. Just like "-x + y" is different from "-(x + y)", each part does it's thing, and you control the order with parens. /Paul

Paul Svensson wrote:
I think the difference is that mentally one already tends to think of "with x as y" being grouped "with (x as y)" rather than "(with x) as y". So, if "x as y" becomes a legal expression all by itself, it will *seem* as though the meaning of "as" is changed simply by adding explicit parentheses around an expression that was already implicitly parenthesised. Whether this is too subtle or not is a matter of opinion, but it was raised as one of the objections to using "as", so some people obviously thought so. -- Greg

On Sat, May 19, 2018 at 4:12 PM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
The reason it's dangerously subtle is that this will work: with (open("filename") as f): And this will work: with ( open("filename") as f ): And this will fail: with (threading.Lock() as lock): Do you know why? Do you want to try to debug that? I can guarantee you that a lot of people will go "hey, cool, now I can use parentheses in 'with' statements to wrap them across lines", and will do so very happily for a long time, wrapping their file-open blocks without an issue. But the semantics have changed *and they will not even know*. The current meaning of "as" is never "bind the expression on the left to the target on the right". Oh and here's another one: Should the target be required to be a name, or should it be allowed to be more complicated (eg "blah blah as self.spam")? Because Python isn't consistent on that point. Do you know which uses of "as" allow other targets than names and which don't? Again, do you want to have to debug situations where parentheses don't seem to matter for simple names, but with another kind of target, they're mandatory / forbidden? (Depending on which way the semantics are defined.) Using "as" in this way will NOT be consistent with the rest of Python, and it will introduce many subtleties for both the compiler and for humans. ChrisA

On Sat, May 19, 2018 at 02:11:38PM +1200, Greg Ewing wrote:
Yes. What is so special about "if" and "while" that they and they alone can make use of assignment expressions? The `and` and `or` short-cut operators are only useful in `if` and `while` statements, so we ought to prohibit them outside of `if` and `while` statements. Do you agree? Either assignment expressions are useful, and they should be allowed anywhere an expression is allowed, or they aren't, and they shouldn't be allowed at all. Breaking the rules for a special case is rarely the right decision.
/s/good/bad/ Compared to the Zen of Python, Go seems to break all the rules. Or at least most of them -- the beauty of the Zen is that it is open to interpetation :-) Go doesn't have exceptions. It requires the use of the tedious old anti-pattern of flag, result = function() if flag: do what you want else: # Now how the hell do I handle this? as a fundamental design principle, apparently because ugly and tedious boilerplate is a good thing, giving us all the verbosity of EAFP at its worst without any of the benefits. Similarly, its 2018 and the designers of Go turned their back on about 4/5th of the world, insisting that strings are ASCII and Unicode is an afterthought. I'm sure Rob Pike has his reasons for prohibiting exceptions, and network engineers probably think text is just another data format. Go might be an awesome language for what it is designed to do, but Forth is an awesome language for what it does best too, and we don't copy Forth's way of doing things. Before we copy Go, whether we copy a feature or a lack of feature, we ought to carefully consider how well that fits Python. (Sorry to just pick on Go, but it is the one I'm most familiar with.) -- Steve

On 2018-05-18 17:54, Mike Miller wrote:
Thanks, it is really great to see that comparison. That definitely pushes me even more towards disliking the assignment-expression proposals and preferring a more specific change to the syntax of particular constructs (if, while, maybe comprehensions) --- or no change at all. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

Thanks for writing up the summary, but you have picked a very narrow subset of new languages. One might even say a biased subset. How about these new languages? Elixir, Elm, TypeScript, Hack, Julia, Perl6, Ring, LiveScript, Ballerina, Crystal, Opa, Red, Ceylon TL;DR At least 10 out of these 13 include some form of assignment expressions. If we include the five you selected, 13 out of 18, or 72% of the sample, include some form of assignment expression. Details follow below. Even in the limited set of five languages you chose, 3 out of the 5 allow some form of assignment expressions: - Go allows assignment expressions in "if" statements, but not "while"; - Rust allows assignment expressions in both "if" and "while"; - as does Swift; - Swift also allows assignment in arbitrary expressions, but it returns Void (equivalent to returning None). As for the other languages I found: Elixir (2011): - everything is an expression, including assignment if current_user = Plug.Conn.get_session(conn, :current_user) do Logger.info "User #{current_user.id} logged out" end https://stackoverflow.com/questions/42682961/assign-variable-and-test-in-if-... Elm (2012) - has "let expressions" http://elm-lang.org/docs/syntax#let-expressions TypeScript (2012) - a strict superset of Javascript, including assignment expressions Hack (2014) - a (not quite full) superset of PHP, including assignment expressions Julia (2012) - includes assignment expressions julia> x = 1 1 julia> y = (x = 2) + 1 3 julia> x 2 Perl 6 a.k.a. Rakudo (2015) - assignment is an expression: steve@orac ~ $ perl6 -e "say (my \$x = 32)+100; say \$x" 132 32 (the backslashes are escaping the dollar signs from the shell, they're not part of the Perl syntax) - also allows any(?) statement to be used as an expression with the `do` prefix Ring (2016) Documentation seems fairly poor, more concerned with singing the praises of the language than explaining the behaviour in detail, but maybe that's just me. But I *think* assignment is not an expression. Using the Ring interpreter here: http://ring-lang.net/ I tried running this: x = 55 x = (x=99) see x and got the output 0. My interpretation of this is that the x=99 expression is being interpreted as equals, not assignment. LiveScript (2011) - assignment is an expression which returns the value assigned http://livescript.net/#assignment Ballerina (2017) - as far as I can tell, assignment is purely a statement https://ballerina.io/res/Ballerina-Language-Specification-WD-2015-05-01.pdf Crystal (2014) - assignment is an expression which returns the value assigned I don't know if this link will work: https://play.crystal-lang.org/#/r/43fq but if not, try running this: x = 99; y = (x = 33) + 1; print x; print y; and the output ought to be 3334. Opa (2011) - assignment is an expression; the following two declarations are equivalent: two = { one = 1 // semicolon and newline are equivalent one + one } two = { one = 1; one + one // the exact same thing as above } https://github.com/MLstate/opalang/wiki/The-core-language Red (2011) (not to be confused with the US DOD "RED" language from 1979). I can't get the documentation to Red to display in a readable form in my browser, but it claims to be nearly identical to Rebol. Wikipedia describes Rebol (and presumably Red) as not having either expressions or statements in usual sense. Based on my reading, it is kinda-sorta like Forth except without the stack or the postfix syntax. The creator of Red describes it: "About the syntax and semantics, in a nutshell, it's a Lisp without parentheses and with infix operator support." which suggests that assignment could be an expression. There's also a "set" function which can assign values, but frankly the Rebol programming model confuses me and so I'll count this as a "Possibly, but I can't tell for sure so let's say No" for the question of assignment expressions. Ceylon (2011) - Ceylon has "let" assignment expressions. https://ceylon-lang.org/documentation/1.3/tour/attributes-control-structures... - while loops support assignment https://ceylon-lang.org/documentation/1.3/tour/attributes-control-structures... - and switch statements also support a named assignment https://ceylon-lang.org/documentation/1.3/tour/attributes-control-structures... -- Steve

On 2018-05-19 06:41, Steven D'Aprano wrote:
Certainly. I chose basically on whether it was well used (popular), I'd heard about it, and it was easy to find documentation/discussion on. Then I ran out of energy around number six, haha. But I think this is representative of industry trends. -Mike

Also meant to mention, I didn't pick them specifically in advance to match a goal. I was mildly surprised at their similar design on that front. -Mike

On 2018-05-19 06:41, Steven D'Aprano wrote:
Overall your list is fair, and you're right that more languages could be considered. However, you also argued elsewhere in this thread that Go had an unpythonic design so we shouldn't put too much weight on what it does. Given that, I think it's fair to add that, in my opinion, JavaScript and PHP are abysmally designed languages, so we should put little weight on any precedent they set. What would be nice is to know how much people like these various languages, and how productive people are with these languages, and then correlate that with their various features. However, as discussed on another thread, getting hard data about such matters is not easy. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

On 2018-05-19 06:41, Steven D'Aprano wrote:
Details follow below.
Thanks for this, had some more time to read it more closely. Correct me if I'm probably wrong, but most of these are not used by many. Except perhaps: - Typescript, which is constrained by a compatibility goal with JavaScript. - Julia, - Possibly Elixir, have heard good things about it. My focus was on "industry standard" languages however, not pushing the envelope too far but rather on humdrum bug reduction and maintainability, an area Python aspires to, I believe. There was a stack overflow survey recently, should probably dig that up. -Mike

On Sat, May 19, 2018 at 04:28:10PM -0700, Mike Miller wrote:
According to the latest survey on TIOBE (May 2018), *none* of the languages either of us surveyed are used by more than a small niche. I realise that the TIOBE ranking is not the only available ranking, nor is their methodology necessarily the best, but if other people want to look at other language surveys they are free to do so. According to TIOBE, the rankings of your five languages are: Go #14 (up two places) 0.970% Swift #19 (down six places) 0.907% Dart #26 0.859% Kotlin #49 0.292% Rust #51-100 (percentages too small to differentiate) So even the most popular of your languages are still very niche. Go and Swift have a lot of industry "buzz" about them, but that's not translating to jobs or Google searches yet. The thirteen languages I surveyed are in a similar position: the highest ranked of them is Julia at #46. (I don't think TIOBE distinguishes between Perl at #18 and Perl 6.) Almost by definition, any new language is going to only be used by a small subset of programmers. [...]
My focus was on "industry standard" languages however,
I'm sorry, but no it wasn't. If we want *industry standard* languages, none of them are going to be *new*, and we need to look at those at the top of the TIOBE rankings: Java C C++ Python C# VB .Net PHP Javascript SQL Ruby R Delphi/Object Pascal Go and Swift are backed by large comporations and may some day be "industry standard", but they aren't yet. [Aside: it is sobering to realise that according to at least one objective ranking, "ancient and obsolete" Pascal is still more popular than "cool" new languages like Go and Swift.] I've somewhat arbitrarily cut the list off at "languages ranked above 1% on TIOBE", but we have to cut the list of somewhere. And of course in certain specific industries the standard languages may be very different, e.g. there are still tens of millions of lines of COBOL code being maintained in the banking industry. Out of those industry standard languages (as ranked by TIOBE, other methodology may result in other rankings) we find: 8/12 have some form of assignment expressions; (Java, C, C++, C#, PHP, Javascript, Ruby, R) 4/12 do not (Python, VB .Net, SQL, Delphi). -- Steve

On Sun, May 20, 2018 at 11:43 AM, Steven D'Aprano <steve@pearwood.info> wrote:
SQL isn't really comparable here. To figure out whether Python is in better company *with* or *without* assignment expressions, we need to compare with languages that have a concept of "assignment" and "expression". SQL itself most certainly has expressions, but it doesn't have assignment per se. There are named sub-expressions in SELECT statements, but that's more of a temporary view on a table than anything like a Python variable / name binding; I don't think standard SQL has any way to fetch up a scalar value and then reuse it, other than burying it in a table and selecting twice from that table. Some database engines have an SQL-based procedural language, but it's non-standard. FWIW, PostgreSQL's "PL/pgSQL" has assignment, but it is a statement and not an expression, probably because its expression evaluator is defined in terms of SQL's SELECT statement. So there are really eleven: eight that do, two that don't, and one that currently doesn't, but its BDFL is discussing the possibility of adding it. ChrisA

[Steven D'Aprano <steve@pearwood.info>]
I was an early REBOL user, and my head still hurts ;-) It was ... different, for sure. The syntax is in some sense extremely simple, hoping to make it easy to define problem-specific layers of syntax on top of it. But I gave up before they got that far. In any case, everything is "an expression" there, including assignment. That's spelled: WORD ":" WHITESPACE+ EXPRESSION although that's lying a bit. Here from a Red shell:
That last one shows one of the challenges for people coming from "normal" languages: this does the same:
(x: 12) (print (add (y: 88) x)) 100
showing that it really is a lot like "Lisp without parentheses". Without the parens, it's impossible to tell where function calls begin and end without knowing which names denote functions and how many arguments each function requires! That's "a feature", they insist ;-) To be fair, you really do get used to it quickly for the heavily used builtin functions. One more:
Yup! "i1+2=3*88" is a variable name there :-) Short course: definitely "yes" on assignment expressions for Red. But I'm going to out on a limb and guess that the typical Python programmer wouldn't find "but Red does it!" persuasive ;-)

On Sun, May 20, 2018 at 12:21:16AM -0500, Tim Peters wrote:
Yeah, to me it looks more like a prefix version of Forth than Lisp. Complete with "anything can be a name":
Yup! "i1+2=3*88" is a variable name there :-)
but maybe when languages are weird enough they all look the same :-)
True :-) Thanks for doing the testing. -- Steve

On Sun, May 20, 2018 at 1:35 AM, Steven D'Aprano <steve@pearwood.info> wrote:
but maybe when languages are weird enough they all look the same :-)
https://www.dreamsongs.com/WorseIsBetter.html Look at what happened with PERL... IMPORTANT NOTE: Enabling "as" in "if" and "while" doesn't have to be at the expense of some form of assignment expression. We've been having this discussion as if has to be either one or the other, and there's no reason we can't have both (as mentioned before, "reduce()" is live and well in the libraries, for people who need to use it). I in particular would not like it at all if something like ":=" was at the expense of "as", and I think that others on the side of expanding "if" and "while" may feel the same way. As a reminder, some of the arguments in favor of "as" are around the patterns exemplified by the "re" module; patterns which seem correct and useful, and that are used by other standard and 3rd-party modules. if os.fork() as child_pid: parent(child_pid) else: child() Cheers! -- Juancarlo *Añez*

On Mon, May 21, 2018 at 1:19 AM, Juancarlo Añez <apalala@gmail.com> wrote:
Let's suppose that the := syntax already existed in Python - that you could write this: if child_pid := os.fork(): parent(child_pid) else: child() What would be the benefit of adding the "as child_pid" syntax? The full assignment expression syntax completely covers all the jobs that you can do with a capturing if statement. ChrisA

The "as" syntax over "if" and "while" may go in with all the restrictions and caveats already discussed. The discussion about the semantics of how ":=" should affect the enclosing context when used in generators, etc. may continue for as long as it must. And there are some of us who like the consistency of "us", and would shy away from ":=". For example, Should this be valid? if child_pid := os.fork(): parent(child_pid) else: child() print(child_pid) This shouldn't be: if os.fork() as child_pid: parent(child_pid) else: child() print(child_pid) # child_pid is undefined -- Juancarlo *Añez*

print(child_pid) # child_pid is undefined
Why on earth would it be undefined?
Indeed, users would expect the new uses of "as" to behave as the previous ones. The problem is that "with" and "except" do things differently: In [*1*]: *import* *os* In [*2*]: *with* open(os.path.expanduser('~/tmp/xx')) *as* f: ...: *pass* ...: In [*3*]: print(f) <_io.TextIOWrapper name='/Users/apalala/tmp/xx' mode='r' encoding='UTF-8'> In [*4*]: *try*: ...: print('ok') ...: *raise* *Exception*() ...: *except* *Exception* *as* e: ...: *pass* ...: In [*5*]: print(e) --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-5-bb2ea196f768> in <module>() ----> 1 print(e) NameError: name 'e' is not defined
Anyway, if you want to propose an alternative to PEP 572, you ought to write your own competing PEP.
I don't take that as a challenge. It would be good to have different, maybe somewhat competing PEPs. It's been done before. -- Juancarlo *Añez*

[Tim]
I was an early REBOL user, and my head still hurts ;-) It was ... different, for sure.
[Steven D'Aprano <steve@pearwood.info>]
Yeah, to me it looks more like a prefix version of Forth than Lisp. Complete with "anything can be a name":
The example I gave just strung "words" together, but just as fundamental is the notion of "block": a series of words and/or blocks enclosed in square brackets. That's akin to Lisp's "S expressions", but Rebol/Red _don't_ "evaluate" blocks by default. Blocks can hold data or code, or a mix of both, and the syntax doesn't care. That's the real reason "(almost) anything can be a name (word)". Here I'll build to a simple example where _some_ Pythoneers will experience envy rather than revulsion ;-)
[x y] == [x y]
The block isn't evaluated. Depending on how it's _used_, it may be treated as data (perhaps you want to think of it as being a sequence of two symbols, or strings). If you evaluate it, it blows up (because I don't happen to have any variables with those names defined):
do [x y] *** Script Error: x has no value
Give the names some values, and _then_ it can be evaluated; and evaluating a block returns the value of the last expression in the block:
If you want a block with all the expressions' values, use `reduce` instead:
reduce [x y] == [12 13]
Code? Data? No difference. Here's the part where some Pythoneers will get jealous:
`func` is just another function that happens to build an anonymous function. It takes two blocks as arguments: a block containing the formal argument names, and a block with the code to execute. Both blocks are essentially "data" to `func`. It doesn't look much like Forth anymore ;-) Note that the following does exactly the same:
In practice, slinging Rebol/Red most feels like working in a functional language. One of their real goals, though, is to enable writing functions that can be called like so: schedule "Change furnace filter" [ starting today then every 3 months ] There's an elaborate `parse` function built in that supports many ways of applying user-supplied rules to parse blocks like the one in that example. Of course you can emulate much the same in Python by, e.g., passing triple-quoted strings instead. In that specific example. Rebol/Red provide a minimum of syntax shared by all such applications, and when the sub-language gets elaborate enough that _nesting_ blocks makes good sense, nesting triple-quoted strings sucks ;-) All languages have some good things going for them. But I'm not sure I've ever seen Rebol/Red code that _used_ the value of an assignment expression; that it has them at all seems much more to follow from that everything is an expression. As in most functional languages, if you want initialized local variables, you're more likely to invoke an anonymous spelled-inline function instead. Which is what, e.g., Haskell's "let PILE_OF_BINDINGS in EXPRESSION" is syntactic sugar for doing.

[Tim, on Rebol/Red]
[Greg Ewing <greg.ewing@canterbury.ac.nz>]
How does scoping work? If you pass a block to a function which evaluates it, how are names in the block resolved?
Too involved, but basically a form of lexical scoping. Rebol struggled with this, and details vary by version. Here are the last docs for version 2: http://www.rebol.com/r3/docs/concepts/funcs-scope.html Details changed again for version 3. Best I can tell, Red hasn't written up its own rules yet. The model did _not_ support closures naturally (in the sense of local bindings persisting beyond a function's return). Version 3 added an alternative to `func`, named `closure`, which could be used when you wanted a closure: http://www.rebol.com/r3/docs/datatypes/closure.html But I noticed just now that Red doesn't have `closure` built in, and its funcs don't support closures naturally either:
adder: func [x] [func [y] [x + y]] == func [x][func [y] [x + y]]
add3: adder 3 == func [y][x + y]
That's not dynamic scoping, either - it's still unhappy if `x` is defined at top level:

You seem determined to throw out requirements when it suits. One of which was for newer languages. While this thread's original post was not so specific on that, in a previous post (I believe you read) defined that as created in the "last decade or so". The ones created to address shortcomings in the previous generation. The point being to find momentum in design of newer languages. Your list of older languages is therefore not pertinent. -Mike On 2018-05-19 18:43, Steven D'Aprano wrote:

On Sun, May 20, 2018 at 10:57:39AM -0700, Mike Miller wrote:
You seem determined to throw out requirements when it suits. One of which was for newer languages.
YOU are the one who changed the requirements: first you said "newer languages", then you changed it to industry standard. I showed that a broad sample of new languages by far favours assignment expressions (14 out of 18 including Red, which I wrongly guessed didn't) and even the narrow subset that you wrote up included some form of assignment expressions in 3 out of the 5 cases. So you shifted the goal posts from "newer languages" to "industry standard". That was YOUR choice, not mine. Based on the TIOBE language popularity rankings, there is no overlap between "industry standard" and "newer" languages. Not even Go or Swift. Delphi/Pascal is more popular than Go, unless you happen to work for Google. If you disagree about using TIOBE, feel free to choose your own objective and neutral ranking system, so long as it is better than "I've heard of these five languages, so they're industry standard". In 2016 Liefehacker came up with this list, based on combining rankings from TIOBE, PYPL and various job sites: Java, C, Python, C++, JavaScript, C#, PHP, Swift, Objective-C, R https://www.lifehacker.com.au/2016/12/the-most-popular-programming-languages... only one of which is in your list of new languages, and that one (Swift) is one of those which includes assignment as an expression. Looking at job rankings: https://www.codingdojo.com/blog/9-most-in-demand-programming-languages-of-20... https://www.codingdojo.com/blog/7-most-in-demand-programming-languages-of-20... and you won't find any new languages. [...]
The point being to find momentum in design of newer languages. Your list of older languages is therefore not pertinent.
Then you shouldn't have raised "industry standard" as relevant. If we limit the list to new AND "industry buzz", then we get a sample space of *two*: Swift and Go, both of which have some form of assignment expressions: - Swift has assignment expressions; - Go allows assignment in if statements, but not while statements. That's not a big sample to go by, and it is subject to Rob Pike's idiosyncratic design (not necessary bad, but bucking language trends). -- Steve

To clarify there were three main criteria, and one minor. Newer, popular/becoming industry standard, and designed to address shortcomings in previous generations. Finally, the limit of my energy when already working on a project. I also should have provided the link to the previous discussion. When writing the first message I indeed thought a wiki might be better, because I'd undoubtedly forget to mention, or inaccurately state something. Meta discussion: While I do prefer the "as" version of assignment, I didn't enter into the inquiry to prove it better, but rather to find industry momentum, and had only some minor exposure to go/kotlin via their tutorials beforehand. I did a similar thing when "f-string" design was debated. At first I wasn't in favor of them handling expressions, but after doing a survey of other modern/popular languages found it was working well elsewhere and changed my opinion. So it goes both ways, and I'm open to being convinced against my first preference and not heavily invested in it. -Mike

On Tue, May 22, 2018 at 2:43 AM, Mike Miller <python-ideas@mgmiller.net> wrote:
Note how not one of your criteria says that the language has to have made the right choice - only that it's made a DIFFERENT choice to languages in a previous generation. So you're heavily selecting in favour of languages that say "hey look, we know better than everyone else does", without actually proving that it's better. There's no shortage of language designers who say "oh look how terrible C is", and when a language designer has a lot of clout behind him (like "apps for this new mobile phone should be written using this language"), it can get extremely popular among people who don't actually have much choice of language - and certainly don't have the option to use C. Much more useful would be to look at languages that (a) work in a field where programmers have ample freedom to choose between languages, and (b) have been around long enough to actually demonstrate that people want to use them. Look through the Stack Overflow Developer Survey's report on languages: https://insights.stackoverflow.com/survey/2018/#most-loved-dreaded-and-wante... A "Wanted" language is one that many developers say "I don't currently use, but I would like to". (It may also be a language that has murdered semicolons. I believe the bounty on JavaScript's head is quite high now.) Go through that list and you'll get an idea of what people wish they could use; then toss out anything that hasn't been around for at least 10 years, because there's a tendency for new technologies to be over-represented in a "Wanted" listing (partly because fewer programmers already know them, and partly because people want to try the latest toys). That may give you a better list of languages to compare against. ChrisA

Chris makes a lot of good points regarding *which* languages to look at, but it seems like that line of enquiry is unlikely to suggest anything more than it has so far, especially if we're limiting it to languages everyone has heard of. They either use a keyword, an operator, don't support the feature or they're wacky. If anything, the survey says we need to think outside the box. -- Carl Smith carl.input@gmail.com On 21 May 2018 at 20:11, Chris Angelico <rosuav@gmail.com> wrote:

If the ability to capture a subexpression is given up on, then we are are down to just capturing the value of the predicate in if statements. In loops, it is only the predicate and iteration index. If you mashup `if` and `def`, you end up with this mess: if get_something() def (value): do_something(value) And `while` and `def` is no prettier: while get_something() def (value, index): do_something(value) Both ugly, and totally misleading, but being able to accept the values as params is nice, and the parenthesis make things clearer and more Pythonic IMO, so maybe just use `as` but require the names be parenthesised: if predicate as (value): use(value) while predicate as (value, index): use(value) The next options may be a bit too functional-looking, when the suite is not a function body, but I thought they read better: while predicate pass (value, index): use(value) if predicate -> (value): use(value) while predicate yield (value, index): use(value) The main idea is to reuse comma separated params in parens to make the assignment clearer and to look more like the `def` and `class` grammar. -- Carl Smith carl.input@gmail.com On 21 May 2018 at 22:37, Carl Smith <carl.input@gmail.com> wrote:

On 2018-05-21 12:11, Chris Angelico wrote:
I'd say that also has limited usefulness. The problem is that people may "want" to learn a language for many reasons, and "the language made good design choices" is only one such reason. A lot of people may want to use JavaScript because it's hip or in demand or because they can (or think they can) make money with it. But I'm not so interested in that. What interests me is: what are the languages that people specifically believe are superior to other languages *in design*? (Even better would be what are the languages that actually ARE superior, in some reasonably nonsubjective, definable, way, but we have even less data on that.) -- 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 22, 2018 at 11:45 AM, Brendan Barnwell <brenbarn@brenbarn.net> wrote:
What you want is virtually impossible to obtain, so we go for whatever approximations we can actually get hold of. There are a few reasons someone might want to use a language. One is "I hate this language but it'll earn me money", yes, but the way the Stack Overflow survey is worded, those ones won't come up. IIRC the wording is "which languages do you desire to be using next year", after removing the ones for which you also said that you're using them now. So there are a good few reasons that you might wish you could be using a language: 1) You believe it's a good language, worth using, but just have never gotten around to starting with it 2) You think it's really fun and awesome, and wish your employer would let you use that instead of what you currently use 3) It represents a particular coding arena that you want to get into (eg you want to get into iOS development, so you pick "Swift") 4) It's an absolutely awesome language and you have plans to learn it, but haven't executed on them yet 5) It's a new language, and you want the shinies 6) Etc, etc, etc. Generally speaking, for a language to show up in the "Most Wanted", a lot of developers have to think it's something worth knowing. After cutting out the youngest languages (which I defined as "released within the last ten years") to remove their overrepresentation, you're left with languages that, in the opinions of people who don't yet program in them, are worth learning. That's far from an exact answer to the question we really want to ask, but it's reasonably concrete (to the extent that surveys ever are). But as Guido says, this is not a popular vote among languages. It's interesting to notice what other languages are doing, but harder to pin down what's good or bad about what they're doing. ChrisA

I thought this thread did a good job of establishing that looking at other languages is not going to help with introducing assignment expressions into Python. It was still interesting to read. If we can't copy from other languages (or even agree on *which* languages to copy), Python will have to do something novel or give up on this. I thought, if the thread's dead, it'd be nice to do a bit of bike-shedding on the end, but there was no appetite for that. I'm not the most sensitive guy, and don't really have a sense of how my posts are being received, but would appreciate being told if my contribution isn't especially welcome. Best, -- Carl Smith carl.input@gmail.com On 22 May 2018 at 02:58, Chris Angelico <rosuav@gmail.com> wrote:

On Mon, May 21, 2018 at 09:43:45AM -0700, Mike Miller wrote:
To take your criteria in reverse order: 1. Why should the limit on *your* energy be a deciding factor? I was willing to spend a few hours doing a more complete sample of new languages. Should we ignore those because you ran out of energy? If anyone else wants to extend it even further, and survey more languages, we should welcome an even more extensive survey. 2. By definition, EVERY new language is designed to "address shortcomings in previous generations" of languages. If the designer thought previous languages were ideal, they wouldn't invent a new language. 3. None of the languages you surveyed are "popular/becoming industry standard" according to the closest thing we have to an objective measure of popularity: rankings like those provided by TIOBE. If you don't like TIOBE's methodology, feel free to propose a different, neutral, ranking. If you want a subjective opinion based on "industry buzz", then I would say that out of the five languages you listed, only two (Go and Swift) are anything close to "becoming industry standard", a tiny sample indeed, but nevertheless one where both languages have some form of assignment expressions (Go only in "if" statements, Swift everywhere). 4. What was your criteria for "newer"? I must admit I assumed it was languages invented since 2010, but I see Go was invented in 2009. If your cutoff is 2009, then we ought to include Coffeescript, which also has assignment expressions. -- Steve

On 2018-05-21 16:40, Steven D'Aprano wrote: Consider the link Chris sent above: https://insights.stackoverflow.com/survey/2018/#most-loved-dreaded-and-wante... The top six coincide with my list, plus TypeScript (superset of JS) and Python. I'm pretty happy with those chosen, considering. -Mike

On Mon, May 21, 2018 at 7:40 PM, Steven D'Aprano <steve@pearwood.info> wrote:
What acknowledgement do you want him to make? That your list is better? I'm not sure what's the point of continuing this line of questioning. Personally, I don't think we should restrict the survey to newer languages. New changes to older languages are also relevant. C++17 is adding initialization syntax to `if`. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0305r0.html Why they're doing that, when C++ has assignment expressions, may or may not be relevant to this discussion.
It should not. But it is. He's simply admitting it.
Perhaps Mark wants to say that these languages were meant to replace particular languages. Each of his examples listed languages that they were meant to replace, and those replaced languages aren't exactly esoteric, academic, or whatever Perl is. They're languages by industry giants.
"Industry _standard_" may be the wrong term. Except for maybe Kotlin, the languages are created and backed by major players in the industry: Apple, Mozilla, and Google.
Current top 50 programming languages, filtered by year >= 2000. (Years are from quick Google and Wikipedia lookups.) 6 Visual Basic .NET 2001 14 Go 2009 19 Swift 2014 20 Scala 2004 21 Apex 2006 26 Dart 2011 31 D 2001 33 Scratch 2002 (2007) 37 Clojure 2007 41 OpenCL 2009 46 Julia 2012 49 Kotlin 2011 Possible removals: - Some might argue that Visual Basic .NET is just a rebranding of Visual Basic, an older language. - Scratch looks like it's a language for teaching, not using, and I'm not sure why it's on the list. - I don't know whether to remove Apex, since it's specific to one platform and it sounds like it's almost Java. - Similarly, OpenCL sounds like it's just a fork of C/C++ for GPUs. With those languages removed, and reverse-sorted by year: 19 Swift 2014 46 Julia 2012 26 Dart 2011 49 Kotlin 2011 14 Go 2009 37 Clojure 2007 20 Scala 2004 31 D 2001 Mark's list captures four of the first five, while Rust is nowhere to be seen. Without Julia, "publicized in the last ten years" describes the split pretty well. Clojure and Scala may be outside of Mark's experience. They are functional languages, with at least a lean toward pure functions. Clojure, being a Lisp, probably uses `let` for name-binding, and I assume you'd make a macro for assign-and-compare if you want it, so it might not be informative for Python's decision. In Scala, pretty much everything is an expression (as it is in Lisp), so it might also be uninformative . D is much older than the others, but given its design goals, it might be interesting to look at anyway. That leaves Julia.
If your cutoff is 2009, then we ought to include Coffeescript, which also has assignment expressions.
CoffeeScript, like Scala, favors making things expression, so its decision might not be informative. CoffeeScript's grammar notes say, "[E]verything that can be an expression is one." http://coffeescript.org/v2/annotated-source/grammar.html

I don't know what to do with this thread. I enjoyed reading Mike's survey of what other languages do. I also enjoyed Chris's survey of what some other languages do. Then the thread veered off into several unproductive directions at once: a mini-tutorial for Rebol (or Red?), and endless bickering about which list of languages to allow in the survey. But that's not how this stuff gets decided. It's not the case that if we find that 90% of languages surveyed don't have inline assignment that means Python should not have it either. So changing the criteria for inclusion in the survey is entirely unproductive (except in that it may create a strong bonding experience between participants in the discussion -- though in this case perhaps the forces involved may be better described as repulsion than attraction :-). *Perhaps* it would help if we could ask those languages' designers for their rationale (most of them are still alive :-). But nobody proposed that. And it probably wouldn't help either, because the answers of actual language designers for such questions are typically a mixture of strongly held opinion on issues that aren't decidable based on hard facts alone, and hard but incredibly subtle facts related to the rest of the language's design and implementation (including the state of tooling and the structure of the compiler used as a reference implementation). So let's just end this thread. -- --Guido van Rossum (python.org/~guido)

For more background, this is the thread that inspired this one: https://mail.python.org/pipermail/python-dev/2018-April/153071.html -Mike

2018-05-19 3:54 GMT+03:00 Mike Miller <python-ideas@mgmiller.net>:
Thank you Mike this is a very useful analysis. It would be also great to know when they introduced this feature - from the beginning or after a while. Your results are completely consistent with all the examples from the numerous threads - that this feature should be added only in `while` and `if` statements. [Chris] The bit that you tag on as an afterthought is actually critically
I think I have a very strong argument "why are not others valid" - Because already three months have passed and among 1300+ messages there was not a single real example where assignment expression would be convenient or useful outside `while` and `if` statements. If you have a counterargument (with an example), I would be glad to see. This thread is about the design choices which were made in other modern languages. And from Mike's analysis it is evident that other languages have chosen to allow the assignment as an expression only where it is practical - in `while` and `if` statements, or not to allow it at all. I find this at least absolutely not constructive to push `print(f(x) as y)` or `print(y:=f(x))` in every thread. Is this really the only compelling example outside `while` and `if`? Also if we consider only the usage in `while` and `if` statements, what is the point of discussing differences between `with (f(x) as y)` and `with f(x) as y`? The first one will be a ` SyntaxError`, period. Can you also explain why the assignment expression should be valid outside `while` and `if` statements? - for dummy consistency? The `as` variant was _hammered_ only in the general case, which in itself does not make sense (which is confirmed by 1300+ messages without examples). Some time ago Guido said that he is not _impressed_ with `as` variant. But I think he does not like this form because it does not play well in general case. There are several reasons why. The main one is if `as` will be accepted it will prevent the use of this form in theoretically future statements (for example match statement). Another point that it looks ugly for general case. As it seems to me, the intersection with `with`, `except` is also important but can be overcomed. The use of any system requires some study and understanding. BUT I also think that Guido will not so strongly object if only `while` and `if` statements were under consideration, because there is no other choice. Examples from other languages are very important and their choice should be taken into account. Those languages are also designed by smart guys and I'm sure that they were thinking about where the assignement expression should be allowed and 1300+ messages only confirm their choice. Therefore, it may be worthwhile to focus only on actual use cases - in `while` and `if` statements??? and stop to flood every thread with dummy examples. p.s.: I deliberately did not mention the pseudo _usefulness_ of assignment expression in generators/comprehensions because the benefits are very questionable. Therefore, to begin with, we should start with `while` and `if` statements, and possibly in the future add it to comprehensions/generators. With kind regards, -gdg

On Sun, May 20, 2018 at 4:07 AM, Kirill Balunov <kirillbalunov@gmail.com> wrote:
Okay. Let me put it to you this way, then: Write up a simple and easy-to-explain set of rules for exactly what is valid and what is not. Make an actual proposal here. Be sure to demonstrate that the new construct is useful, with examples (like you're asking me for), and be sure that you're thorough enough in your definitions that this could actually be coded. I think you'll find that 1300+ messages aren't enough to adequately define what you're asking for. Or, alternatively: Give up on the 'as' syntax, because it was dropped from the PEP for a reason. ChrisA

JavaScript and PHP are abysmally designed languages, so we should put little weight on any precedent they set.
JavaScript has issues, due to its unique history, but to dismiss the entire language as too poorly designed to take seriously... Many clever people prefer JS to Python. -- Carl Smith carl.input@gmail.com On 19 May 2018 at 19:07, Kirill Balunov <kirillbalunov@gmail.com> wrote:

On Sat, May 19, 2018, 11:07 Kirill Balunov <kirillbalunov@gmail.com> wrote:
comprehensions. I don't think you read any of the messages. about 1000 were solely about comprehensions. How about every example brought up in 1300+ messages that assignment expressions could handle that otherwise required far more code? Some including imports? Did you miss where _every_ counter example was either doing what assignment expressions do with more code, or where outright misunderstandings of how they will work? Did you read Chris' responses where he clearly refused each example? Really hard to see how you got that take away if you read those messages. Basically you are not backed up at all by citing that. assignment expressions handle many cases with ONE syntax. They are the "one way to do it" where "it" covers every example brought up so far.

On 20/05/18 06:19, Matt Arcidy wrote:
I'm afraid I haven't seen a convincing example of an useful assignment expression in a comprehension that wasn't also a convincing example of things that shouldn't have been comprehensions in the first place. -- Rhodri James *-* Kynesim Ltd

On Mon, May 21, 2018, 03:58 Rhodri James <rhodri@kynesim.co.uk> wrote:
I haven't seen a convincing counter argument that doesn't axiomatically depend on personal opinion. Can you define your "shouldn't" in a way that isn't just your personal taste? I can't know from your statement here.

On 19/05/18 01:54, Mike Miller wrote:
Thanks for the analysis, but I'm afraid I must disagree with your recommendation. It was the thought I first had when Chris came out with his first draft of the PEP several months ago, but it's not enough to cope with my usual use cases. What I normally want is the Python equivalent of: while ((v = get_something()) != INCONVENIENT_SENTINEL) do_something(v); The condition expression itself is not what I want to capture; I need a subexpression, which the "as" syntax won't give me. -- Rhodri James *-* Kynesim Ltd

On 21 May 2018 at 12:05, Rhodri James <rhodri@kynesim.co.uk> wrote:
That use case should be covered by for v in iter(get_something, INCOVENIENT_SENTINEL): do_something(v) -- <https://www.machinalis.co.uk> Daniel Moisset UK COUNTRY MANAGER A: 1 Fore Street, EC2Y 9DT London <https://goo.gl/maps/pH9BBLgE8dG2> P: +44 7398 827139 <+44+7398+827139> M: dmoisset@machinalis.com <dmoisset@machinalis.com> | S: dmoisset <http://www.linkedin.com/company/456525> <http://www.twitter.com/machinalis> <http://www.facebook.com/machinalis> <https://www.instagram.com/machinalis.life/> Machinalis Limited is a company registered in England and Wales. Registered number: 10574987.

for v in iter(get_something, INCOVENIENT_SENTINEL): do_something(v)
There are many ways round my use case, all of them inelegant. That has to be one of the less comprehensible alternatives.
In for-loops (because they include an assignment already) we can improve this with more indicatively named functions in practice. -- Carl Smith carl.input@gmail.com On 21 May 2018 at 14:14, Rhodri James <rhodri@kynesim.co.uk> wrote:

On 5/21/2018 9:14 AM, Rhodri James wrote:
On 21/05/18 12:29, Daniel Moisset wrote:
The following might be slightly clearer. item_iterator = iter(get_something, INCONVENIENT_SENTINEL) for item in item_iterator: do_something(item) I think iter is the right thing to use here. I think your contrary response is a least partly due to unfamiliarity with the two-argument form of iter and how it abbreviates and encapsulates the alternatives you already know. The essential idea of 'iter' is to produce iterators from Python objects. When the input is a function, sentinel pair, iter returns an instance of a internal callable_iterator class, equivalent to class callable_iterator def __init__(self, function, sentinel) self.function = function self.sentinel = sentinel def __iter__(self): return self def __next__(self): item = self.function() if item == self.sentinel: raise StopIteration return item If iter were actually written in Python, it might instead return the generator returned by a generator function encapsulating one of well-know while loop idioms. # Double call form. def function_sentinel(function, sentinel) item = function() while item != sentinel yield item item = function() # Loop and a half form; the loop body is essentially the same # as the body of callable_iterator.__next__, above def function_sentinel(function, sentinel): while True: item = function() # No 'self.' if item == sentinel: # No 'self.' break # Instead of 'raise StopIteration yield item # Instead of 'return item' The essential idea of for-loops is to cleanly separate sequential production of items to be processed, in the header, from processing of each item, in the body. It always calls iter(iterable) for you. If you need to pass two arguments to iter, you must do it explicitly. The while alternatives for this case intermix getting and processing items in the body. -- Terry Jan Reedy

while ((v = get_something()) != INCONVENIENT_SENTINEL) do_something(v);
The current pattern in Python would be something like: v = get_something() while v != INCONVENIENT_SENTINEL: do_something(v) v = get_something() With "as" allowed in "while", they pattern might be: while get_something() as v: if v == INCONVENIENT_SENTINEL: break do_something(v) The discussion isn't over, so it could also be: while (get_something() as v) != INCONVENIENT_SENTINEL: do_something(v) Cheers, -- Juancarlo *Añez*

v = get_something() while v != INCONVENIENT_SENTINEL: do_something(v) v = get_something() I'd personally go with: while True: v = get_something() if v != INCONVENIENT_SENTINEL: break do_something(v) But it's not much different. I'd really like to be able to use jump statements in ternary expressions, like: do_something(v) But that's another story. -- Carl Smith carl.input@gmail.com On 21 May 2018 at 13:22, Juancarlo Añez <apalala@gmail.com> wrote:

On 21/05/18 13:22, Juancarlo Añez wrote:
Actually more usually while True: v = get_something() if v == INCONVENIENT_SENTINEL: break do_something(v) Inelegant and, as has been pointed out, frankly misleading about the nature of the loop, but at least you can tell what's going on fairly straightforwardly.
These two are somewhat different things. -- Rhodri James *-* Kynesim Ltd

On 2018-05-21 05:22, Juancarlo Añez wrote:
This is a good summary of the choices. I think the second one is a good compromise. More elegant, yet avoiding the problems with assignment-expressions available everywhere. It is true that := handles more (perhaps less-common) use cases, but subjectively it is not as "Pythonic." Also doesn't appear to be the direction the surveyed languages are going. YMMV, -Mike

On Mon, May 21, 2018 at 10:00:01AM -0700, Mike Miller wrote:
Your conclusion does not follow even from the limited sample of languages you looked at. Out of the five languages you surveyed, three include some form of assignment expressions. It completely collapses if we look at a more extensive sample of languages dating back to 2009. If we limit ourselves to the three clear "winners" since 2009 (languages which have significant, if still niche, popularity/relevance according to TIOBE), we find all three: Coffeescript, Go, Swift have some form of assignment expressions. Go's is very limited: only in "if" statements. Coffeescript and Swift allow assignment expressions anywhere. -- Steve

On 2018-05-21 16:50, Steven D'Aprano wrote:
Your conclusion does not follow even from the limited sample of
They're not going in the direction of assignment-expressions everywhere, but rather one built in to the if/while statement. Coffeescript could certainly be a good candidate, though falling out of favor to typescript. Swift may allow them but are not useful when returning Void. -Mike
participants (18)
-
Brendan Barnwell
-
Carl Smith
-
Chris Angelico
-
Daniel Moisset
-
Franklin? Lee
-
Greg Ewing
-
Guido van Rossum
-
Juancarlo Añez
-
Julien Salort
-
Kirill Balunov
-
Matt Arcidy
-
Mike Miller
-
Nick Coghlan
-
Paul Svensson
-
Rhodri James
-
Steven D'Aprano
-
Terry Reedy
-
Tim Peters