3 ms·
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.
by akvadrako 8y ago
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.
- chriswarbo 8y agoNo idea why you think it seems that way. Using a temporary variable is not "just sugar", it's a big change in semantics! Take a more familiar example: x, y = (y, x) Let's pretend that this is "just sugar" for using a temporary variable. What would the desugared version look like? As a first guess, how about: z = (y, x) x = z[0] y = z[1] del(z) This seems fine, but it's wrong. For example, it would break the following code (since `z` would get clobbered): z = "hello world" x, y = (y, x) print(z) A temporary variable would need to be "fresh" (i.e. not clobber any existing variable). As far as I'm aware, there's no syntax for that in Python. What we can do is create a fresh scope, so that the temporary variable would merely shadow an existing binding rather than overwrite it. We can do that with a lambda and the new `:=` syntax: (lambda z: (x := z[0], y := z[1]))((y, x)) However, this alters the semantics because the stack will be different. For example, we might have a class which forbids some attribute from being altered: class A(object): def __init__(self, x): super(A, self).__setattr__('x', x) def __setattr__(self, name, value): if name == "x": raise Exception("Don't override 'x'") return super(A, self).__setattr__(name, value) This will raise an exception if we try to swap two attributes: >>> a = A('foo') >>> a.y = 'bar' >>> print(repr({'x': a.x, 'y': a.y})) {'y': 'bar', 'x': 'foo'} >>> a.x, a.y = (a.y, a.x) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 6, in __setattr__ Exception: Don't override 'x' If we replace this with the lambda version above, the exception will have a different stack trace, which we can catch and process in arbitrary ways. For example, maybe we know that the `foo` function will trigger these exceptions when given `A` objects, but it's a recoverable error. So we "ask for forgiveness instead of permission" by catching these exceptions somewhere, looking checking the stack trace to see if the Nth stack frame is `foo`, and abort if it wasn't. If we "desugared" using the above lambda, the Nth stack frame source of the exception would be a different function (`<lambda>` instead of `foo`) and hence such a program would abort. On the one hand, that's a pretty crappy program. But on the other it demonstrates that "use a temporary variable" is not "easy" in the general case (which is what language implementations must handle).
- akvadrako 8y agoSugar obviously does not require some automatic language translation. It just means that in all cases a human can rewrite it without the new syntax and get the same semantics. Unless you can provide an example where that isn't true, it's just sugar, i.e. unneeded, but maybe desired, syntax.
- chriswarbo 8y ago> Sugar obviously does not require some automatic language translation What makes you say that? I would say it's crucial. Syntactic sugar is anything where we can say "Code of the form 'foo x y z...' is defined as 'bar x y z...'" where both forms are valid in the same language. Such a definition, by its very nature, gives us an automatic translation (look for anything of the first form, replace it with the second). > It just means that in all cases a human can rewrite it without the new syntax and get the same semantics. Yet that's so general as to be worthless. I'm a human and I've rewritten Java programs in PHP, but that doesn't make Java "syntactic sugar" for PHP.
- chriswarbo 8y agoAlso, 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 ':='".
- dragonwriter 8y agoYeah, 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.
- akvadrako 8y agoYour definition was too broad, but the definition others have proposed is too narrow. I would suggest that if you can express the exact same semantics with a "few" more lines then it's just sugar. In the case of x := y, it's always possible to rewrite the program with a "few" extra lines where it means the same thing. It's just combining the assignment and expose operations.
- chriswarbo 8y ago> I would suggest that if you can express the exact same semantics with a "few" more lines then it's just sugar. I agree. The important question is what we mean by "the exact same semantics". I would say that observational equivalence is the most appropriate; i.e. that no other code can tell that there's a difference (without performing unpredictable side-effects like parsing the contents of the source file). Python is a really difficult language for this, since it provides so many hooks for redefining behaviour. For example in many languages we could say that 'x + x' and 'x * 2' and 'x << 1' are semantically the same (they double 'x'), but in Python those are very different expressions, which can each invoke distinct, arbitrary code (a `__mul__` method, an `__add__` method, etc.). The fact they often do the same thing is purely a coincidence (engineered by developers who wish to remain sane). It's fine if we only care about the 'black box' input/output behaviour, but at that point it no longer matters which language we're using; we could have something more akin to a compiler rather than desugaring into expressions from the same language. > it's always possible to rewrite the program There's an important distinction here too. Are we saying that "a semantically equivalent program exists"? That's a trivial consequence of Turing completeness (e.g. there's always an equivalent turing machine; and an equivalent lambda calculus expression; and an equivalent Java program; etc.) Are we saying that an algorithm exists to perform this rewriting? That would be more useful, since it tells us that Rice's theorem doesn't apply for this case (otherwise it might be impossible to tell if two programs are equivalent or not, due to the halting problem). Are we saying that we know an algorithm which will perform this rewriting? This is the only answer which lets us actually run something (whether we call that an "elaborator", a "compiler", etc.). Yet in this case I don't know of any algorithm which is capable of rewriting Python involving `:=` into Python which avoids it. I think such an algorithm might exist, but I wouldn't be surprised if Python's dynamic 'hooks' actually make such rewriting impossible in general. I certainly don't think that a local rewrite is possible, i.e. where we can swap out any expression of the form `x := y` without changing any other code, and keep the same semantics. If it is possible, I would say that such a local, observational equivalence preserving rewrite rule would qualify for the name "syntactic sugar". > It's just combining the assignment and expose operations. I'm not sure what you mean by "expose", and a search for "python expose" didn't come up with anything. It would be nice to know if I've missed out on some Python functionality!