On Wed, Sep 30, 2020 at 4:24 PM Ben Rudiak-Gould <benrudiak@gmail.com> wrote:
On Wed, Sep 30, 2020 at 1:43 PM David Mertz <mertz@gnosis.cx> wrote:
Fluent programming is uncommon in Python, and hence few methods return a call of the same or similar type.

I think that if you include built-in operators as (shorthand for) method calls, and you count the number of occurrences in typical Python programs rather than the number of distinct species, it's common.

I think this misses the point.  Yes, the methods that implement operators are ALL "fluent" for Python numeric types.  I can write:

    a + b * c / d % e ** f - g

Subject to operator precedence, every one of those "method calls" represented by the symbols produces a new value of the same or similar type (sometimes we coerce up the numeric hierarchy).  Of course if you were to write that out using a bunch of dunder methods, you'd get a "fluent programming" style.

The dunders are very self-consciously different from "regular" methods.  One of the biggest and clearest differences is that most/many dunders are specifically designed for fluent programming... but they are designed that way because the methods themselves are rarely used directly, but rather the mathematical symbols are instead.
 
The fact that methods are inconsistent about mutating in place versus returning a new object, and it's sometimes hard to guess from the name which will happen, is unfortunate but I don't think this proposal makes the problem any worse.

They are inconsistent, but not THAT inconsistent.  Mutation of mutable collections almost always returns None.  Almost everything that doesn't return None on a mutable object returns an aggregation or reduction of the collection, not a transformation.  Yes, I can find exceptions, and so can you.  But as a mental model, that comes pretty close.

That is to say, in PYTHON.  Yes, Pandas and some other libraries encourage a fluent style.  But very little within Python itself does (except strings).

Here are a few cases where the syntax would be useful for values other than strings:
    named_tuple .= _replace(...)

In all the years I've used and taught namedtuples, I think I've never used the ._replace() method.  The leading underscore is a hint that the method is "private" (that said, I've used the "private" ._asdict() fairly often).
 
    pil_image .= crop(...)

Not Python, but an external library.  Pillow doesn't really encourage a fluent style either, but most methods do return some sort of image, admittedly.
 
    numpy_array .= transpose()

More often than not in NumPy (again, not Python itself), you will be led astray trying to do stuff like this.  Yes, .transpose() works.  On the other hand, why on earth would you ever not just spell that `numpy_array.T` which is so much easier?!
 
    node .= get_{parent,left_child,right_child}()

I'm not sure if this is meant to refer to some particular library or a hypothetical.  Yes, I can imagine this working with a recursive descent into a binary tree.  But it's no big win. I'd be less likely to want to rebind the descent to the same name, but maybe.
 
    mutable_thing_from_caller .= copy()

This will DEFINITELY fail code review.  Why on earth would you replace a name with a copy of its initial object like that?!  I'm sure there is SOME scenario where that would be desirable, but it's got to be unusual (and more likely a bug than an intent).
 
If this feature were somehow to get into the language, yes I can imagine a few places where it would be usable.  But I can imagine vastly more cases where it would be an attractive nuisance.

--
The dead increasingly dominate and strangle both the living and the
not-yet born.  Vampiric capital and undead corporate persons abuse
the lives and control the thoughts of homo faber. Ideas, once born,
become abortifacients against new conceptions.