
Why operators are useful (2019) - edward
https://mail.python.org/archives/list/python-ideas@python.org/message/52DLME5DKNZYFEETCTRENRNKWJ2B4DD5/
======
msluyter
Slightly orthogonal to the original point, but worth noting that operators can
also increase confusion when they don't interact with each other in expected
ways. For example, in Python, 'x += y' isn't equivalent to 'x = x + y' in all
contexts. Example:

    
    
      >>> a_tuple = ([1, 2], [3, 4], [5, 6])
      >>> a_tuple[2] += ['foo', 'bar']
      TypeError: 'tuple' object does not support item assignment
      >>> a_tuple
      ([1, 2], [3, 4], [5, 6, 'foo', 'bar'])
    

vs

    
    
      >>> a_tuple = ([1, 2], [3, 4], [5, 6])
      >>> a_tuple[2] = a_tuple[2] + ['foo', 'bar']
      TypeError: 'tuple' object does not support item assignment
      >>> a_tuple
      ([1, 2], [3, 4], [5, 6])
    

Edited to add, to understand what's happening here, check out the
documentation on in place operators:
[https://docs.python.org/3/library/operator.html#in-place-
ope...](https://docs.python.org/3/library/operator.html#in-place-operators)

------
Aardwolf
I agree, I find it awkward when a programming language (like Java and JS)
provide operators for their built in types, but if you want to use different
types (say, complex numbers, numbers with different precision, ...) you have
to use much less readable functions instead.

About the argument that operator overloading can be abused for unreadable
code: so can functions, naming, or anything else.

~~~
abjKT26nO8
I much prefer languages which simply do not make a distinction between
functions and operators.

This distinction seems useful only in the context of lower-level languages
like C where the operators translate directly to single machine code
instructions and thus they provide clues about performance.

But in a higher-level language this distinction is superficial. It's just
another function, just with a much shorter name and perhaps using infix
instead of prefix notation. E.g. in Lisps there is no such distinction. "+" is
a valid function name. In Haskell - same, but you also have a built-in syntax
for specifying that you want your function to be called using infix notation
by default.

~~~
Aardwolf
How do you deal with namespaces (or anything analogous like package names)
there?

E.g. if you have a function named "+", it can likely clash with other
functions named "+"

If the programming language supports function overloading that can be resolved
through the types

By the way, I actually like RPN calculators, and low level stack based
programming languages, where operators and functions also work the same way,
but that is mostly interactive or for small self contained programs

~~~
abjKT26nO8
This is a problem of the model of polymorphism a language has. It's orthogonal
to the function/operator distinction. It doesn't arise based on having this
distinction or not.

In Haskell you use typeclasses (you can think of them as Go interfaces or Rust
traits) and without them you cannot introduce nameclashes. In Scheme (the Lisp
that I know)... you don't have anything. You import things from libraries,
define variable bindings and depending on the order of all this your variable
is going to be bound to something, most likely a procedure... but which one?
Depends on the order. Not great. I prefer the Haskell approach to this, but
then Haskell is a bit complicated in other areas. :/

But again, this has little to do with "operators vs functions".

~~~
Aardwolf
> But again, this has little to do with "operators vs functions".

It has to do with it in the sense that you can add package names or namespaces
to function names which are already text, but it looks bad to do this with
symbolic infix operators like "+"

It would be nice if everything that operates looks exactly the same, but maybe
the field of mathematics could start with fixing their notation then instead:
mathematics uses a bit of _everything_ you can imagine:

add, subtract, multiply, division, power, root and log are all binary
operators and instead of making them all look the same, mathematicians have
chosen to:

* infix symbol for add and subtract

* no symbol at all (usually) for multiply

* superscript for power

* horizontal bar and put things on top of each other for division

* another horizontal bar and a superscript on the _left_ for root

* function-name notation with subscript for log

Maybe this is a global optimum that math has converged to because this mix of
different things actually _is_ the easiest way for the human brain to read
formulas the fastest, e.g. the arbitrary grouping that division creates and
the fact that this eliminates the need for some parenthesis, and the easy
visual difference between all the different notations, may really help. In
some programming languages, if you've got dozens of parenthesis or deeply
nested indentation, can you easily tell which argument is to which function?

Or, it's just a historically grown monster that could as well have turned out
entirely different and can have other forms that would be faster to interpret.

~~~
abjKT26nO8
A lot of these things were established long before mathematicians started
talking about functions, let alone give a robust definition of what a function
is. It may have obscured the fact that these things are very similar to each
other.

At the point when there was an effort to formalize these things ideas like RPN
starting popping up.

Mathematics also deals with a much more flexible medium. It's written on a
plane, in 2 dimensions, whereas programmers confine themselves to just one.
Mathematicians are also free to make up their own notation and symbols,
whereas programmers are confined to a single alphabet and most often can't
extend the syntax.

EDIT: Case in point: some mathematicians argued for a while if abs (absolute
value) is a function. Some argued that it's not, because you need two formulas
to define it. When somebody pointed out that (for real numbers) it's just
"sqrt(x^2)", the first ones agreed. Sounds silly in retrospect now that we
have a set-theoretic definition, but at the time it wasn't obvious and all
these functions looked very different from each other.

~~~
nybble41
> Case in point: some mathematicians argued for a while if abs (absolute
> value) is a function. Some argued that it's not, because you need two
> formulas to define it. When somebody pointed out that (for real numbers)
> it's just "sqrt(x^2)", the first ones agreed.

Isn't that a circular definition, though? sqrt(x²) = ±x, which isn't a
function since there are two values in the domain for each value in the range
(other than zero). The version they're equating with abs(x) is the _absolute
value_ of the square root, or abs(x) = abs(sqrt(x²)), which is true but
wouldn't help prove that abs(x) is a function.

Of course, the underlying problem was the premise that a function must be
defined by exactly one formula.

~~~
abjKT26nO8
When people take the square root of a number during working with real numbers,
they almost always mean the non-negative root. It's a convention. Only when
they start considering complex numbers, this convention breaks, and the square
root ceases to be a function - even according to the modern definition -,
because "non-negative" still doesn't narrow it down to a single number in the
general case.

~~~
nybble41
Isn't that basically what I said? The _convention_ is that sqrt(x) is
generally read as abs(sqrt(x)). The _definition_ is that sqrt(x²) = x, which
has positive and negative solutions in x for any x² > 0\. You can choose to
ignore the negative solutions (or the positive solutions) to make it a
function, but I wouldn't consider that any simpler or closer to a single
formula than the piecewise-defined version of abs(x). It's an arbitrary
restriction—much like the mistaken idea that a function must be defined by
exactly one formula.

Why not just say that abs(x) = ±x, ignoring the negative solutions? If you'll
accept "the non-negative square root of x²" then I see no reason to reject
"the non-negative component of ±x". Both are single formulas with positive and
negative solutions combined with a qualifier rejecting the negative solutions.

~~~
abjKT26nO8
_> The definition is that sqrt(x²) = x, which has positive and negative
solutions in x for any x² > 0._

When people write "sqrt", they mean the function, or, "principal square root".
"A square root" is a different thing. Saying "the definition" makes sense only
within the context where the definition is established, otherwise we have to
fallback to the common usage. Wittgenstein would laugh at this conversation.

Also, I'm not arguing for this convention ("it needs to have a single formula
to be a function"). And when you start substituting symbols, almost nothing
withstands this test. Because then you can always always twist the formula
into several cases. And generally thinking about functions as something that
is defined by formulas is a very limiting view. Almost all functions cannot be
defined by formulas.

------
ken
> For mathematicians, operators are essential to how they think.

But programming is not math, just like it isn't text, or color, or datetimes,
or any other type of value our programs deal with. Even in programs I've
written whose purpose is numerical processing, a vanishingly small fraction of
the program involves doing math.

We have no problem using a domain-specific language (like regular expressions)
for the occasions where we need to work with text. Likewise for querying a
database (SQL), or text styling (HTML). We shouldn't need to infect the rest
of the language with all the massive complexity of "operators" just to be able
to write "a+b" in every possible context.

In fact, even when I need to do math, the built-in operators are too weak to
support half the math I need to do. We're paying a huge cost, and not getting
our complexity's worth.

It's not even visually correct. The example here is:

    
    
        x + (y + z) == (x + y) + z
    

which is true in mathematics but not true in most programming languages, where
the values could be implicitly promoted modulo integers of different widths,
or IEEE-754 floating-point values. He says he wants operators so you can see
that addition is associative, but in his language, it's not.

My favorite design, in terms of separating the syntax, is actually Tcl. Remove
the complexity of needing infix arithmetic operators from 99% of the language,
but keep it available for the times when you want it. It still doesn't support
half the math I need, but at least the language was kept simple by having math
("expr") and text ("regexp") off in their own commands.

------
jerome-jh
Operators are a tradeoff between compactness and ambiguity. They add ambiguity
because they are polymorphic, and the order of operations becomes implicit,
governed by the operator's priority (for infix or mixed pre/postfix).

Many programming languages abuse the + operator for string concatenation,
which is not commutative. This is where my tolerance for operators ends.

~~~
pansa2
> Many programming languages abuse the + operator for string concatenation,
> which is not commutative.

This is why Julia uses `*` for string concatenation, which they claim is “more
natural” than `+`. Not sure I agree - I’d rather see a completely separate
operator for concatenation, such as Haskell’s `++` or Lua’s `..`.

~~~
contravariant
Arguably the simplest notation for string concatenation would be just
concatenation (which would be the notation for multiplication, hence why
Julia's choice could be argued to eb the most natural). Unfortunately
programming languages can't quite deal with the ambiguity of having `a (x +
y)` and `f(x)` refer to different operations.

Some languages seem to allow it for string literals though.

~~~
leethargo
In Julia, you can actually omit the `*` for multiplication, if it involves
literals, i.e.

    
    
      julia> x = 10
      10
      julia> 2x
      20
      julia> 2 x
      ERROR: syntax: extra token "x" after end of expression
    

But you have to be careful not to use white space either.

~~~
yunruse
That’s quite clever, actually. This way you could chain a complex string
together with parentheses without needing a lot of pluses.

Not the most useful of features, but it does explain why * is the concat
operator.

------
1wd
The PEP adding a matrix multiplication operator to Python also contains a
section about the advantage of an operator in that case:
[https://www.python.org/dev/peps/pep-0465/#why-should-
matrix-...](https://www.python.org/dev/peps/pep-0465/#why-should-matrix-
multiplication-be-infix)

On the other hand adding a ~ operator similar to R seems was just rejected:
[https://mail.python.org/archives/list/python-
ideas@python.or...](https://mail.python.org/archives/list/python-
ideas@python.org/thread/GE77ACXLJMLHCTK73MKVKEW47GAVQOHZ/)

------
continuational
Yes, well known operators like `+` with well known meanings are useful.

Why would you write a post about this? Is there some kind of disagreement out
there about this? I can't think of any popular language that doesn't have it!

Now, consider this instead:

    
    
        n <> x &~ n
    

Not only do you not know how to _parse_ this without further knowledge, you
also can't _pronouce_ it in a meaningful way or hope to guess what it means.

~~~
csunbird
Is this legal python?

~~~
JNRowe
When you're unsure about how Python treats an expression, then the ast¹ module
is your friend:

    
    
        >>> ast.dump(ast.parse('n != x &~ n'), False)
        Module([Expr(Compare(Name('n', Load()), [NotEq()], [BinOp(Name('x', Load()), BitAnd(), UnaryOp(Invert(), Name('n', Load())))]))])
    

It can make it easier to reason through the curiosities, such as the
precedence in the given example.

 _Note_ : Suggesting `ast` as a solution surely proves continuational's point
;)

1\.
[https://docs.python.org/3/library/ast.html](https://docs.python.org/3/library/ast.html)

------
thomasahle
This may have been mentioned elsewhere, but Python already has a syntactic way
of merging dictionaries:

    
    
      d = {**d1, **d2}
    

which arguably has all the visual benefits argued for.

~~~
bobbiechen
The PEP [1] addresses this:

 _Few people would be able to guess what it means the first time they see it,
or think of it as the "obvious way" to merge two dicts

[...]

{_ _d1,_ _d2} ignores the types of the mappings and always returns a dict.
type(d1)({_ _d1,_ _d2}) fails for dict subclasses such as defaultdict that
have an incompatible __init__ method._

Though I'm not personally convinced that + would be more obvious or that dict
subtypes are merged often enough for this to matter.

[1]
[https://www.python.org/dev/peps/pep-0584/#d1-d2](https://www.python.org/dev/peps/pep-0584/#d1-d2)

~~~
thomasahle
Great PEP :-)

It also mentions further down, that for lists er have both

    
    
      a + b
    

and

    
    
      [*a, *b]
    

As well as other ones I hadn't even thought about like a[len(a):] = b.

------
saagarjha
Unrelated, but I think this is the first time I've seen Mailman with actual
CSS applied to it…

~~~
Cthulhu_
Is that from a mailing list? I really like it, it's much more readable / user
friendly than most mailing list posts I've seen. Call me an inverse luddite or
something but I never could get into mailing lists.

~~~
oblio
This is the kind of thing that wouldn't hurt if would be contributed back to
mailman and became the default. Many of these Open Source projects have really
poorly thought out defaults (if anybody even thought of these default values,
at all).

~~~
elcomet
I think that's the default for mailman 3

~~~
oblio
That's great to hear. I hope more projects upgrade, then :-)

------
ludwigvan
Interesting point. This might also explain why Lisp never really went
mainstream.

~~~
toolslive
It might also be that your brain is just more used (years of extra experience)
to infix notation. So why not take advantage of this ? In the monad world,
people also prefer (>>=) to bind. OTOH, how long does your brain need to get
used to other notations? As an aside, his arguments are completely debunked if
you use RPN.

    
    
       > Commutative: a b + == b a + 
       > Associative: a b c + + == a b + c +
       > ...

~~~
VMG
> It might also be that your brain is just more used (years of extra
> experience) to infix notation.

Maybe infix notations is preferred because it aligns more closely to natural
language grammar (for some languages)

~~~
steerablesafe
Infix notation is preferred because the binary operator is visually close to
both of its operands. The same equations as GP, but == is also postfix:

    
    
       > Commutative: a b + b a + ==
       > Associative: a b c + + a b + c + ==
       > ...
    

As expressions getting larger and larger the operands are getting farther away
from their operators. RPN is great for computers to read and maybe humans to
write (RPN calculators). Not so much for humans to read.

------
xen0
There are two things I hate when dealing with maths heavy code.

    
    
      1. Lack of operator overloads.
      2. Long variable names.
    

The first I find uncontroversial, the second is where I have met with
disagreement.

------
mcherm
So he's saying that operator notation engages "system 1" mental processing
whereas the function call notation requires "system 2".[1]

[1]
[https://en.m.wikipedia.org/wiki/Thinking,_Fast_and_Slow](https://en.m.wikipedia.org/wiki/Thinking,_Fast_and_Slow)

------
regularfry
It's almost like notation is a tool of thought, or something.

~~~
LittlePeter
For the uninitiated, "notation as a tool of thought" is a paper by Ken Iverson
([https://m-cacm.acm.org/magazines/1980/8/10988-notation-
as-a-...](https://m-cacm.acm.org/magazines/1980/8/10988-notation-as-a-tool-of-
thought/pdf)), creator of APL programming language

------
bibyte
For example take a look at the Python API for Z3. Because of operator
overloading it is just beautiful. Much better then raw SMT2.

[https://ericpony.github.io/z3py-tutorial/guide-
examples.htm](https://ericpony.github.io/z3py-tutorial/guide-examples.htm)

~~~
edflsafoiewq
Sympy is nice too.

------
jevgeni
Please, please, please, a pipe forward operator. <3

~~~
smitty1e
Operators for all my friends!

[https://www.ozonehouse.com/mark/periodic/](https://www.ozonehouse.com/mark/periodic/)

~~~
jevgeni
The range exclusive operator is the most friendly, I think.

------
seemslegit
Operators _can_ be useful in a setting where they are expected and natural -
numbers, strings etc. Overriding them for dictionaries, streams custom classes
has been shown to be a bad idea that makes the authors feel clever but results
in code that is both less readable and harder to lookup - your IDE could bring
up the right documentation when asked on dict.union but without some advanced
syntax and type analysis will not be able to guess what | means for dicts and
you'll get the documentation for bitwise or

~~~
gowld
It's weird to blame a language design for what's simply a bug in your IDE.

------
sswaner
Leaving the substantive comments to the mathematicians, this was an enjoyable
read that was easy to understand.

Especially “Of course, it's definitely possible to overdo this -- then you get
Perl.”

------
MaxBarraclough
> it doesn't matter whether the + operator binds tighter to the left or to the
> right

Depending on the language, _(a+b) + c_ may or may not be the equivalent of _a
+ (b + c)_

Interesting. Apparently this is true in Python because it uses bignum numbers.

As I understand it, it's true in Java too, because (signed) integer overflow
is defined to wrap-around. It's not true in C, where signed overflow invokes
undefined behaviour. In C# it depends on the mode whether you get wrap-around
or an exception on overflow.

And of course it's never true with floating-point.

------
mettamage
Reducing cognitive load [1a], transforming representations into something we
can understand (e.g. x86 vs Python) [1b] and engaging our visual cortices [2]
are all amazing things to get ourselves to be more productive.

[1a] Cognitive psychology: working memory 4 to 10 items

[1b] Cognitive psychology: I read it in some textbook how this works well, it
also makes intuitive sense to me.

[2] This idea was stated out loud in one of 3Blue1Brown's videos, and well, in
his case he demonstrates how true it is.

------
jbverschoor
Without operator we’d still be MOVing and ADDing

~~~
saagarjha
We might be using Lisp.

------
nitnelave
In a discussion on the usefulness/overuse of operators, I'm surprised nobody
has mentioned J. Here's quicksort in J:

    
    
      (($:@(<#[), (=#[), $:@(>#[)) ({~ ?@#)) ^: (1<#)
    
    

Good luck understanding that!

------
leethargo
I like the example about the distributive law. But for the associative law,
one could also simply write:

    
    
      add(x, add(y, z)) == add(x, y, z)

~~~
abjKT26nO8
No. That assumes that "add" is left-associative, i.e. it expands to
add((add(x, y), z)). It's not obvious.

~~~
leethargo
OK, but for operators that are both left- and right-associative, this notation
(variable number of arguments) is already used, e.g. in LISP, right?

~~~
abjKT26nO8
Both... at the same time? I.e. they are associative?

If only one at a time, then perhaps there is. But TBH I can't think of an
example from the top of my head right now.

~~~
leethargo
Yes, I meant (fully) associative.

~~~
abjKT26nO8
In this case it's justified, because there is no ambiguity. Just as a
mathematician would write using traditional notation without parentheses. At
the courses I attended in group theory, module theory etc. we were always
required to prove that things are associative before dropping parentheses.
Lisp notation just requires less characters (the '+' isn't repeated), but the
convention is the same.

------
keymone
assumption that ‘add’ is a function of two arguments is wrong if not
deliberately misleading

~~~
AnimalMuppet
Depends on your language. In Lisp, + takes any number of arguments. In C++, it
takes exactly two.

(I'm presuming that by 'add', you mean '+'. If not, then 'add' takes however
many arguments it is defined as a function to take, and that still is
language- or library-dependent.)

(And if you refer to pure math, then add or + is only a function of more than
two arguments if it associates, which is dependent on the argument type.)

~~~
keymone
no, by `add` i mean exactly the function `add`, not + operator. he cherry
picked the example to show how operators are "better", he's wrong. in practice
sum of a sequence is more important concept than it's special case - sum of
two arguments.

~~~
AnimalMuppet
_What_ function 'add'? 'add' in some computer language? If so, _which_
language? Or 'add' in mathematics? If so, what is the difference between 'add'
and '+'?

~~~
keymone
`add` is just another manipulation on guido's part. `sum` is the name of the
concept.

~~~
AnimalMuppet
One more time: _Are you talking about a specific feature in a language, or are
you talking about mathematics?_

~~~
keymone
a concept in programming

~~~
AnimalMuppet
Then what is your basis for saying that the sum of a sequence is more
important than the sum of two numbers? Given associativity, the sum of a
sequence is the same as recursively taking the sum of two numbers. Going the
other direction, if you have the sum of a sequence, then the sum of two
numbers is obviously included in that. The two approaches therefore seem
completely equivalent to me. So why say that one is the more important
concept?

~~~
keymone
Because it’s more general

