about sort and dictionary

Alex Martelli aleax at mail.comcast.net
Wed Nov 23 11:11:54 EST 2005


Magnus Lycka <lycka at carmen.se> wrote:
   ...
> >>indicate that the method is a mutator.  So, you can have a.reverse [NOT
> >>mutating a since no !] _and_ a.reverse! [mutating a].  Probably too much
> >>of a change even for Python 3000, alas... but, it DOES make it obvious
> >>when an object's getting mutated, and when not...
> 
> Except when it isn't obvious. What constitutes mutation of an object?

"Potential alteration of the object's state".  [2,2,2] -- a list with
three identical items -- may be subject to any permutation in-place,
including sorting and reversing, without _actual_ alteration of state,
just as, say, L[2]=2 MAY happen not to alter state if L[2] was already 2
before the assignment.  However, such corner cases do not constitute any
deep conceptual blockage to the notion of mutation, any more than, say,
the possibility of state being empty (e.g in an empty tuple) constitues
any to the notion of state.

I classify list and dict methods as mutating and non-mutating in the
Nutshell, for example, and nobody's quibbled about their usefulness.  If
you want to get more formal, you can focus on the post-condition (either
in programming-by-contract terms, or the more formal Hoare and Djikstra
ideas that preceded Pbc) using X' to mean "X as it was on entry":

for any type T,
  a method M of T is said to be non-mutating iff,
    for any instance t of T,
      the strongest postcondition of calling M on t includes
        t == t'
  a method M is said to be mutating if it is not non-mutating

Note how cleanly this deals (by delegating to ==, if you will;-) with
the issue of whether 'non-observable state' (e.g. a cache) "counts" as
state (answer: if it cannot influence the result of == it does not
matter regarding this definition).

Objects which cannot be compared for equality with their peers, or for
which it is conceptually absurd to talk of "as it was", are always going
to be problematic for any system of formal reasoning about programming,
but the problem is with the formalization under such conditions (it's
hard to do most any formal reasoning without equality, for example) and
not with the pragmatics of the situation.

> C++ handles this with 'const', and lets the programmer cheat by using
> transient member variables, since there are cases when you actually
> want to mutate objects a little, but claim that you don't...

I think you mean volatile or mutable rather than transient?  "transient"
is not a keyword in C++, while both volatile and mutable are, with
different semantics.  Anyway, C++'s 'const' is a mess both theoretical
AND practical.  I'm told Ruby's "object-freezing" works better (but I
have no practical experience).

 
> Perhaps we need a.reverse? for just-mutating-a-little reverse as well?
> ;^)

I don't see the alleged humor in this ill-defined concept.  Anyway, a
trailing question mark is used in Ruby to indicate a predicate (a non
mutator which returns a boolean result); a convention similar to that of
exclamation for mutators, though not quite as important IMHO.  I do see
some nice symmetry, supposing for example that S is a mutable string, in
being able to name some of S's methods as:

upper   return an uppercased copy of S, not mutating S
upper!  mutate S in-place to be uppercased
upper?  return True iff S is already uppercased, not mutating S

but (maybe because I have no extensive Ruby real-life experience) the
predicate case, while nice, doesn't seem compelling to me (having to
name it, say, isupper, doesn't appear to cause practical problems).


Alex



More information about the Python-list mailing list