Another newbie question

Alex Martelli aleax at mail.comcast.net
Sat Dec 10 11:15:28 EST 2005


Mike Meyer <mwm at mired.org> wrote:
   ...
> >> it. Nothing you do with zim.foo or zim.foo.bar can change the state of
> >> zim. The only invariants you need to check are bar's, which you do at
> >> the exit to it's baz method.
> > So foo's class is not allowed to have as its invariant any formula
> > depending on the attributes of its attribute bar, such as "bar.x>23" or
> > the like?
> 
> Of course you can do such things. But it's a silly thing to do. That

I guess this is the crux of our disagreement -- much like, it seems to
me, your disagreement with Xavier and Steven on the other half of this
thread, as I'll try to explain in the following.

> invariant should be written as x > 23 for the class bar is an instance

Let's, for definiteness, say that bar is an instance of class Bar.  Now,
my point is that absolutely not all instances of Bar are constrained to
always have their x attribute >23 -- in general, their x's can vary all
over the place; rather, the constraint applies very specifically to this
one instance of Bar -- the one held by foo (an instance of Foo) as foo's
attribute bar.

Let's try to see if I can make a trivially simple use case.  Say I'm
using a framework to model statical structures in civil engineering.
I have classes such as Truss, Beam, Pier, Column, Girder, and so forth.

So in a given structure (class Foo) I might have a certain instance of
Beam, attribute beam1 of instances of Foo, which carries a certain load
(dependent on the overall loads borne by each given instance of Foo),
and transfers it to an instance of Pier (attribute pier1 of instances of
Foo) and one of Girder (attribute girder1 ditto).

Each of these structural elements will of course be able to exhibit as
attributes all of its *individual* characteristics -- but the exact
manner of *relationship* between the elements depends on how they're
assembled in a given structure, and so it's properly the business of the
structure, not the elements.

So, one invariant that had better hold to ensure a certain instance foo
of Foo is not about to crash, may be, depending on how Foo's detailed
structual geometry is, something like:

  foo.beam1.force_transferred_A <= foo.pier1.max_load_top AND
  foo.beam1.force_transferred_B <= foo.girder1.max_load_A

The natural place to state this invariant is in class Foo, by expressing
'foo' as 'self' in Python (or omitting it in languages which imply such
a lookup, of course).

If I'm not allowed (because you think "it's silly"!) to express a class
invariant in terms of attributes of the attributes of an instance of
that class, I basically have to write tons of boilerplate, violating
encapsulation, to express what are really attributes of attributes of
foo "as if" they were attributes of foo directly, e.g.

  def beam1_force_transferred_A(): return beam1.force_transferred_A

(or other syntax to the same purpose).  After going through this
pointless (truly silly) exercise I can finally code the invariant as

  self.beam1_force_transferred_A <= self.pier1_max_load_top AND

(etc).  Changing a lot of dots into underscores -- what a way to waste
programmer time!  And all to NO advantage, please note, since:

> of. Invariants are intended to be used to check the state of the
> class, not the state of arbitary other objects. Doing the latter
> requires that you have to check the invariants of every object pretty
> much every time anything changes.

...in the end the invariant DOES have to be checked when anything
relevant changes, anyway, with or without the silly extra indirection.

But besides the wasted work, there is a loss of conceptual integrity: I
don't WANT Foo to have to expose the internal details that beam1's
reference point A transfers the force to pier1's top, etc etc, elevating
all of these internal structural details to the dignity of attributes of
Foo.  Foo should expose only its externally visible attributes: loads
and forces on all the relevant points, geometric details of the
exterior, and structural parameters that are relevant for operating
safety margins, for example.

The point is that the internal state of an object foo which composes
other objects (foo's attributes) beam1, pier1, etc, INCLUDES some
attributes of those other objects -- thus stating that the need to check
those attributes' relationships in Foo's class invariant is SILLY,
strikes me as totally out of place.  If the state is only of internal
relevance, important e.g. in invariants but not to be externally
exposed, what I think of as very silly instead is a style which forces
me to build a lot of "pseudoattributes" of Foo (not to be exposed) by
mindless delegation to attributes of attributes.


> Invariants are a tool. Used wisely, they make finding and fixing some
> logic bugs much easier than it would be otherwise. Used unwisely, they
> don't do anything but make the code bigger.

I disagree, most intensely and deeply, that any reference to an
attribute of an attribute of self in the body of an invariant is
necessarily "unwise".

> > I'm also quite dubious as to how you can then express some
> > invariants that can be very important
> 
> Not all invariants, pre-conditions or post-conditions can be
> expressed.

Not all can be sensibly CHECKED, but most definitely all can be
EXPRESSED.  Another one of my long-standing contentions with Eiffel is
the inability to express invariants (and pre- and post- conditions)
because the compiler is unable to figure out a decent way to check them;
Z and the VDL, just to name very old design languages, show easy ways to
allow full expression.  Of course, if a condition is of the form, say,
"all items of potentially infinite iterable X satisfy predicate P", it
may not be runtime-checkable -- big furry deal, I want to be able to
EXPRESS it anyway, because apart from runtime checking there are other
precious uses of such conditions (e.g., the compiler might be able to
DEDUCE from such a condition some important optimization, or the
compiletime proof of other assertions, when run in the appropriate
mode).

But that has little to do with the use case I covered here.  Surely
you're not claiming that the structural invariants showing that foo
isn't about to crash *can't be expressed*, just because the obvious way
to express them violates your stylistic preferences (which I find
totally unjustified in this case) and the way that meets your style
preferences, without offering any advantages, requires lots of pesky
useless boilerplate?!  That's not the DEFINITION of "can't"!-)

> > Fortunately, for most of my work, I do get to use Python, Objective-C,
> > or Haskell, which, albeit in very different ways, are all "purer" (able
> > to stick to their principles)...
> 
> I think Eiffel is fairly pure. But practicality beats purity, so there
> are places where it has to give in and deviate from it's
> principles. Clearly, you don't agree with the underlying
> philosoiphy. So don't use it.

I don't, but I also occasionally take the time to explain, as I've done
here, where it (or, in this case, a specific style you claim it requires
-- not using attributes of attributes in a class invariant -- I'd
appreciate URLs to where Meyers dictates this restriction, btw)
interferes with purity, practicality, or both.


Alex



More information about the Python-list mailing list