John Nagle <nagle at animats.com> writes:

[Stepanov]

makes the point that, for generic programs to work right, the basic

operations must have certain well-defined semantics. Then the same

algorithms will work right across a wide variety of objects.

This is consistent with Python's "duck typing", but inconsistent with

the current semantics of some operators.

This isn't a disaster. You should check that the arguments define the

necessary operations and obey the necessary axioms. Python is already

dynamically typed: this kind of proof-obligation is already endemic in

Python programming, so you've not lost anything significant.

For example, "+" as concatenation makes "+" non-commutative. In other

words,

a + b

is not always equal to

b + a

which is not good.

I think I probably agree with this. Concatenation yields a nonabelian

monoid (usually with identity); `+' is pretty much universally an

abelian group operator (exception: natural numbers, where it's used in

an abelian monoid which extends to a group in a relatively obvious way).

But then we'd need another operator symbol for concatenation.

Nonnegative integers act on strings properly, but the action doesn't

distribute over concatenation, which is also a shame. i.e.,

n*(a + b) != n*a + n*b

But it's a familiar notation, by no means peculiar to Python, and

changing it would be difficult.

Exactly one of

a > b

a = b

a < b

is true, or an type exception must be raised.

This will get the numerical people screaming. Non-signalling NaNs are

useful, and they don't obey these axioms.

I think, more generally, that requiring a full total order (rather than

either a preorder or a partial order) is unnecessarily proscriptive.

Sorting only requires a preorder, for example, i.e., { (a, b) | a <= b

<= a } is an equivalence relation, and the preorder naturally induces a

total order on the equivalence classes. Topological sorting requires

only a partial order, and makes good use of the notation. As an

example, sets use `<=' to denote subsetness, which is well known to be a

partial order.

(I presume you weren't going to deny

a <= b iff a < b or a == b

or

a < b iff b > a

because that really would be bad.)

The basic Boolean identities

(a or b) == (b or a)

not (a or b) == (not a) and (not b)

not (not a) == a

should all hold, or an type exception should be raised.

The first of these contradicts the axiom

x => x or _|_ == x

which is probably more useful. The last can't usefully be true since

`not' is lossy. But I think that, for all values a, b,

not (a or b) == not (b or a) == (not a) and (not b)

not (not (not a)) == not a

which is probably good enough. (The application of `not' applies a

boolean coercion, which canonifies adequately.)

-- [mdw]