Minor gripe: The ability to articulate Python is not the same as the ability to type Python verbally.

Nobody articulates `def area(width, height): return width * height` as def area, open paren, width, comma, space, height, closed paren...

They would say something like def area as a function of width and height, equal to width by height.


On 12 May 2018 at 18:24, Steven D'Aprano <steve@pearwood.info> wrote:
On Sat, May 12, 2018 at 08:16:07AM -0700, Neil Girdhar wrote:

> I love given compared with := mainly because
>
> Simpler is better than complex:
> * given breaks a complex statement into two simpler ones,

(Foreshadowing: this argument applies to augmented assignment. See
below.)

I don't see how you justify that statement about "given". I think that
it is "given" which is more complex. Significantly so.

Let's compare the syntax:

    target := expr


That is a single, simple expression with a single side-effect: it
assigns the value to <target>. That's it.

Like all expressions, it returns a value, namely the result of "expr".
Like all expressions, you can embed it in other expressions (possibly
wrapping it in parens to avoid precedence issues), or not, as required.

(That surrounding expression can be as simple or complex as you like.)


Now here's Nick's syntax:

    target given target = expr

Exactly like the := version above, we can say that this is a single
expression with a single side-effect. Like all expressions, it returns a
value, namely the result of "expr", and like all expressions, you can
embed it in other expressions.

So far the two are precisely the same. There is no difference in the
complexity, because they are exactly the same except for the redundant
and verbose "given" spelling.

But actually, I lied.

Nick's syntax is *much more complicated* than the := syntax. Any
arbitrary expression can appear on the left of "given". It need not
even involve the binding target! So to make a fair comparison, I ought
to compare:

    target := expr

which evaluates a single expression, binds it, and returns it, to:

    another_expr given target := expr

which evaluates "expr", binds it to "target", evaluates a SECOND
unrelated expression, and returns that.

If you want to argue that this is more useful, then fine, say so. But to
say that it is *simpler* makes no sense to me.

Option 1: evaluate and bind a single expression

Option 2: exactly the same as Option 1, and then evaluate a second
expression

How do you justify that Option 2 "given", which does everything := does
PLUS MORE, is simpler than Option 1?

That's not a rhetorical question.



> which is putting people off in the simple examples shown here (some
> people are annoyed by the extra characters).  However, when given is
> used in a list comprehension to prevent having to re-express it as a
> for loop, then two simple statements are easier to understand than one
> complex statement.

I'd like to see an example of one of these list comprehensions that is
simpler written with given. Here's an earlier example, an exponentially
weighted running average:


average = 0
smooth_signal = [(average := (1-decay)*average + decay*x) for x in signal]
assert average == smooth_signal[-1]


I'm not even sure if "given" will support this. Nick is arguing strongly
that bound targets should be local to the comprehension, and so I think
you can't even write this example at all with Nick's scoping rule.

But let's assume that the scoping rule is the same as the above. In that
case, I make it:

average = 0
smooth_signal = [average given average = (1-decay)*average + decay*x) for x in signal]

Is it longer, requiring more typing? Absolutely.

Does it contain a redundantly repetitious duplication of the repeated
target name? Certainly.

But is it simpler? I don't think so.


If you don't like the exponential running average, here's a simple
running total:


total = 0
running_totals = [total := total + x for x in xs]


versus


total = 0
running_totals = [total given total = total + x for x in xs]


If you don't like this example either, please show me an example of an
actual list comp that given makes simpler.


> This is a
> common difference between code written at programming contests versus code
> written by those same software engineers years later at big companies. 
> Code that you write for yourself can be compact because you already
> understand it, but code you write professionally is read many many more
> times than it is written.  Accessibility is much more important than
> concision.

Ah, nice rhetorical argument: "given" is for professionals, := is a hack
for amateurs and programming contests. Seriously?

Do you use augmented assignment? Your simple versus complex argument for
"given" applies well to augmented assignment.

Augmented assignment combines two conceptual operations:

    x = x + 1
    - addition (for example)
    - assignment

into a single operator:

    x += 1


By your argument, augmented assignment is more complex, and we ought to
prefer splitting it into two separate operations x = x + 1 because
that's simpler.

I think I agree that x = x + 1 *is* simpler. We can understand it by
understanding the two parts separately: x+1, followed by assignment.

Whereas += requires us to understand that the syntax not only calls a
dunder method __iadd__ (or __add__ if that doesn't exist), which
potentially can operate in place, but it also does an assignment, all in
one conceptual operation. There's a whole lot of extra complexity there.

That doesn't mean I agree with your conclusion that we ought to prefer
the simpler version, let alone that the complex case (augmented
assignment) is fit only for programming contests and that professionals
ought to choose the simpler one.



--
Steve
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org