Another newbie question

Mike Meyer mwm at mired.org
Sat Dec 10 13:59:09 EST 2005


aleax at mail.comcast.net (Alex Martelli) writes:
> 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.

Well, the hard-core solution is to note that your class doesn't really
deal with the type Bar, but deals with a subtype of Bar for which x >
23 in all cases. Since types are represented by classes, you should
subclass Bar so you have a class that represents this subtype. The
class is trivial (with Eiffel conventions):

class RESTRICTED_BAR
   inherits BAR
   invariant x > 23
END

> 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).

I don't think that's the natural place. It's certainly one place to
consider, and may be the best one. However, it might work equally well
to use preconditions on the methods that add the beam and pier to Foo
to verify that the beam and pier in question are valid. If the
attributes of the beam and pier can't change, this would be the right
way to do it.

> 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.
[...]
> (etc).  Changing a lot of dots into underscores -- what a way to waste
> programmer time!  And all to NO advantage, please note, since:

If you knew it was going to be to no advantage, why did you write the
boilerplate? That's also pretty silly. Care to provide reasons for
your wanting to do this?

>> 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.

No, it doesn't have to be checked. Even invariants that don't suffer
from this don't have to be checked. It would be nice if every
invariant was checked every time it might be violated, but that's not
practical. If checking relationships between attributes attributes is
the best you can do, you do that, knowing that instead of an invariant
violation raising an exception after the code that violates it, the
exception may raised after the first method of your class that is
called after the invariant is violated. That's harder to debug than
the other way, but if it's the best you can get, it's the best you can
get.

> 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.

You're right. Choosing to do that would be a bad idea. I have no idea
why you would do that in any language I'm familiar with. I'd be
interested in hearing about the language you use that requires you to
do that.

>> 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".

So do I.

>> > 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).

So you think that a tool being imperfect means you shouldn't use it
all? So you don't test your code, because testing can't reveal all
bugs? That's odd - I always thought that testing was a critical part
of program development. I'd be interested in hearing about any
research that justifies doing development with testing, preferably
with URLs.

     <mike
-- 
Mike Meyer <mwm at mired.org>			http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.



More information about the Python-list mailing list