On Thu, Mar 7, 2019 at 9:12 PM Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:

Ka-Ping Yee writes:

> On Wed, Mar 6, 2019 at 4:01 PM Chris Angelico <rosuav@gmail.com> wrote:

> > But adding dictionaries is fundamentally *useful*. It is expressive.

>

> It is useful. It's just that + is the wrong name.

First, let me say that I prefer ?!'s position here, so my bias is made

apparent. I'm also aware that I have biases so I'm sympathetic to

those who take a different position.

TBH, I am warming up to "|" as well.

Rather than say it's "wrong", let me instead point out that I think

it's pragmatically troublesome to use "+". I can think of at least

four interpretations of "d1 + d2"

1. update

2. multiset (~= Collections.Counter addition)

I guess this explains the behavior of removing results <= 0; it makes sense as multiset subtraction, since in a multiset a negative count makes little sense. (Though the name Counter certainly doesn't seem to imply multiset.)

3. addition of functions into the same vector space (actually, a

semigroup will do ;-), and this is the implementation of

Collections.Counter

4. "fiberwise" set addition (ie, of functions into relations)

and I'm very jet-lagged so I may be missing some.

There's also the fact that the operations denoted by "|" and "||" are

often implemented as "short-circuiting", and therefore not

commutative, while "+" usually is (and that's reinforced for

mathematicians who are trained to think of "+" as the operator for

Abelian groups, while "*" is a (possibly) non-commutative operator. I

know commutativity of "+" has been mentioned before, but the

non-commutativity of "|" -- and so unsuitability for many kinds of

dict combination -- hasn't been emphasized before IIRC.

I've never heard of single "|" being short-circuiting. ("||" of course is infamous for being that in C and most languages derived from it.)

And "+" is of course used for many non-commutative operations in Python (e.g. adding two lists/strings/tuples together). It is only *associative*, a weaker requirement that just says (A + B) + C == A + (B + C). (This is why we write A + B + C, since the grouping doesn't matter for the result.)

Anyway, while we're discussing mathematical properties, and since SETL was briefly mentioned, I found an interesting thing in math. For sets, union and intersection are distributive over each other. I can't type the operators we learned in high school, so I'll use Python's set operations. We find that A | (B & C) == (A | B) & (A | C). We also find that A & (B | C) == (A & B) | (A & C).

Note that this is *not* the case for + and * when used with (mathematical) numbers: * distributes over +: a * (b + c) == (a * b) + (a * c), but + does not distribute over *: a + (b * c) != (a + b) * (a + c). So in a sense, SETL (which uses + and * for union and intersection) got the operators wrong.

Note that in Python, + and * for sequences are not distributive this way, since (A + B) * n is not the same as (A * n) + (B * n). OTOH A * (n + m) == A * n + A * m. (Assuming A and B are sequences of the same type, and n and m are positive integers.)

If we were to use "|" and "&" for dict "union" and "intersection", the mutual distributive properties will hold.

Since "|" (especially "|=") *is* suitable for "update", I think we

should reserve "+" for some future commutative extension.

One argument is that sets have an update() method aliased to "|=", so this makes it more reasonable to do the same for dicts, which also have a. update() method, with similar behavior (not surprising, since sets were modeled after dicts).

In the spirit of full disclosure:

Of these, 2 is already implemented and widely used, so we don't need

to use dict.__add__ for that. I've never seen 4 in the mathematical

literature (union of relations is not the same thing). 3, however, is

very common both for mappings with small domain and sparse

representation of mappings with a default value (possibly computed

then cached), and "|" is not suitable for expressing that sort of

addition (I'm willing to say it's "wrong" :-).

--

--Guido van Rossum (python.org/~guido)