Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[??] Transformation and invariant properties.

1 view
Skip to first unread message

R. Michael Saunders

unread,
Jan 17, 1997, 3:00:00 AM1/17/97
to

How does one enforce invariant properties of a class while not requiring
that
features be used in a specified order?

Suppose I have created a BOUNDED_VALUE class that has four features:
set_upper,
get_upper, set_lower, and get_lower. The features transform and access
two
attributes: upper and lower. For the set_upper and set_lower features I
would
like to specify a precondition that requires attribute lower is less
than
attribute upper: e.g. require lower_value.less(get_upper).

A precondition like this seems to require that I use the transforming
features,
set_upper and set_lower, in some specified order.

I recognize that I could create a feature that sets both the upper and
lower
boundary values together however this creates a host other problems
especially
with N number of attribute dependencies.

--
Name: Michael Saunders
Disclaimer: Representing myself only (i.e. not the Boeing Company)
Reply: michael....@boeing.com

R. Michael Saunders

unread,
Jan 17, 1997, 3:00:00 AM1/17/97
to

Roy Phillips

unread,
Jan 17, 1997, 3:00:00 AM1/17/97
to

R. Michael Saunders wrote:
>
> How does one enforce invariant properties of a class while not requiring
> that features be used in a specified order?
>
> Suppose I have created a BOUNDED_VALUE class that has four features:
> set_upper, get_upper, set_lower, and get_lower. The features transform
> and access two attributes: upper and lower. For the set_upper and
> set_lower features I would like to specify a precondition that requires
> attribute lower is less than attribute upper: e.g. require
> lower_value.less(get_upper).
>
> A precondition like this seems to require that I use the transforming
> features, set_upper and set_lower, in some specified order.
>
> I recognize that I could create a feature that sets both the upper and
> lower boundary values together however this creates a host other
> problems especially with N number of attribute dependencies.

If it is not desireable or possible to set related attributes at one
time (and thus check their relationship to each other), then defer the
check until the values are used.

The same problem is encountered when implementing a DATE class: on
the
one hand, you don't want illegal dates added (e.g., 31st Feb), but you
do
not want to constrain the order in which individual elements of the date
are set. A solution is to provide a 'valid: BOOLEAN' operation that
acts
on the current state of the DATE object. Any query on the DATE object
that
requires it to be consitant should satisfy the precondition 'valid =
True'.
The same should be possible for the BOUNDED_VALUE class.

- Roy

Patrick Doyle

unread,
Jan 18, 1997, 3:00:00 AM1/18/97
to

In article <32DFB7...@boeing.com>,

R. Michael Saunders <michael....@boeing.com> wrote:
>How does one enforce invariant properties of a class while not requiring
>that features be used in a specified order?
>
>Suppose I have created a BOUNDED_VALUE class that has four features:
>set_upper, get_upper, set_lower, and get_lower. The features transform
>and access two attributes: upper and lower. For the set_upper and
>set_lower features I would like to specify a precondition that requires
>attribute lower is less than attribute upper: e.g. require
>lower_value.less(get_upper).
>
>A precondition like this seems to require that I use the transforming
>features, set_upper and set_lower, in some specified order.
>
>I recognize that I could create a feature that sets both the upper and
>lower boundary values together however this creates a host other
>problems especially with N number of attribute dependencies.

I see two alternatives:

1. Have a flag that indicates when to start enforcing the invariant.
When the object is first created, this flag would not be set, but once
both bounds are set, the flag should be set, and from then on, the
invariant would be enforced.

2. (Better) Make the constructor for the object require initial values
for all attributes involved in invariants. If it's a true invariant,
it should ALWAYS be true, right from the object's initialization.

Both options reflect the fact that either (1) your invariant is not really
very invariant, or (2) it should be true right from the start.

Hope this helps.

-PD
--
--
Patrick Doyle
doy...@ecf.utoronto.ca

Robert Rock Howard

unread,
Jan 19, 1997, 3:00:00 AM1/19/97
to

In article <32DFB7...@boeing.com> "R. Michael Saunders" <michael....@boeing.com> writes:

> How does one enforce invariant properties of a class while not requiring
> that features be used in a specified order?
>
> Suppose I have created a BOUNDED_VALUE class that has four features:
> set_upper, get_upper, set_lower, and get_lower. The features transform
> and access two attributes: upper and lower. For the set_upper and
> set_lower features I would like to specify a precondition that requires
> attribute lower is less than attribute upper: e.g. require
> lower_value.less(get_upper).
>
> A precondition like this seems to require that I use the transforming
> features, set_upper and set_lower, in some specified order.
>
> I recognize that I could create a feature that sets both the upper and
> lower boundary values together however this creates a host other
> problems especially with N number of attribute dependencies.
>

> --
> Name: Michael Saunders
> Disclaimer: Representing myself only (i.e. not the Boeing Company)
> Reply: michael....@boeing.com


I would add features or constants that allow the client to set the
values to their minimum and maximum extent. Then a "safe" path for
changing the values is always available:

set_upper( maximum_value )
set_lower( mimimum_value )
set_upper( new_upper )
set_lower( new_lower )

or

set_upper_to_maximum
set_lower_to_maximum
set_upper( new_upper )
set_lower( new_lower )


This trick may not be wise if reseting the values causes a
large computation or a reallocation of some sort.

----------------------------------------

The more general way of handling complicated reinitializations
of objects is to add an attribute `initialized : BOOLEAN' and
the feature `set_initialized_state( new_state : BOOLEAN )'.
If we use this in BOUNDED_VALUE, then the invariant looks like:

invariant
initialized implies lower <= upper

Now clients can "turn off" the invariant checking and reinitialize
attributes in any order they wish before reenabling the object.

----------------------------------------

If you restrict all initialization to within creation procedures,
then you never have to worry about this kind of call ordering
problem because the Eiffel runtime is smart enough to turn off all
invariant checking until the creation procedure is finished operating.
Thus this type of problem only occurs when you want to change the
initialization settings of an existing initialized object.

Rock

Robert "Rock" Howard ro...@twr.com
President to...@twr.com
Tower Technology Corp. http://www.twr.com
1501 W. Koenig Lane Tel (512) 452-9455
Austin, TX 78756 USA Fax (512) 452-1721

Ulrich Windl

unread,
Jan 20, 1997, 3:00:00 AM1/20/97
to

In article <32DFB2...@boeing.com> "R. Michael Saunders" <michael....@boeing.com> writes:

> How does one enforce invariant properties of a class while not requiring
> that
> features be used in a specified order?
>
> Suppose I have created a BOUNDED_VALUE class that has four features:
> set_upper,
> get_upper, set_lower, and get_lower. The features transform and access
> two
> attributes: upper and lower. For the set_upper and set_lower features I
> would
> like to specify a precondition that requires attribute lower is less
> than
> attribute upper: e.g. require lower_value.less(get_upper).
>
> A precondition like this seems to require that I use the transforming
> features,
> set_upper and set_lower, in some specified order.
>
> I recognize that I could create a feature that sets both the upper and
> lower
> boundary values together however this creates a host other problems
> especially
> with N number of attribute dependencies.

Why not create a feature with three attributes: a mode and two bounds.
The mode can be set_lower, set_upper, and set_both. Despite of that
you can either use the features in the correct (in respect to the
invariant or precondition) order, or you have a problem with the understanding
of the invariant (I guess).

Ulrich Windl

Chris Flatters

unread,
Jan 20, 1997, 3:00:00 AM1/20/97
to

R. Michael Saunders wrote:
>
> How does one enforce invariant properties of a class while not
> requiring that features be used in a specified order?
>
> Suppose I have created a BOUNDED_VALUE class that has four features:
> set_upper, get_upper, set_lower, and get_lower. The features
> transform and access two attributes: upper and lower. For the
> set_upper and set_lower features I would like to specify a
> precondition that requires attribute lower is less than
> attribute upper: e.g. require lower_value.less(get_upper).
>
> A precondition like this seems to require that I use the transforming
> features, set_upper and set_lower, in some specified order.

I don't see why: you just have to make sure that instances of the
class start off in a valid state. For example

class BOUNDED_VALUE

create

make

feature

make (lower, upper: INTEGER) is
require
lower < upper
do
lower_bound := lower;
upper_bound := upper;
ensure
lower_bound = lower;
upper_bound = upper;
end;

lower_bound, upper_bound: INTEGER;

set_lower_bound (new_bound: INTEGER) is
require
new_bound < upper
do
lower_bound := new_bound
ensure
lower_bound = new_bound
end;

set_upper_bound (new_bound: INTEGER) is
require
new_bound > lower
do
upper_bound := new_bound
ensure
upper_bound = new_bound
end;

invariant

lower_bound < upper_bound

end -- class `BOUNDED_VALUE'

That the lower bound be less than the upper bound is properly part
of the class invariant (an object will not be a valid bounded
value if this isn't true) and this condition should be established
when an instance of the class is created. If the class is in a
valid state then `set_upper_bound' and `set_lower_bound' can be
called in any order. The class invariant is implicitly made
part of the pre and post-conditions of both procedures.

--

Chris Flatters
cfla...@nrao.edu

Kent Tong

unread,
Jan 23, 1997, 3:00:00 AM1/23/97
to

Chris Flatters <cfla...@nrao.edu> wrote:

>I don't see why: you just have to make sure that instances of the
>class start off in a valid state. For example

You have missed the point. Given your code, the following
client code will trigger an exception but actually it
should be valid:

local
x: bounded_value;
do
!!x.make(0, 1)
x.set_lower_bound(3) -- Oops! lower bound = 3 while upper bound = 1
x.set_upper_bound(4)
end

A solution is to temporarily disable the consistency
check:

---
class BOUNDED_VALUE

feature

enable(b: boolean) is
do
isenabled := b
end

invariant
isenabled and then lower_bound < upper_bound

end
---

Then the client code can be modified like:

local
x: bounded_value;
do
!!x.make(0, 1)
x.enable(false)
x.set_lower_bound(3)
x.set_upper_bound(4)
x.enable(true)
end


---
Kent Tong
v3 is out!!!
Freeman Installer ==> http://www.netnet.net/users/freeman/

Ulrich Windl

unread,
Jan 23, 1997, 3:00:00 AM1/23/97
to

In article <32e7015d...@news.wr.com.au> free...@wr.com.au (Kent Tong) writes:

> Chris Flatters <cfla...@nrao.edu> wrote:
>
> >I don't see why: you just have to make sure that instances of the
> >class start off in a valid state. For example
>
> You have missed the point. Given your code, the following
> client code will trigger an exception but actually it
> should be valid:

Please don't mix up a precondition (or any other assertion) with
run-time checks. You may not violate a precondition. If you want to,
then change the precondition.

>
> local
> x: bounded_value;
> do
> !!x.make(0, 1)
> x.set_lower_bound(3) -- Oops! lower bound = 3 while upper bound = 1

The above call is simply incorrect. To make it correct, either modify
the client code (= the above), or change the precondition in the
supplier class.

Ulrich Windl

Ryan Shelswell

unread,
Jan 24, 1997, 3:00:00 AM1/24/97
to m...@mpce.mq.edu.au, ji...@socs.uts.edu.au

Kent Tong wrote:

> There are times when the object is placed
> in a temporary inconsistent state but this doesn't hurt
> as long as this "modification period" ends. The reasoning
> behind Eiffel's invariant semantics is that a single
> exported routine is enough to perform any modification
> so the "modification period" simply doesn't exist. But
> there are cases where we need to build up the data
> structure gradually in a step by step manner. See the
> Builder Pattern (?) in the Design Pattern book for more
> info.

I think you're highlighting an "open problem" in the OO Programming
model (not just of Eiffel, but any progamming system claiming to be OO).
I would say that the OO model implicitly assumes that you can not only
make any modifications within a single routine, but also without making
any external calls to other objects.

In the course of an object updating its state, it may well need to call
other objects. Unfortunately, this can cause a cascade of commands and
queries into the objects in the system, which can eventually result in a
call to the original object. However, this object is still in the
middle of its execution of the routine, and can't be guaranteed to be in
a consistent state at this point (its invariant may be false).

One way to fix this is to lay a responsibility on the designer of the
object to make its invariants true before calls to other objects,
however this can be so restrictive as to make invariants useless, or
cause the designer to begin modelling the state of the object within
itself (which some people have said is bad; I'm not totally convinced
yet).

It seems to me there are objects which are more tightly bound together
than other objects, but there is (currently) no way to indicate these
different relationships formally.

With regard to the Builder pattern, the director and builder objects
need to be "more tightly bound" than the other objects in the system, in
that they trust each other to interact what you've called the
"modification period", but they do not trust any other parts of the
system to interact with them during that period (except for system-level
constructs, such as File IO, for instance).


Ryan

Bart Samwel

unread,
Jan 24, 1997, 3:00:00 AM1/24/97
to

Ryan Shelswell wrote:

> In the course of an object updating its state, it may well need to
> call other objects. Unfortunately, this can cause a cascade of
> commands and queries into the objects in the system, which can
> eventually result in a call to the original object. However, this
> object is still in the middle of its execution of the routine, and
> can't be guaranteed to be in a consistent state at this point (its
> invariant may be false).

So, the call is invalid. Exported routines are not expected to
work when the invariant is not satisfied before the routine is called.
With invariant checking enabled, such a call causes an exception. This
is correct, because the calling object does not know the object is
busy; the exception warns you about a problem in your design.

The same can happen in procedural programming, but in that case, no
exception is raised, and a routine can easily modify a 'busy' data
structure without knowing it is busy. I don't think it's good
programming when a routine can change an object's state when an other
routine is also changing the object.


And now this: A feature call x of object X occurs, and the feature
calls another routine y of object Y. Y contains the only reference to
X, and routine y unknowingly sets the reference to Void. There is no
risk that the garbage collector will throw away X, because x's Current
is still referencing the object, but x is now working on an object that
is unreferenced after the routine finishes. No exception is thrown when
this happens (I think). I'd like to hear some of your opinions: is this
bad practice?


Bart

Bart Samwel

unread,
Jan 24, 1997, 3:00:00 AM1/24/97
to

Simon Willcocks wrote:
> >A solution is to temporarily disable the consistency check:
>
> >class BOUNDED_VALUE
> >
> >feature
> >
> > enable(b: boolean) is
> > do
> > isenabled := b
> > end
> >
> >invariant
> > isenabled and then lower_bound < upper_bound
>
> What happens if you forget to re-enable the checks?

I'd rather call the features 'lock' and 'unlock'. The locked state is
the state with invariant checking enabled.

The solution to forgetting to re-enable the checks is to add 'locked'
to the precondition of every query and routine that does not need the
locking.

Kent Tong

unread,
Jan 25, 1997, 3:00:00 AM1/25/97
to

simon.w...@octel.com (Simon Willcocks) wrote:

>In article <32e7015d...@news.wr.com.au>, free...@wr.com.au (Kent Tong) says:
>>
>>Given [Chris Flatters' <cfla...@nrao.edu>] code, the following


>>client code will trigger an exception but actually it
>>should be valid:
>>

>> local
>> x: bounded_value;
>> do
>> !!x.make(0, 1)
>> x.set_lower_bound(3) -- Oops! lower bound = 3 while upper bound = 1

>> x.set_upper_bound(4)
>> end
>
>You could always do:
>
>do
> !!x.make(0, 1)
> x.set_upper_bound(4)
> x.set_lower_bound(3)
>end
>
>Although, obviously, that's not a general solution.

As noted by you, it won't work for more complicated cases.

>>invariant
>> isenabled and then lower_bound < upper_bound
>
>What happens if you forget to re-enable the checks?

As replied by another poster, by refusing to run other
routines if isenabled = false.

>If you're going to add a method to the class, you might as well add
>change_bounds( lower, upper : integer ) since the invariant does not
>have to hold within a call, only before and after. In other words,
>Mr. Meyer has already solved that problem for you.

This solution has been suggested a few people, but it
is not a general solution.

Kent Tong

unread,
Jan 25, 1997, 3:00:00 AM1/25/97
to

Ryan Shelswell <ry...@mpce.mq.edu.au> wrote:

>I think you're highlighting an "open problem" in the OO Programming
>model (not just of Eiffel, but any progamming system claiming to be OO).
>I would say that the OO model implicitly assumes that you can not only
>make any modifications within a single routine, but also without making
>any external calls to other objects.
>

>In the course of an object updating its state, it may well need to call
>other objects. Unfortunately, this can cause a cascade of commands and
>queries into the objects in the system, which can eventually result in a
>call to the original object. However, this object is still in the
>middle of its execution of the routine, and can't be guaranteed to be in
>a consistent state at this point (its invariant may be false).

I don't think this generally is not a problem. Why not?
Because inside our routine when we make a call to another
object, we are pretty sure about what this call does (but
not how it does that). Since we know what it will do, we
will also know whether it will eventually call us back
and if it will whether we will be able to handle the call.

>With regard to the Builder pattern, the director and builder objects
>need to be "more tightly bound" than the other objects in the system, in
>that they trust each other to interact what you've called the
>"modification period", but they do not trust any other parts of the
>system to interact with them during that period (except for system-level
>constructs, such as File IO, for instance).

Not just system-level classes. In general they shouldn't
trust other classes that hold a circular client relationship
with them.

David Clark

unread,
Jan 30, 1997, 3:00:00 AM1/30/97
to

In article <32DFB7...@boeing.com> "R. Michael Saunders" <michael....@boeing.com> writes:
>From: "R. Michael Saunders" <michael....@boeing.com>
>Subject: [??] Transformation and invariant properties.
>Date: Fri, 17 Jan 1997 17:30:24 GMT

>How does one enforce invariant properties of a class while not requiring
>that features be used in a specified order?

>Suppose I have created a BOUNDED_VALUE class that has four features:
>set_upper, get_upper, set_lower, and get_lower. The features transform
>and access two attributes: upper and lower. For the set_upper and
>set_lower features I would like to specify a precondition that requires
>attribute lower is less than attribute upper: e.g. require
>lower_value.less(get_upper).

>A precondition like this seems to require that I use the transforming
>features, set_upper and set_lower, in some specified order.

>I recognize that I could create a feature that sets both the upper and


>lower boundary values together however this creates a host other
>problems especially with N number of attribute dependencies.

I don't agree with you here.
If the attributes are semantically linked they should be set together.
Take your example of setting lower and upper.
If the currect values are lower = 30, and upper = 40, and you want to set them
to 10 and 20 you have to set lower first.
but if you want to set them to 50 and 60 you have to set higher first.

Perhaps you could give an example of multiple dependencies.
I think that they would
a) be rare
b) require simultaneous setting

David


0 new messages