Re: Clean Object Class Design -- Circle/Ellipse

From: Bob Badour <bbadour_at_golden.net>
Date: 9 Sep 2001 20:53:00 -0700
Message-ID: <cd3b3cf.0109091952.4a0630b4_at_posting.google.com>


dmitry_at_elros.cbb-automation.de (Dmitry A. Kazakov) wrote in message news:<3b9b8545.1636346_at_news.cis.dfn.de>...
> On 8 Sep 2001 15:36:59 -0700, bbadour_at_golden.net (Bob Badour) wrote:
>
> >dmitry_at_elros.cbb-automation.de (Dmitry A. Kazakov) wrote in message news:<3b94d133.1837081_at_news.cis.dfn.de>...
> >> On Tue, 4 Sep 2001 03:14:31 -0400, "Bob Badour" <bbadour_at_golden.net>
> >> wrote:
> >>
> >> >>>If we accept your assumption that no such thing as subtype exists, we can
> >> >>>end this conversation now. The conversation is, after all, a discussion of
> >> >>>subtype.
> >> >>
> >> >>How the view that any value has a type disallows subtypes?
> >> >
> >> >The view that any value has one and only one type disallows inherited type,
> >> >which then disallows supertypes and subtypes.
> >> >
> >> >Any value has a specific type and possibly many inherited supertypes.
> >>
> >> That a type inherits means only that methods are inherited.
> >
> >Type inheritance means that types are inherited. Nothing says that
> >inheritance necessarily means method inheritance.
>
> Interface inheritance. All other cases you probably mean are
> irrelevant when no representation is considered.

I believe that interface inheritance is more permissive than type inheritance; although, type inheritance implies interface inheritance.

> >> >If one properly recognizes the type difference between variables and values,
> >> >LSP poses no problem. A variable is a reference to a representation of a
> >> >value, which makes its type different from that of the value whose
> >> >representation it references.
> >>
> >> How it helps in the case of LSP.
> >
> >Apply LSP to subtype/supertype relationships. While a circle value is
> >a subtype of an ellipse value,
>
> [Value is not a subtype]

If you insist, insert "the type of" in front of "a circle value" and in front of "an ellipse value". I omitted it for brevity and (I thought) clarity.

> > a reference to a represented circle
> >value is not a subtype of a reference to a represented ellipse value.
>
> [Reference being a value is also not a subtype]

Again, insert "the type of" where appropriate.

> >> What happens when a variable of the
> >> type Circle is resized as if it were an Ellipse?
> >
> >Since references to represented circle values are not subtypes of
> >references to represented ellipse values, they inherit no operations
> >that allow such resizing. ie. A circle value inherits all operations
> >on ellipse values, but circle variables do not inherit all update
> >operations on ellipse variables.
>
> [Is that ISO terminology, that value *is* a subtype and *inherits*
> something?]

No. A value has types some of which are inherited. I omitted "the type of" above for brevity. I thought it would be clear enough without the phrase.

> If I dare translate it from the "ISO-ish", the above should simply
> mean that objects accessible for update do not inherit update [inout]
> methods.

If you want to focus exclusively on unimportant implementation issues, I suppose you could, but I have no interest in such a discussion. I do not think such a discussion would illuminate much.

> It was the starting point of this bunch of LSP-threads in
> comp.object months ago: "an in-subtyping faces less problems with
> LSP". Nice, but the actual problem is that inout-subtyping is required
> = in your "ISO-ish" we *need* to inherit update operations, because we
> want to *reuse* them.

I don't see how this is a problem. Why would you want to reuse "setFoci" on a circle variable when it has no meaning for circle variables?

Apparently, you mean that you want to reuse "setFoci" on ellipse variables even when the current value of an ellipse variable is a circle value. A well thought language that properly distinguishes values from variables will allow this. The resulting value of the ellipse variable might no longer be a circle value, but it will be a valid ellipse value.

Since the ellipse variable can contain ellipse values, the result is a valid value for the variable. N'est pas?

> And *this* inevitable violates LSP.

Only in languages that apply LSP inappropriately.

> >The OO programming world took existing vocabulary from other
> >disciplines and restricted the meaning -- much as those other
> >disciplines took vocabulary from the english language and restricted
> >the meaning.
> >
> >Before you can redefine the meaning of the word for the OO programming
> >community, you must establish a consensus among its members.
> >Currently, that community generally equates class with data type as
> >evidenced by the ISO standard vocabulary for programming languages.
>
> You may ask the community whether it accepts that class = type [I
> wouldn't do it (:-))], alternatively try google search for this topic
> in comp.object.

Well, I'll continue to use the ISO/IEC standard vocabulary. If the comp.object community object to the standard definition, they can always inform the standards body of their apparent mistake.

I would note in passing C++ equates class with data type, Smalltalk equates class with data type, Simula equates class with data type, Java equates class with data type, VB.net equates class with data type etc.

> >> >>>>>"Most-specific type", 'declared type" and "inherited type" have meaning.
> >> >>>>
> >> >>>>How many types has the value 1, provided that I can derive from
> >> >>>>integer?
> >> >>>
> >> >>>Integer is rational is real is complex.
> >> >>
> >> >>So only 4 types. What if I derive my own type? What if I call my
> >> >>friends to derive a bit more types?
> >> >>
> >> >>>Natural is whole is integer.
> >> >>>
> >> >>>Any others you would like to propose?
> >> >>
> >> >>Lots. The file descriptor for instance. 1 is stdout.
> >> >
> >> >File descriptor is neither a subtype nor a supertype of integer. While it
> >> >uses similar symbols, the properties of a file descriptor, including defined
> >> >operations, are neither a subset nor a superset of the properties of
> >> >integer.
> >>
> >> Did I claim that file descriptor is a subtype of integer? I said only
> >> that it has the literal 1.
> >
> >You originally posited the value 1 in the context of the integer type.
> >If you originally meant the symbol 1, you should have said that.
>
> I do not know what an integer context might be. 1 is a literal.

"How many types has the value 1, provided that I can derive from integer?"

You called it a value and not a literal, and you provided the context of integer in the same sentence.

If you had asked how many types the literal 1 has, I would have told you that the number is unlimited.

> You
> claimed that there is only 4 types it might have.

I most certainly did not. Read above. Please do not put words in my mouth. I am quite capable of doing that myself.

> I argued that there
> may an unlimited number of 1s of different types.

If you had argued that, I would have simply agreed with you. However, that is not what you argued. Re-read your earlier post if you doubt me.

> >> >For instance, what does it mean to add two file descriptors?
> >> >Substract? Multiply?
> >>
> >> They have no meaning, they are operations of some other unrelated
> >> types.
> >
> >And values of those types are different from values of file
> >descriptors even if they use the same symbols for some representation.
>
> Of course values of different types are different even if their
> representation, notation or what else is same. That is my point.

Well, why didn't you just say so? I would have simply agreed. Of course, that does not change the fact that all integer values have multiple types -- including rational type and real type.

> >> But nothing prevents me from making it a subtype of integer, so
> >> that 1 + 1 be 2 = stderr.
> >
> >Nothing prevents you from declaring that pigs are subtypes of integer,
> >but that does not change pigs into integers.
>
> Absolutely. It only makes pigs a subtype of integer

No, it does not do that either. It means that one can declare things that are nonsensical.

> which only means
> that pigs would inherit some integer methods.

It would not necessarily even do that. It would attempt to expand the range of integers to contain things that are not integers -- namely, pigs.

> It says nothing about
> what a pig or integer is, or how it is represented.

Nonsensical statements say nothing at all.

> >> >Again, I must point out that your example uses an artifact from a non-object
> >> >oriented system.
> >>
> >> Maybe the artifact is that a particular system is unable to explain
> >> what appears "artefact"? I wouldn't blaim OO for this.
> >
> >I never did blame OO for this. I only stated that your non-OO example
> >is inappropriate for a discussion of OO.
>
> Some people find OO appropriate to explain the whole universe, not
> only its unmeasurably small fraction called programming.

I don't. However, I agree that some people will try anything once.

As I said previously, non-OO examples are inappropriate for a discussion of OO. In the end, the OO response to "Dr. Dr. It hurts when I do that!" is "Don't do that."

> Though I
> disagree with that broad view, I am definitely sure that OO is able to
> explain types and their relationship.

I think that Date's work advances the state of the art in that respect -- or at least clarifies it.

> >> >>Literal can be viewed as a read-only variable. Both literals and
> >> >>constants might require elaboration (construction) if they have some
> >> >>complex types.
> >> >
> >> >This describes exactly why neither literals nor named constants are values!
> >> >They are variables constrained to a single value.
> >>
> >> Yes and this supports my view that values do not exist without
> >> variables.
> >
> >No, it doesn't. Variables do not exist without values, which says
> >nothing about the existence of values.
>
> You said that constants, literals, variables are not values. I agreed.
> What remains? Parameters? They are not too.

Values are values. The variables that contain values are not.

> >> >>Value of an ellipse cannot be resized because the result [in most
> >> >>cases] is another value.
> >> >
> >> >One cannot resize an ellipse value because values do not change -- ever. One
> >> >can resize an ellipse variable because variables do change.
> >>
> >> "Resize" means "to call a routine named Resize with an actual
> >> parameter which value is of ellipse type".
> >
> >Since "Resize" changes the actual parameter, one must pass a variable
> >-- not a value. Since a circle variable is not an ellipse variable,
> >one cannot pass it.
>
> Which means that one cannot inherit and so reuse Resize.

Since the type of a circle variable is not a subtype of the type of an ellipse variable, I see no reason for circle variables to inherit Resize. Why would it have meaning to do so?

> This is what
> I meant saying that the cure is worse than the disease.

You do not yet understand the cure. I doubt you will understand the cure until you stop trying to focus exclusively on physical implementation details. Focusing on implementation details prior to understanding the logical details puts the cart before the horse.

> >No. As Jan pointed out, "the type of a variable referencing a circle"
> >is a supertype of "the type of a variable referencing an ellipse".
>
> Aha. So your proposal is to make Circle an in-subtype and
> simultaneously inout-supertype of Ellipse.

Again, introducing this new terminology, while possibly correct in comp.lang.ada or alt.sex.sadomasochism, adds nothing to the current discussion. If you need to translate the other concepts to those terms to understand the concepts, please do so quietly.

> Although it is impossible
> in all OO languages I know (unfortunately), this chimera does not
> solve the LSP problem, because there is an absolute simmetry in how
> specialization + out-subtyping breaks LSP and generalization +
> in-subtyping does same. Ellipse is an generalized Circle, so here you
> are. Example: try to apply Circle's GetArea to an ellipse. This is no
> C/E solution, and there is no one compatible with LSP.

You are confusing yourself with implementation details. One cannot apply the GetArea property of a circle value to an ellipse value unless the ellipse value happens to be a circle value as well.

> >> On which type setFoci is defined? Does it violate LSP?
> >
> >It is defined on "the type of a variable referencing an ellipse". It
> >does not violate LSP.
>
> Because it is not inherited. No subtyping - no LSP problems.

Neat huh?

> >> >>2. void Foo (const X); is Foo an undate or non-update operation?
> >> >
> >> >...in C++... it is impossible to say, because C++ makes insufficient
> >> >distinction between variable and value:
> >> >
> >> >1. Foo could update a global or static variable.
> >> >2. Foo could be a member function that updates the variable referenced by
> >> >"this".
> >> >3. Foo could be a non-update operation, in which case it performs little of
> >> >enduring use.
> >>
> >> From this point of view there is no non-update operations, because any
> >> operation always has side effects [memory mapping, stack use etc].
> >
> >I disagree. You are again trying to introduce physical implementation
> >irrelevancies into a discussion of the logical concept of type
> >inheritance.
>
> Why is then relevant your reference to global and static variables
> that might be updated?

You introduced the physical details in your question. While one might assume that the question is irrelevant, it offered an example of how C++ makes insufficient distinction between values and variables. Since the example could just as easily be Java, it offers an example of how Java makes the same mistake.

In a well thought language, the example you gave should be rejected as syntactically incorrect or flagged as semantically useless.

> >The ability to distinguish a specific type from an inherited type does
> >not change the fact that a given value can have many types.
>
> The ability to distinguish value of a specific type from a value of
> some of its bases.

Same value, same set of types.

> >> Should your statement mean that any non-polymorphic subroutine is not
> >> an issue regarding LSP and subtyping.
> >
> >An operation defined on values of a supertype must operate on values
> >of all subtypes; otherwise, they are not subtypes. If you assume a
> >non-polymorphic operation, you assume no subtypes, which makes the
> >issue irrelevant.
>
> ?
>
> >> What makes you think so:
> >>
> >> 1. Non-polymorphic subroutines do not exist
> >
> >They exist for any type with no subtypes.
>
> That's wrong. A non-polymorphic subroutine can be inherited.

If it is inherited, it is polymorphic. Remember that we are discussing logical concepts and not physical implementations.

> >> 3. Non-polymorphic subroutines never violate LSP
> >> 4. Non-polymorphic subroutines always violate LSP
> >
> >Since there are no subtypes to substitute, they cannot violate LSP.
> >LSP does not apply in this situation.
>
> class Ellipse
> {
> public :
> void Resize (unsigned NewXAxis, unsigned NewYAxis);
> };
>
> Resize is not polymorphic here [at least until it is not overloaded,
> when overloading is counted as polymorphism].

I assume the syntax you use means Resize changes variables declared as Ellipse variables. The language looks like C++ or Java. Both of these languages only demonstrate that they do not distinguish adequately between value and variable.

They make the mistake of assuming that subtype variables should inherit update operations when they should not.

I do not count overloading as polymorphism; although, one can implement a crude polymorphism with overloading.

> >> >>>Not my point. My point is: Type does not determine representation and
> >> >>>representation does not determine type.
> >> >>
> >> >>Then you should explain how dispatch can work, if there is not way to
> >> >>determine the type.
> >> >
> >> >Dispatch is ultimately an irrelevant implementation detail when discussing
> >> >logical type inheritance. A DBMS might derive the type any number of ways
> >> >just as it might derive the representation any number of ways.
> >>
> >> Dispatch = [run-time] selection of an implementation of some
> >> polymorphic subprogram.
> >
> >Since the DBMS constructs the physical access path, it might not
> >dispatch anything. It might implement the operation in place with the
> >access path.
>
> I am talking about the case when the type is unknown untill run time.
> Then dispatch is *inevitable*. This [late binding] has nothing to do
> with implementation or representation.

Since the DBMS might leave some or all of the access path undetermined until runtime, it is irrelevant implementation.

> >> Provided that implementations are written by
> >> the user, how could it be irrelevant which one is selected?
> >
> >Here is a fundamental misunderstanding. When one assumes physical
> >independence, one does not assume that the ultimate implementation is
> >written by the user. One assumes quite the opposite.
>
> Ultimative implementation is of no interest as well as the color of
> the box running that ultimative implementation. Implementation is what
> a user writes.

I disagree. The last statement runs contrary to the principles of sound database management.

> >> It is very doubtful that
> >> whether to declare a variable DenseMatrix or SparseMatrix is
> >> irrelevant if the matrix is 100,000x100,000.
> >
> >Since each will obviously lead to very different performance
> >characteristics, and since those performance characteristics will
> >depend on many potentially unpredictable factors including data
> >distribution and context of use, the DBMS should choose based on which
> >representation will perform the given task better.
>
> Egh, you know, it is halting problem.

I disagree. In the halting problem, no DBA can provide hints.

> >> It is also thinkable to
> >> have some IntelligentMatrix which would try automatically get an
> >> advantage from the distribution of non-zero elements, but in any case
> >> those three are different types.
> >
> >The DBMS has no need for IntelligentMatrix because the DBMS already
> >performs the proposed function of the representation.
>
> I am starting to want your DBMS to be the next president. (:-))

Unfortunately, database management and executive decision are different tasks requiring very different attributes.

> >> >They have exactly the same values and exactly the same properties, including
> >> >operations.
> >>
> >> Do you really believe that (int) 1 and (double) 1.0 have same
> >> properties?
> >
> >Since they are different types, why should they? We were discussing
> >multiple possible representations of a single type and not multiple
> >types.
>
> So int is not a subtype of double?

It is, and they have different properties. For instance, a circle has a radius while an ellipse does not. If you recall, a subtype has a superset of the properties of its supertype.

> >> >>>>And how do you call the set of all values having exactly one [of that
> >> >>>>multiple] representation. I would call it a type. And you?
> >> >>>
> >> >>>I call it the empty set. All of the values have both representations.
> >> >>
> >> >>Simultaneously? So the representations are undistinguishable = same.
> >> >
> >> >Yes. At the logical level, the cartesian representation of Complex(0,1) is
> >> >indistinguishable from polar representation of Complex(1,Degrees(90)).
> >>
> >> Which means that there is exactly one representation.
> >
> >Internally, there are two representations. Externally, there is a
> >single type.
>
> What happens internally is no mater.

Exactly why I have often stated that internal representation is irrelevant to the discussion.

> >> >>>>>>class Circle : public RosaElephant, public Ellipse {...}
> >> >>>>>>
> >> >>>>>>You cannot pass such circle to an ellipse method without conversion.
> >> >>>>>
> >> >>>>>In the specific implementation of your compiler, perhaps, but that says
> >> >>>>>nothing about subtypes and supertypes in general.
> >> >>>>
> >> >>>>In which compiler it could be possible?
> >> >>>
> >> >>>In any compiler that fully supports polymorphism.
> >> >>
> >> >>How it supposed to work, provided that according to you values of
> >> >>Circle are values of RosaElephant and simultaneously of Ellipse?
> >> >
> >> >Why would it not work? Variables of RosaElephant can have any value of
> >> >Circle as well as any other value of RosaElephant. Variables of Ellipse can
> >> >have any value of Circle as well as any other value of Ellipse. Variables of
> >> >Circle can have any value of Circle.
> >>
> >> Because in this case the domain sets of RosaElephant and Ellipse would
> >> intersect. But RosaElephant has no axis, it has a big salivous trunk
> >> and a rose-colored bow with "Rosa" written upon it. You could try to
> >> resize it, but it will surely take offence.
> >
> >Hey, it was your example not mine. If you want to criticize it as
> >semantically inappropriate, choose a different example; otherwise, it
> >is only a straw man.
>
> It has nothing to do with semantic. It shows that multiple inheritance
> and building new types using cartesian product is incompatible with
> your theory.

Unfortunately, it does not do that either. While RosaElephant has no axis, Circle does because it is also Ellipse. Since resize operates on [Quoted] Ellipse variables and Circle variables are not Ellipse variables, it should take offence. A well thought language will issue a compile-time error. Received on Mon Sep 10 2001 - 05:53:00 CEST

Original text of this message