A digression on abuse of operators, notation, and "abuse of notation".

Steven D'Aprano writes:
 > On Sun, Aug 30, 2020 at 05:49:50PM +1200, Greg Ewing wrote:

 > > I don't see why we need to pick one use case to bless as the
 > > official "true" use case. 
 > 
 > We do that for every other operator and operator-like function.
 > 
 > Operator overloading is a thing, so if you want `a + b` to mean
 > looking up a database for `a` and writing it to file `b`, you
 > can. But the blessed use-cases for the `+` operator are numeric
 > addition and sequence concatenation.

Of course the example is abusive.  But not being "blessed" isn't the
reason.  (And shouldn't the notation be "b += a" for that operation? ;-)

 > Anything else is an abuse of the operator: permitted, possibly a *mild* 
 > abuse, but at best it's a bit wiffy.

I don't agree.  The reasons for picking a "blessed" use case for
operators do not include "to exclude other uses".  Rather, they are to
help the language designer decide things like precedence and
associativity, to give developers some guidance in choosing an
operator to overload for a two-argument function, and to provide
readers with a familiar analog for the behavior of an operator.

For example, sequence concatenation is not "blessed because it's
blessed".  It's blessed because it just works.  What does "5 * 'a' +
'b'" mean to the reader?  Is the result 'aaaaab', or 'ababababab'?
The reader knows it's the former, and that is the right answer for the
developer because 'ababababab' == 5 * 'ab' -- there's another way to
construct that without parenthesis.  Why is 5 * 'a' == 'aaaaa'?
Because 5 * 'a' == 'a' + 'a' + 'a' + 'a' + 'a'.  None of this is an
accident.  There's nothing wiffy about it.

Recall that some people don't like the use of '+' for sequence
concatenation because the convention in abstract algebra is that '+'
denotes an commutative associative binary operation, while '*' need
not be commutative.  But I think you don't want to use '*' for
sequence concatenation in Python because 5 * 'a' * 'b' is not
associative: it matters which '*' you execute first.

Where things get wiffy is when you have conflicting requirements, such
as if for some reason you want '*' to bind more tightly than '+' which
in turn should bind at least as tightly as '/'.  You can swap the
semantics of '+' and '/' but that may not "look right" for the types
involved.  You may want operator notation badly for concise expression
of long programs, but it will always be wiffy in this circumstance.
And use of "a + b" to denote "looking up a database for `a` and
writing it to file `b`" is abusive, but not of notation.  It's an
abuse because it doesn't help the reader remember the semantics, and I
don't think that operation is anywhere near as composable as
arithmetic expressions are.

 > If you don't like the term "abuse", I should say I am heavily
 > influenced by its use in mathematics where "abuse of notation" is
 > not considered a bad thing:
 > 
 > https://en.wikipedia.org/wiki/Abuse_of_notation

I think operator overloading and abuse of notation are quite different
things, though.

Using '*' to denote matrix multiplication or "vertical composition of
morphisms in a category" is not an abuse of notation.  It's
overloading, using the same symbol for a different operation in a
different domain.  Something like numpy broadcasting *is* an abuse of
notation, at least until you get used to it: you have nonconforming
matrices, so the operation "should" be undefined (in the usual
treatment of matrices).  If you take "undefined" *literally*, however,
there's nothing to stop you from *defining* the operation for a
broader class of environments, and it's not an abuse to do so.

So if you have a class Vector and a function mean(x: Vector) ->
Vector,

    v = Vector(1, 2, 3)
    m = mean(v)            # I Feel Like a Number, but I am not
    variance = (v - m) * (v - m) / len(v)

"looks like" an abuse of notation to the high school student targeted
by the "let's have a Matrix class in stdlib" thread.  But as Numpy
arrays show, that's implementable, it's well-defined, and once you get
used to it, the meaning is clear, even in the general case.  You could
do the same computation with lists

    v = [1, 2, 3]
    m = sum(v)/len(v)
    variance = (v - len(v) * [m]) * (v - len(v) * [m]) / len(v)

but I don't think that's anywhere near as nice.

To call definitions extending the domain of an operator "abuse of
notation" is itself an abuse of language, because abuse of notation
*can't happen* in programming!  Abuse of notation is synecdoche (use of
a part to denote the whole) or metonymy (use of a thing to represent
something related but different).  But do either in programming, and
you get an incorrect program.  No matter how much you yell at Guido,
the Python translator is never going to "get" synecdoche or metonymy
-- it's just going to do what you don't want it to do.
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/VTQ37LQPU5T3Q22GQYUAJL6WKAE2I4BU/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to