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

scientific publications on the "Square-rectangle problem"?

6 views
Skip to first unread message

Leslaw Bieniasz

unread,
Feb 1, 2010, 6:01:05 AM2/1/10
to

Hi,

Are there any scientific publications about the "square-rectangle problem"
(also known as the "circle-ellipse problem")
and its possible treatments in C++?
My search in literature databases does not reveal anything concrete.
I would appreciate pointers to relevant publications, if there are any.

Leslaw

Saeed Amrollahi

unread,
Feb 1, 2010, 6:19:25 AM2/1/10
to

Hi

I think the following are good:
1. http://parashift.com/c++-faq-lite/proper-inheritance.html#faq-21.6,7,8,9,10,11
2. Kevlin Henney. From Mechanism to Method: Total Ellipse, C/C++
Users Journal March 2001.
http://www.curbralan.com/

Regards,
-- Saeed Amrollahi

Balog Pal

unread,
Feb 1, 2010, 8:06:37 AM2/1/10
to
"Leslaw Bieniasz" <nbbi...@cyf-kr.edu.pl>

Scott Meyers wrote it as the 'All birds fly. Penguins are birds. Penguins
don't fly. Uh-oh.' problem.

Stefan Ram

unread,
Feb 1, 2010, 10:39:50 AM2/1/10
to
Leslaw Bieniasz <nbbi...@cyf-kr.edu.pl> writes:
>Are there any scientific publications about the
>"square-rectangle problem" (also known as the "circle-ellipse
>problem")

This has nothing to do whatsoever specifically with C++.

It has been solved by me some years ago - I don't know if
anyone else has published this solution, but I guess so:

This pseudoproblem only comes from the lack of distinction
between a /value/ and a /store/ (i.e., a typed region of storage).

Every square value is a rectangle value.

Every rectangle store is a square store.

(If you remove �value� and �store� from the sentences
above, you will get two contradicting statements.)

These four lines from above are all to be written about
this pseudo-problem. It is this shallow. It does not even
have to do with object-oriented programming, but with
programming in general as soon as typed stores are used.

Most articles on this �problem� do not make this solution clear,
yes, it seems as most authors really are not aware of it
and therefore are a �part of the problem�, not of the solution.

For another example, assume, x e {0,1} and y e {0,1,2,3}
(e = �element of�).

Then, every x is a y, that is x e {0,1,2,3} is always true.
That is,

every x-value is a y-value.

Now, assume, x* s {0,1} and y* s {0,1,2,3}
(s = �is able to store a value from the set ...�).

Then, every y* is an x*, that is y* s {0,1} is always true.
That is,

every x-store is a y-store.

Thus, the general rule is:

Whenever a value set U is a subset of a value set S, then
every U value is an S value, and every S store is a U store.

In C++, an immutable (const) object representing a square or
a rectangle is a �value� in the sense used above, while a
mutable (non-const) object able to store a square value or a
rectangle value is a �store� in the sense used above.

There is another place in C++, where it might have to
be observed (I do not know how this usually is treated in C++):
When U is a subtype of T, then vector<U> will usually /not/ be
a subtype of vector<T>. This is so, because a vector of a type
is not a value of this type, but a store for values of this type.

Citation for this publication:

Stefan Ram, On the Square-Rectangle Problem, Usenet-post
<rectangle-20...@ram.dialup.fu-berlin.de>, 2010.

Tony D

unread,
Feb 1, 2010, 11:21:19 AM2/1/10
to
On Feb 2, 12:39 am, r...@zedat.fu-berlin.de (Stefan Ram) wrote:

> Leslaw Bieniasz <nbbie...@cyf-kr.edu.pl> writes:
> >Are there any scientific publications about the
> >"square-rectangle problem" (also known as the "circle-ellipse
> >problem")
>
>   This pseudoproblem only comes from the lack of distinction
>   between a /value/ and a /store/ (i.e., a typed region of storage).
>
>       Every square    value is a rectangle value.
>
>       Every rectangle store is a square    store.

An elegant observation, but haven't you rather skipped over the
crucial elements: the expectations that come with the terminology,
what functions are appropriate in the interfaces, any inheritance
relationship, justified in terms of implications to correct design and
usage. Personally, I consider the FAQ helpful, but I haven't followed
your link to your complete publication... I'm sure if someone likes
the in-thread summary and is still in need of help of the issue,
they'll do so.

Regards,
Tony

Message has been deleted
Message has been deleted

Alf P. Steinbach

unread,
Feb 1, 2010, 11:47:25 AM2/1/10
to
* Stefan Ram:

> r...@zedat.fu-berlin.de (Stefan Ram) writes:
>> It has been solved by me some years ago - I don't know if
>> anyone else has published this solution, but I guess so:
>
> I have been looking around and found:
>
> http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
>
> Using the terms from this article, I can define a function:
>
> *: T -> T*
>
> that maps a type T of values to a type T* of storage cells
> for such values, and the essential assertion then becomes:
>
> * is contravariant.
>
> That is, using �<=� from this article:
>
> square <= rectangle, but
> rectangle* <= square*.
>
> So, obviously, many computer scientists are aware of
> contravariance - just some authors of web articles about
> �the square-rectangle problem� are not.

Not sure if I follow the above, it looks like obfuscation.

I discussed the ellipse/circle problem in my "pointers" tutorial, which is now
off-web. Perhaps I should put it on Google docs. Essentially, as you point out,
it is about an immutable-values-view versus a modifiable-variables view.

And yes, understanding it is essential for understanding the Liskov substitution
principle (contra-variance and co-variance), and it ties in with "const" in C++.
It also ties in with "in", "in/out" and "out" in languages that support such,
e.g. the partial support in C#. For C++ the only such support is half hidden and
very limited, namely co-variance for pointer or reference function results.


Cheers,

- Alf

Kaz Kylheku

unread,
Feb 1, 2010, 12:51:25 PM2/1/10
to
On 2010-02-01, Leslaw Bieniasz <nbbi...@cyf-kr.edu.pl> wrote:
>
>
> Hi,
>
> Are there any scientific publications about the "square-rectangle problem"
> (also known as the "circle-ellipse problem")

There is no problem.

The ``problem'' is whether you can model a circle as a subtype of
ellipse.

Doing this is only a problem if you allow mutability, without type
change.

Clearly, in mathematics, a circle is a kind of ellipse. But in
mathematics, we don't change an ellipse into a different ellipse while
lying to ourselves that it's still the same object.

Nilone

unread,
Feb 1, 2010, 6:09:28 PM2/1/10
to

Look for the following authors: Birkhoff, Cardelli, Cockburn, Date,
Guttag, Halpin, Kay, Liskov, Lispon, and Wing.

Alistair Cockburn has a good page on the topic, and some references
too:

http://alistair.cockburn.us/Constructive+deconstruction+of+subtyping

HTH

Philip Potter

unread,
Feb 2, 2010, 6:18:58 PM2/2/10
to
On 01/02/2010 16:23, Stefan Ram wrote:

> r...@zedat.fu-berlin.de (Stefan Ram) writes:
>> It has been solved by me some years ago - I don't know if
>> anyone else has published this solution, but I guess so:
>
> I have been looking around and found:
>
> http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
>
> Using the terms from this article, I can define a function:
>
> *: T -> T*
>
> that maps a type T of values to a type T* of storage cells
> for such values, and the essential assertion then becomes:
>
> * is contravariant.
>
> That is, using �<=� from this article:
>
> square <= rectangle, but
> rectangle* <= square*.

I agree that a square value is a rectangle value.
I don't agree that a rectangle store is a square store.

Everything that I can say about a rectangle value also applies to square
values, but I can come up with properties about a square store that do
not apply to a rectangle store. In particular:

A square store is guaranteed to hold a square. A rectangle store has no
such guarantee.

A rectangle store which currently holds a nonsquare rectangle is
certainly not useful as a square store.

Therefore, at this level of abstraction, Liskov substitutability is not
satisfied. I can see that perhaps, if you introduce some more conditions
or clarifications, a rectangle store might be considered to be a square
store, but right now neither seems to be an example of the other.

Phil

tonydee

unread,
Feb 2, 2010, 11:18:44 PM2/2/10
to
On Feb 2, 1:30 am, r...@zedat.fu-berlin.de (Stefan Ram) wrote:

> Tony D <anthony...@gmail.com> writes:
> >what functions are appropriate in the interfaces, any inheritance
> >relationship, justified in terms of implications to correct design and
> >usage.
>
>   Such questions are best discussed given a specific set of
>   requirements for a programming task.
>
>   For example, when someone tells me to write a shape
>   editor where one can edit rectangles and squares, these
>   requirements would eventually lead to a class design,
>   possibly with certain subtype-relationships.
>
>   Without specific requirements, we can not derive enough
>   information from just the words »square« and »rectangle«
>   to get such a design.

Agreed, but is it not the exploration of options and implications in
this space that makes the square/rectangle circle/ellipse problem
space educational?

Message has been deleted
Message has been deleted

tonydee

unread,
Feb 3, 2010, 1:25:42 AM2/3/10
to
On Feb 3, 1:45 pm, r...@zedat.fu-berlin.de (Stefan Ram) wrote:

> tonydee <tony_in_da...@yahoo.co.uk> writes:
> >Agreed, but is it not the exploration of options and implications in
> >this space that makes the square/rectangle circle/ellipse problem
> >space educational?
>
>   Educational or just entertaining?
>
>   Whenever one has a specific assignment, one has a context.
>
>   Given just the words one can only use the mathematical
>   definition, because this is the realm those words come from.

Not so, the words also have meaning in common usage.

>   By this, a circle is the set of all points where the
>   distance from a given point is a given constant while an
>   ellipse is the set of all points in a plane such that the
>   sum of the distances to two fixed points is a given constant.
>
>   By this definition, every circle is an ellipse, because
>   here, set theory applies.

In common usage, calling something an ellipse may imply it's not
(obviously) simply a circle, in the same way that calling some person
an animal is presumed to be making a point. But I play Devil's
Advocate here... it's only a distraction from the OO modeling issues
that these examples are used to illustrate.

>   So in programming, to come to other conclusions, one needs
>   to use other meanings of »circle« and »ellipse«, and those
>   cannot be derived from just those words.

Not so... the programming issues embrace the definitions you've
provided above - accepting those conclusions. Specifically, they
explore what happens when you map that conclusion most simply/naively
into an object model: a Circle object as a special case (subclass) of
a more general Ellipse. The answer is that you have Circles that
can't do what is reasonable to ask of an Ellipse, namely, alter the
ratio of width to height, without ceasing to be Circles. You hide
form Mrs Liskov spotlight. You can try to mitigate the mess by having
the Circles throw exceptions, return a success indicator, assert or
any other manner of error handling/notification, but someone who knows
they're dealing with an Ellipse may take it for granted that these
fundamental operations are always successful and not even read the
documentation let alone check for and handle failure. It's a bad
design in that what's intuitively certain may not work. That level of
presentation of the problem is not so vague and arbitrary that it is


"best discussed given a specific set of requirements for a programming

task" as you've suggested. That is the starting point for
discussion. If you don't get that far, then you're not addressing
yourself to the same problem. It is from an understanding of that
conflict that alternative modeling can be explored, and that's where
things might get vague - although in practice it's not so difficult to
have a reasonably tight and meaningful discussion around this.

Regards, Tony

Message has been deleted

tonydee

unread,
Feb 3, 2010, 2:23:05 AM2/3/10
to
On Feb 3, 3:50 pm, r...@zedat.fu-berlin.de (Stefan Ram) wrote:
> tonydee <tony_in_da...@yahoo.co.uk> writes:
> >explore what happens when you map that conclusion most simply/naively
> >into an object model: a Circle object as a special case (subclass) of
> >a more general Ellipse.  The answer is that you have Circles that
> >can't do what is reasonable to ask of an Ellipse, namely, alter the
> >ratio of width to height, without ceasing to be Circles.  You hide
>
>   Such an object does not truly model a circle, because a
>   circle cannot be modified.

Indeed :-).

> Instead it seem to model a
>   circle storage, which is something different from a circle.
>   The essential property of an ellipse storage is an ellipse
>   /requirement/: It requires a value to be an ellipse (in order
>   to become accepted for storage). Since every circle is an
>   ellipse, every ellipse requirement R also accepts circles. Thus:
>
> x e C  ==>  x e E      (if x is a circle, then x is an ellipse)
> R a C  <==  R a E      (if R accepts ellipses, then R accepts circles)
>
>   The transition from objects to requirements is what actually
>   inverts the direction of the arrow above. It happens to apply
>   to stores, because stores for type T require values to be of type T.

The issue is not with storage: an ellipse can store a circle. But
your statement "if R accepts ellipses, then R accepts circles" is
wrong, given a function R that accepts an ellipse by reference and
attempts to change its height:width ratio. That's the flaw in the
naive OO model... itself an important insight, but again -
understanding this is primarily a basis for discussing how to model
Circles and Ellipses in a more inherently robust fashion....

> >form Mrs Liskov spotlight.  You can try to mitigate the mess by having
> >the Circles throw exceptions, return a success indicator, assert or
>

>   You are calling something a Circle here, what is really a
>   circle storage . This is like calling a numeric variable a
>   number : It is alright as long as you know that it really is
>   storage, not a value.

Wrong. I clearly defined "Circle" and "Ellipse" above in terms of
naive OO modeling, in which Circle is subclassed from Ellipse and
therefore inherits its Ellipse storage.

Regards,
Tony

Nilone

unread,
Feb 3, 2010, 2:25:02 AM2/3/10
to
On Feb 1, 6:23 pm, r...@zedat.fu-berlin.de (Stefan Ram) wrote:

> r...@zedat.fu-berlin.de (Stefan Ram) writes:
> >It has been solved by me some years ago - I don't know if
> >anyone else has published this solution, but I guess so:
>
>   I have been looking around and found:
>
> http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_...)

>
>   Using the terms from this article, I can define a function:
>
>       *: T -> T*
>
>   that maps a type T of values to a type T* of storage cells
>   for such values, and the essential assertion then becomes:
>
>       * is contravariant.
>
>   That is, using <= from this article:
>
>       square     <= rectangle, but
>       rectangle* <= square*.
>
>   So, obviously, many computer scientists are aware of
>   contravariance - just some authors of web articles about
>   the square-rectangle problem are not.

Why do you disable archiving of your posts, Stefan? I think these are
important points.

Nilone

unread,
Feb 3, 2010, 2:27:18 AM2/3/10
to
On Feb 3, 8:50 am, r...@zedat.fu-berlin.de (Stefan Ram) wrote:
> tonydee <tony_in_da...@yahoo.co.uk> writes:
> >explore what happens when you map that conclusion most simply/naively
> >into an object model: a Circle object as a special case (subclass) of
> >a more general Ellipse.  The answer is that you have Circles that
> >can't do what is reasonable to ask of an Ellipse, namely, alter the
> >ratio of width to height, without ceasing to be Circles.  You hide
>
>   Such an object does not truly model a circle, because a
>   circle cannot be modified. Instead it seem to model a

>   circle storage, which is something different from a circle.
>
>   The essential property of an ellipse storage is an ellipse
>   /requirement/: It requires a value to be an ellipse (in order
>   to become accepted for storage). Since every circle is an
>   ellipse, every ellipse requirement R also accepts circles. Thus:
>
> x e C  ==>  x e E      (if x is a circle, then x is an ellipse)
> R a C  <==  R a E      (if R accepts ellipses, then R accepts circles)
>
>   The transition from objects to requirements is what actually
>   inverts the direction of the arrow above. It happens to apply
>   to stores, because stores for type T require values to be of type T.
>
> >form Mrs Liskov spotlight.  You can try to mitigate the mess by having
> >the Circles throw exceptions, return a success indicator, assert or
>
>   You are calling something a Circle here, what is really a
>   circle storage . This is like calling a numeric variable a
>   number : It is alright as long as you know that it really is
>   storage, not a value.

My apologies for contradicting your archiving wishes by quoting you.
I really do want to keep posts like these around.

tonydee

unread,
Feb 3, 2010, 2:54:34 AM2/3/10
to
On Feb 3, 4:23 pm, tonydee <tony_in_da...@yahoo.co.uk> wrote:
> On Feb 3, 3:50 pm, r...@zedat.fu-berlin.de (Stefan Ram) wrote:
> >   The essential property of an ellipse storage is an ellipse
> >   /requirement/: It requires a value to be an ellipse (in order
> >   to become accepted for storage). Since every circle is an
> >   ellipse, every ellipse requirement R also accepts circles. Thus:
>
> > x e C  ==>  x e E      (if x is a circle, then x is an ellipse)
> > R a C  <==  R a E      (if R accepts ellipses, then R accepts circles)

Apologies for saying this statement of requirement was wrong...
reading bits of your post piecemeal as I responded, I lost sight of
the fact you'd specifically defined R as a requirement _on the
storage_, and not a more general requirement re functionality/
interface....

That misunderstanding aside, your model seems to do nothing more than
also suggest a "class Circle : public Ellipse" naive modeling. It
only gets interesting in light of the expected set_height_width(...)
or similar member in Ellipse, which by having an error case
necessarily breaks the expectation that the interface be fully
intuitive and safe.

(Personally, I think a "try_to_set_height_width(...)" or similar is a
practical compromise, ensuring client usage is cued to investigate the
potential failure condition....)

(There's also the even more naive model of "class Ellipse : public
Circle", too obviously broken to be interesting).

Regards,
Tony

tonydee

unread,
Feb 3, 2010, 3:10:34 AM2/3/10
to
On Feb 2, 1:47 am, "Alf P. Steinbach" <al...@start.no> wrote:
> * Stefan Ram:
>
>
>
> > r...@zedat.fu-berlin.de (Stefan Ram) writes:
> >> It has been solved by me some years ago - I don't know if
> >> anyone else has published this solution, but I guess so:
>
> >   I have been looking around and found:
>
> >http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_...)

>
> >   Using the terms from this article, I can define a function:
>
> >       *: T -> T*
>
> >   that maps a type T of values to a type T* of storage cells
> >   for such values, and the essential assertion then becomes:
>
> >       * is contravariant.
>
> >   That is, using »<=« from this article:
>
> >       square     <= rectangle, but
> >       rectangle* <= square*.
>
> >   So, obviously, many computer scientists are aware of
> >   contravariance - just some authors of web articles about
> >   »the square-rectangle problem« are not.
>
> Not sure if I follow the above, it looks like obfuscation.
>
> I discussed the ellipse/circle problem in my "pointers" tutorial, which is now
> off-web. Perhaps I should put it on Google docs. Essentially, as you point out,
> it is about an immutable-values-view versus a modifiable-variables view.
>
> And yes, understanding it is essential for understanding the Liskov substitution
> principle (contra-variance and co-variance), and it ties in with "const" in C++.
> It also ties in with "in", "in/out" and "out" in languages that support such,
> e.g. the partial support in C#.
>
> Cheers,
>
> - Alf

Hi Alf,

I would agree that the problem only arises for mutable values, but I
wouldn't agree that the problem is _about_ constness/mutability.
Still, enforced constness may be a legitimate solution, encouraging
robust usage... whether it's actually more natural and intuitive for
developers may depend upon their educational background and
experience....

I do hope you'll have time to put your articles back online....

> For C++ the only such support is half hidden and
> very limited, namely co-variance for pointer or reference function results.

Good point - an explicit way to mark "out" parameter would be great.
Tuples and structs help somewhat but can be clumsy.

Regards,
Tony

Nilone

unread,
Feb 3, 2010, 3:13:49 AM2/3/10
to

I understood R to refer specifically to ellipse stores. A method
which modifies an ellipse store cannot operate on circle stores, since
mutator methods aren't inherited covariantly *in subtyping*. They are
in OO inheritance, which is one of the reasons OO inheritance isn't
subtyping. Another is dynamic dispatch.

>  That's the flaw in the
> naive OO model... itself an important insight, but again -
> understanding this is primarily a basis for discussing how to model
> Circles and Ellipses in a more inherently robust fashion....
>
> > >form Mrs Liskov spotlight.  You can try to mitigate the mess by having
> > >the Circles throw exceptions, return a success indicator, assert or
>
> >   You are calling something a Circle here, what is really a
> >   circle storage . This is like calling a numeric variable a
> >   number : It is alright as long as you know that it really is
> >   storage, not a value.
>
> Wrong.  I clearly defined "Circle" and "Ellipse" above in terms of
> naive OO modeling, in which Circle is subclassed from Ellipse and
> therefore inherits its Ellipse storage.

OO inheritance isn't subtyping.

Message has been deleted

Philip Potter

unread,
Feb 3, 2010, 4:31:08 AM2/3/10
to
On 03/02/2010 05:02, Stefan Ram wrote:

> Philip Potter <p...@doc.ic.ac.uk> writes:
>> A square store is guaranteed to hold a square. A rectangle store has no
>> such guarantee.
>> A rectangle store which currently holds a nonsquare rectangle is
>> certainly not useful as a square store.
>
> If a rectangle store can store a width and a height, then it
> can store a square by storing the width and the height of
> the square (which happen to be equal to each other for a square).
>
> Assuming for simplicity rectangles and squares with borders
> that are parallel to the axes of the coordinate system, both
> have only the width and height as their properties.
>
> (However, you might define some of these terms in other ways,
> and then you would be right. So here, everything depends on the
> definitions used for these terms.)

My problem isn't that a rectangle store can't store a square -- it can
-- it's that a rectangle store is able to store things other than a
square, while a square store promises to only hold squares.

Imagine a function 'void f (SquareStore &x)' which takes a reference to
a square store, and expects that store to come preloaded with a square
value. It will throw an exception if the store is empty, but it assumes
that if the store is not empty then it contains a square value.

Now, suppose I call that function with a rectangle store instead. That
function now has a rectangle value where it was expecting a square value.

These statements are contradictory, and one has to go:

* A rectangle store makes every promise that a square store makes.
* A square store will only ever hold a square.
* A rectangle store can store non-square rectangles.

Phil

Message has been deleted

Nilone

unread,
Feb 3, 2010, 5:09:54 AM2/3/10
to
> Phil- Hide quoted text -
>
> - Show quoted text -

Since the parameter is in/out, arguments would have to comply with the
intersection of covariant inheritance of value properties and
contravariant inheritance of variable mutators, i.e. you could only
pass a square store and not a rectangle store.

A pure in-parameter would be able to accept any square value including
values of subtypes of square.

A pure out-parameter would be able to accept any square store
including stores of supertypes of square.

As for the contradictory statements, a rectangle store promises to
store square values, just like a square store does. Whether the value
in it is a square or a rectangle isn't a property of the store, but of
the value itself.

Philip Potter

unread,
Feb 3, 2010, 6:43:37 AM2/3/10
to
On 03/02/2010 10:09, Nilone wrote:
> On Feb 3, 11:31 am, Philip Potter <p...@doc.ic.ac.uk> wrote:
>> My problem isn't that a rectangle store can't store a square -- it can
>> -- it's that a rectangle store is able to store things other than a
>> square, while a square store promises to only hold squares.
>>
>> Imagine a function 'void f (SquareStore &x)' which takes a reference to
>> a square store, and expects that store to come preloaded with a square
>> value. It will throw an exception if the store is empty, but it assumes
>> that if the store is not empty then it contains a square value.
>>
>> Now, suppose I call that function with a rectangle store instead. That
>> function now has a rectangle value where it was expecting a square value.
>>
>> These statements are contradictory, and one has to go:
>>
>> * A rectangle store makes every promise that a square store makes.
>> * A square store will only ever hold a square.
>> * A rectangle store can store non-square rectangles.
>>
> Since the parameter is in/out, arguments would have to comply with the
> intersection of covariant inheritance of value properties and
> contravariant inheritance of variable mutators, i.e. you could only
> pass a square store and not a rectangle store.

Which parameter is in/out? A slight variation, 'void f (const
SquareStore &x)', has the same problem, but it's definitely not in/out
wrt to the function.

If you're talking about in/out wrt the store itself, I'm afraid that any
definition of "store" which doesn't let you put things in and take
things out is not one I'm willing to accept.

> A pure in-parameter would be able to accept any square value including
> values of subtypes of square.
>
> A pure out-parameter would be able to accept any square store
> including stores of supertypes of square.

If this is the problem, how would you prevent a SquareStore being used
as an in/out parameter?

> As for the contradictory statements, a rectangle store promises to
> store square values, just like a square store does. Whether the value
> in it is a square or a rectangle isn't a property of the store, but of
> the value itself.

What you say is true, but it leaves out the fact that a square store is
guaranteed to give me a square out of it, while a rectangle store makes
no such promise.

Having read Stefan's comments elsethread, I think I finally get it.

These are guaranteed:

* A rectangle store can accept any square value
* A square store will only emit rectangle values

BUT these are not guaranteed:

* A square store can accept any rectangle value
* A rectangle store will only emit square values

If the stores are write-only, a rectangle store is (substitutable for) a
square store. Code that wants to stuff a square somewhere can use either.

If the stores are read-only, a square store is (substitutable for) a
rectangle store. Code that wants to get a rectangle value can use either.

But if (for some reason) you expect both behaviours, then there is no
simple relationship between square store and rectangle store, and that
is my problem with Stefan's original post.

The key thing for me in the Square-Rectangle problem, and the thing that
this analysis doesn't really cover, is Liskov substitutability. A
derived class must keep all the promises a base class makes. The problem
only arises if a Rectangle makes a promise a Square can't keep, or vice
versa. If this is the case, then Rectangle and Square cannot be directly
descended from each other, though they might be siblings.

Phil

Nilone

unread,
Feb 3, 2010, 9:34:08 AM2/3/10
to
On Feb 3, 1:43 pm, Philip Potter <p...@doc.ic.ac.uk> wrote:
> On 03/02/2010 10:09, Nilone wrote:
>
>
>
>
>
> > On Feb 3, 11:31 am, Philip Potter <p...@doc.ic.ac.uk> wrote:
> >> My problem isn't that a rectangle store can't store a square -- it can
> >> -- it's that a rectangle store is able to store things other than a
> >> square, while a square store promises to only hold squares.
>
> >> Imagine a function 'void f (SquareStore &x)' which takes a reference to
> >> a square store, and expects that store to come preloaded with a square
> >> value. It will throw an exception if the store is empty, but it assumes
> >> that if the store is not empty then it contains a square value.
>
> >> Now, suppose I call that function with a rectangle store instead. That
> >> function now has a rectangle value where it was expecting a square value.
>
> >> These statements are contradictory, and one has to go:
>
> >> * A rectangle store makes every promise that a square store makes.
> >> * A square store will only ever hold a square.
> >> * A rectangle store can store non-square rectangles.
>
> > Since the parameter is in/out, arguments would have to comply with the
> > intersection of covariant inheritance of value properties and
> > contravariant inheritance of variable mutators, i.e. you could only
> > pass a square store and not a rectangle store.
>
> Which parameter is in/out? A slight variation, 'void f (const
> SquareStore &x)', has the same problem, but it's definitely not in/out
> wrt to the function.

I was describing an ideal subtyping language, not C++. My apologies
for not being explicit.

>
> If you're talking about in/out wrt the store itself, I'm afraid that any
> definition of "store" which doesn't let you put things in and take
> things out is not one I'm willing to accept.

I wouldn't accept such a store either. I was only talking about
restrictions on accessing a store polymorphically.

>
> > A pure in-parameter would be able to accept any square value including
> > values of subtypes of square.
>
> > A pure out-parameter would be able to accept any square store
> > including stores of supertypes of square.
>
> If this is the problem, how would you prevent a SquareStore being used
> as an in/out parameter?

It's not a problem, and in general, I wouldn't want to prevent any
store being used as an in/out parameter. I was just trying to express
what you described more clearly in the remainder of your post.

>
> > As for the contradictory statements, a rectangle store promises to
> > store square values, just like a square store does.  Whether the value
> > in it is a square or a rectangle isn't a property of the store, but of
> > the value itself.
>
> What you say is true, but it leaves out the fact that a square store is
> guaranteed to give me a square out of it, while a rectangle store makes
> no such promise.
>
> Having read Stefan's comments elsethread, I think I finally get it.
>
> These are guaranteed:
>
> * A rectangle store can accept any square value
> * A square store will only emit rectangle values
>
> BUT these are not guaranteed:
>
> * A square store can accept any rectangle value
> * A rectangle store will only emit square values
>
> If the stores are write-only, a rectangle store is (substitutable for) a
> square store. Code that wants to stuff a square somewhere can use either.
>
> If the stores are read-only, a square store is (substitutable for) a
> rectangle store. Code that wants to get a rectangle value can use either.

I agree completely up to this point.

>
> But if (for some reason) you expect both behaviours, then there is no
> simple relationship between square store and rectangle store, and that
> is my problem with Stefan's original post.

If you want to do both, then the store must be exactly of the expected
type. Stefan's original post distinguished writing the store from
reading a value, whereas we're now talking both of writing and reading
the store.

The reason for the difference is to express the fact that properties
of the value relate to the value, not the store. I tend to prefer
that style myself.

>
> The key thing for me in the Square-Rectangle problem, and the thing that
> this analysis doesn't really cover, is Liskov substitutability. A
> derived class must keep all the promises a base class makes. The problem
> only arises if a Rectangle makes a promise a Square can't keep, or vice
> versa. If this is the case, then Rectangle and Square cannot be directly
> descended from each other, though they might be siblings.

I currently believe the inability of current OO languages to enforce
LSP is due to a broken inheritance model.

0 new messages