←back to thread

317 points est | 1 comments | | HN request time: 0s | source
Show context
gbfowler ◴[] No.17448582[source]
It is not syntactic sugar, "x := 10" is an assignment expression in contrast with "x = 10", which is a statement.

Hence the former can be used in contexts like "if x := 10: pass", which is the whole point of the PEP.

replies(2): >>17448624 #>>17448651 #
Aardwolf ◴[] No.17448624[source]
Why is it not syntactic sugar? It looks like convenience. You could argue everything above machine language is syntactic sugar.
replies(5): >>17448721 #>>17448736 #>>17448847 #>>17448880 #>>17448984 #
akvadrako ◴[] No.17448721[source]
Not so. Many things change the semantics, otherwise high level languages would just be glorified macros.

For example, import mod is NOT defined as

  mod = eval(open("mod.py").read())
but involves abstract load module operation, which is dependant on the environment.

That's why := is just syntactic sugar; there are no new semantics.

replies(3): >>17448810 #>>17448892 #>>17450904 #
chriswarbo ◴[] No.17448892[source]
> there are no new semantics

I don't think that's right; what expression/statement is `x := y` equivalent to? I'm thinking in particular about using mutable collections to emulate assignment in a lambda, e.g.

    >>> counter = (lambda c: lambda: (c.append(c.pop() + 1), c[0])[1])([0])
    >>> counter()
    1
    >>> counter()
    2
    >>> counter()
    3
It looks like this could now be done as:

    >>> counter = (lambda c: lambda: (c := c + 1))(0)
Yet the semantics here are very different: one is pushing and popping the contents of a list, without changing any variable bindings (`c` always points to the same list, but that list's contents changes); the other has no list, no pushing/popping, and does change the variable bindings (`c` keeps pointing to different integers).

Maybe it's equivalent to using a `=` statement, but statements are forbidden inside lambdas. Maybe the lambdas are equivalent to `def ...` functions, but what would their names be? Even if we made the outer one `def counter(c)...` the resulting value would have a different `func_name` (`counter` versus `<lambda>`).

Even the `if` examples that are scattered around this page don't seem to have an equivalent. For example:

    if (x := foo() is not None):
      do_something()
We can't "desugar" this, e.g. to something like the following:

    x = foo()
    if x is not None:
      do_something
The reason is that we're changing the point at which the binding takes place. For example, Python guarantees to evaluate the elements of a tuple in left to right order (which we exploited in the above push/pop example). That means we could write:

    if (sys.stdout.write(x), x := foo() is not None)[1]:
      do_something
This will print the current value of `x`, then update `x` to the return value of `foo()`. I can't think of a way to desugar this which preserves the semantics. For example, using the incorrect method from above:

    x = foo()
    if (sys.stdout.write(x), x is not None)[1]:
      do_something
This isn't equivalent, since it will print the new value of `x`. Maybe we could float the `write` call out of the condition too, but what about something like:

    if foo(x) and (x := bar()):
      do_something
We would have to perform `foo(x)` with the old value of `x`, store the result somewhere (a fresh temporary variable?), perform the `x = bar()` assignment, reconstruct the condition using the temporary variable and the new value of `x`, then `del` the temporary variable (in case `do_something` makes use of `locals()`).

PS: I think this `:=` is a good thing, and writing the above examples just reminded me how infuriating it is when high-level languages distinguish between statements and expressions, rather than having everything be an expression!

replies(1): >>17449218 #
akvadrako ◴[] No.17449218[source]
It seems like your convinced yourself it is just sugar by the end of your post. You need to use a temporary variable but then your example is easy.
replies(2): >>17450071 #>>17450151 #
chriswarbo ◴[] No.17450151[source]
Also, I don't see how something can meaningfully be called "just sugar" when AFAIK there is no general procedure for "desugaring".

> You need to use a temporary variable but then your example is easy.

Yes this example, of `if foo(x) and (x := bar()):`, would be easy with a temporary variable. But there are infinite variations we can make:

    if foo(x) and (x := bar()):
    if foo(x) or  (x := bar()):
    if (x := baz()) and foo(x) and (x := bar()):
    if foo(x, y) and (x := bar()) and baz(x) and (y := quux()):
    ...
I fail to see how something is "just sugar" when desugaring it seems to require implementing a general-purpose compiler from "Python" to "Python without ':='".
replies(1): >>17450945 #
dragonwriter ◴[] No.17450945[source]
Yeah, a definition of “syntactic sugar” which as broad as I'd bring proposed would seem to treat everything in any real praxmctical language as “static sugar” over some minimally Turing-complete subset of the language.
replies(2): >>17451773 #>>17452991 #
1. chriswarbo ◴[] No.17452991{7}[source]
> some minimally Turing-complete subset of the language

How about integer arithmetic? That's the programming language Goedel used for his incompleteness theorems (specifically, he showed that the semantics of any formal logical system can be implemented in Peano arithmetic, using Goedel numbering as an example).

I wouldn't call that a useful definition though. There are reasons why we don't treat RAM as one giant binary integer.