[Python-ideas] Inline assignments using "given" clauses

Neil Girdhar mistersheik at gmail.com
Sat May 12 20:06:02 EDT 2018


On Sat, May 12, 2018 at 2:28 PM Steven D'Aprano <steve at pearwood.info> wrote:

> Part 2.
>
> On Sat, May 12, 2018 at 08:16:07AM -0700, Neil Girdhar wrote:
> > I love given compared with := mainly because
>
> [...]
> > * Python has a reputation for being working pseudocode, and given reads
> > like pseudocode.  := needs to be deciphered by comparison especially in
> the
> > complicated cases where multiple := operators are used on one line.
>
> Until you learn and become familiar with a new syntax, there is
> generally going to be a period you have to decipher it. I spent a long
> time mentally translating list comprehensions into mathematical set
> builder notation before it became second nature to me.
>
> Even now, I know people who find decorators and comprehensions
> indecipherable. Or at least, so they claim, and they aren't motivated to
> bother learning them. Oh well, that's their loss.
>
> Binding-expressions aren't like asynchronous programming, where the
> entire coding paradigm is different, and you literally have to think
> about your algorithms in another way. Whether you spell the binding
> operation
>
>     target := expr
>     expr as target
>     expr -> target
>     target given target = expr
>     let target = expr
>     : target expr ;
>
> (that last one is stolen from Forth, and should not be taken as a
> serious suggestion)
>
> is just a matter of spelling. We'll get used to whatever spelling it is.
> Some may be more convenient than others, or more error-prone, or may be
> harder to parse, but they're secondary issues. (Important, but still
> secondary.) Fundamentally, the operation is the same regardless of the
> spelling:
>
> - evaluate an expression
> - bind that value to a name
> - return the value
>
>
> (Except of course Nick's "given" suggestion is more complex, since the
> returned value is not necessarily the same as the bound value.)
>
>
> > * there are no difficult mental questions about evaluation order, e.g.,
> in
> > a bracketed expression having multiple assignments.
>
> Aren't there just?
>
>     x = 1
>     print( x + x given x = 50 )
>
>
> Will that print 100, or 51? Brackets ought to make it clearer:
>
>     (x + x given x = 50)  # this ought to return 100
>     x + (x given x = 50)  # this ought to return 51
>
> but if I leave the brackets out, I have no idea what I'll get.
>

It has to be 100, or else outer parentheses change how an expression is
evaluated.

>
>
>
> > Similarly, instead of
> > (a.b(a) given a = c.d())  do I write (a.b(a := c.d())) or ((a :=
> > c.d()).b(a)) ?
>
>
> I would expect that your first example is a NameError:
>
>     a.b(a := c.d())
>
> since Python evaluates arguments to a method *after* looking up the
> method. So that corresponds to:
>
> - look up "a"  # NameError, unless you've already got an "a"
> - look up "a.b"
> - evaluate c.d()
> - assign that value to a
> - pass that to the a.b method we found earlier
>
>
> What you probably want is the second version:
>
>     (a := c.d()).b(a)
>
> which of course looks like utter crap. It might look better if we use at
> least half-way sensible variable names and a more realistic looking
> example, instead of obfuscated one-letter names.
>
>     (widget := widget_builder.new(*args)).method(widget)
>
>
> > * it avoids the question of what happens when := is used in a switch:
> (a
> > if (b := c) else d)   Sometimes you want the assignment to happen
> > unconditionally (a if (b:=c) else d) + b; sometimes you don't.  How do
> you
> > force one case or the other?
>
> I think that this argument is really weak. The obvious answer is, if you
> don't want the assignment to happen unconditionally, then don't do the
> assignment unconditionally. Where you do it depends on when you want the
> assignment to take place. There's no mystery here.


>
> # unconditionally pop from a list
> (spam if mylist.pop(idx) else eggs) + mylist
>
> # conditionally pop from a list
> (mylist.pop(idx) if condition else eggs) + mylist
> (spam if condition else mylist.pop(idx)) + mylist
>
>
> Take your choice of which you want:
>
> (spam if (mylist := expr) else eggs) + mylist
> ((mylist := expr) if condition else eggs) + mylist
> (spam if condition else (mylist := expr)) + mylist
>
> Of course, in the last two cases, you're going to get a NameError when
> you try to add mylist if the ternary operator took the wrong branch.
> Unless you already defined mylist earlier.
>

That's the problem I'm showing.  This is impossible:

(spam if (mylist := expr) else eggs) + mylist

but just fine with given:

((spam
  if mylist
  else eggs) + mylist)
 given mylist = expr)


> If you want something else, you have to explain what you want.
>
>
> > given makes it obvious by separating
> > assignment from the usage of its assignment target.
>
> This is just a purely mechanical source transformation from one to the
> other. Just replace ":" with "mylist given mylist " and you are done.
>

First of all, you cannot convert all := expressions to given expressions.
  Even if you could, the point is that they are separated.  I went over the
separation of concerns in my other mail.


> # unconditional version
> (spam if (mylist given mylist = expr) else eggs) + mylist
>
> # conditional versions
> ((mylist given mylist = expr) if condition else eggs) + mylist
> (spam if condition else (mylist given mylist = expr)) + mylist
>
> If you can write the "given" versions, then just do the reverse
> transformation and replace the redundant verbosity with a colon.
>

You can't always do that.  I've given you two examples now where you cannot
go backwards.

>
> > Style:
> > * it avoids the big style question of when to use and when not to use
> :=.
> > (Even if you ask people not to, people are going to write the
> > expression-statement a := b as a synonym for the statement a = b.)
>
> What if they do? Is it really the end of the world if some ex-Pascal
> coders or people with an over-developed desire for (foolish) consistency
> add a colon to their statement level assignments?


> Some people add semi-colons to their statements too. Do we care? No.
>
> But if it aggitates people so much, then I'm perfectly happy with
> Guido's suggestion that we simply ban top level binding expressions and
> require them to leave the colons out.
>
>
> > * it looks a lot like the existing Python "for" and "if" clauses, which
> > also do in-expression assignments.
>
> "if" clauses do an assignment? Have you borrowed the keys to Guido's
> time machine, and are writing from 2025 and Python 4.2? *wink*
>
> I don't think "given" expressions look even remotely similar to either.
>
>     for target in iterable:
>
>     if condition:
>
>     another_expr given target = expr
>

I meant the clauses not the statements:

(expression
 for x in it
 if x.y
 given z = x.z)

All three clauses have the same format, and obvious temporal ordering.  for
and given both bind a name that is visible to the expression and to clauses
below.


> Aside from "all three use a keyword".
>
>
> --
> Steve
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
>
> --
>
> ---
> You received this message because you are subscribed to a topic in the
> Google Groups "python-ideas" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/python-ideas/CFuqwmE8s-E/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> python-ideas+unsubscribe at googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20180512/9f3a8d39/attachment.html>


More information about the Python-ideas mailing list