Re: Question on LSP
- From: "Dmitry A. Kazakov" <mailbox@xxxxxxxxxxxxxxxxx>
- Date: Mon, 15 May 2006 11:36:24 +0200
On Sun, 14 May 2006 16:56:32 GMT, H. S. Lahman wrote:
Responding to Kazakov...
But in an OO context T1 and T2 /are/ disjoint concepts by definition.
Linked list and array are types to model the same concept. Why cannot they
be subtypes of each other?
But double dispatch is not readily implemented in all implementation
environments. The OO paradigm needs to be implementable in all
environments. Moreover, it must be implementable in an unambiguous
manner so if one must bootstrap infrastructure to support double
dispatch, one opens the can of worms for validating the infrastructure.
But same could be said about single dispatch and anything else. How OO
paradigm could depend on some lame languages?
Single dispatch is always implementable at the 3GL level because all
3GLs employ procedural block structuring, message passing, and scope.
It is not essential to single dispatch. Pass a message to a tuple, that's
all. In fact, message passing with multiple dispatch is simpler, because
you can treat all parameters uniformly.
But I don't define what it is. Subtyping is a relation which does not
consider construction or the origin of types. It is merely an ability to
get at the methods. Because (to me) there is no properties beyond methods,
this immediately implies properties sharing.
Nonetheless types in general and subtypes in particular need to be
defined with a consistent meta-semantics and there need to be rules
about how they relate to one another. The type of '*' is independent of
the type of 'T' and there can be no subtyping or substitution between
those types.
Hmm, there is no type "*". "T*" is indivisible. "*" is a types algebra
operation, one level above. When applied to a given type T it yields
another type "T*".
Just out of curiosity, if you think of types solely in terms of methods,
how do you define 'object'?
My God, what a question!! (:-))
I never seriously tried to define it formally. A language object should be
something like an annotated value, a type instance. Technically it could be
an ordered pair (T, x). T is a type known statically. x is object's value
of T. At run-time there should be a group of states associated with x,
namely those where the relation hods.
A polymorphic object is (class T, (S, x)), here S is a type in class T. S
might be statically unknown. In a dynamically typed language it becomes
(ANY, (S, x)). [ Dynamically typed languages represent a special case of
statically typed ones.]
But I don't want this. I want 1-1 mapping. So pointer is bound to a class
type. This makes it polymorphic, but it still exactly two types in the
relation.
It is only bound to a class type through instantiation, which was my
point (4). Because that binding is 1:1, it eliminates any polymorphism
that might have been possible at the type level.
Polymorphism remains because it is a class, no specific type of the class
is bound to the pointer until run-time. Example, in Ada:
type T is tagged ... -- Some type
type T_Ptr is access T: -- Non-polymorphic pointer
type Class_T_Ptr is access T'Class; -- Polymorphic pointer
type S is new T with ... -- Derived type
X : T_Ptr := new S; -- Type error, can't bind T_Ptr to S!
Y : Class_T_Ptr := new S; -- OK, can bind no anything from the class of T.
Operations called on X will not dispatch. Ones called on Y will. (see also
below)
(2) The semantics of Object Reference can be defined without regard to
what the types are of the objects to which it is assigned. Those object
types are completely orthogonal to the semantics of being an object
reference.
I don't want this either. There is no untyped referential semantics.
There has to be that level of generalization. Otherwise one would have
to define an infinite number of Object References in the language a
priori to accommodate all possible applications.
Yes, but I see no problem. Same is true for all other composite types,
arrays, records etc. You need some types algebra to generate types.
Formally, if you applied that algebra to all atomic types, you would get
all possible types (an infinite number of). It is a normal mathematical
procedure. [Pointers are better than arrays/records or parametric types,
because the latter can produce an uncountable number of types.]
By separating the
semantics of the Object Reference type from the target object type one
has a much more compact and versatile mechanism because the association
of types can be deferred to the binding (instantiation). But that only
works if the type of Object Reference is completely independent of the
target type.
It is a bad idea, because it is untyped. [There might consistency in
question. I suspect, it might be possible to construct antinomies.]
But the reverse is not true. One cannot reconstruct the state machine
purely from 3GL method calls without interpreting other things (event
queue, STT, naming conventions, etc.). Thus the 3GL type system is
actually less stringent about the constraints on collaboration.
It is an unfair comparison. 3GLs capable to describe sets of states, while
a state machine deals with individual states. It is untyped per definition.
The fact that it might have some typed (or whatever) semantics attached to
states is irrelevant.
BTW, let's not lose sight of the original point in this subthread, which
was your assertion that pointers reference methods. That is just not
true in an OO context.
Of course they do, how else? Each object references its methods! See my
attempt to define "object" above. The first element of the pair is the
type. Each type references its methods, so the object does. [ Isn't it one
of OO corner-stones? ]
My claim is that formally and technically T<-T1 is nothing better than
T<-T*. The case 1, is irrelevant to the issue, because it is covered by
T<-T* (where "class T" is substituted for T).
I strongly disagree with this in an OO context because of (3) above.
You are arguing that T* is equivalent to {T1* | T2*}. That is quite
true for polymorphic dispatch *IF* T is subclassed.
No, that's the case 1 you are talking about. It is the case 2, for which I
claim that T* "subclasses" T, and the class has types {T, T*}. Further it
also "superclasses" T, because "&" becomes a method of T.
That does not make sense to me, even in a type system. The logical
conclusion is that the language must define all the possible types.
Only if you wanted structured types equivalence for pointers. I.e. if you
wished to be able to use T* without prior declaration of. It is a
disputable way, IMO, but it works. I don't expect either structured, or
by-name pointers to impose any problem (see above).
It can be dynamically instantiated as in 2. And I disagree that time of
bindings may influence semantics. It could be a sugar, but there is
something you want to sweeten...
Where did I mention the time of binding? I just said that the language
is able to hide the indirection because a pointer cannot be polymorphic
once it is instantiated.
But it can! You mix types and classes which forces you to a very complex,
yet limited types system.
Not in an OOPL. Once the pointer is bound to a type through
instantiation you cannot reassign it to another type. That constraint
is what allows the language to hide the indirection. Without that
constraint one would also have to explicitly specify what type T* is
currently pointing to (S, U, V, etc.) in every invocation context. That
would make the indirection an explicit two-stage affair. Instead of
(1) get referenced identity (T*)
(2) access referenced target interface (T)
one would have
(1) get referenced identity (T*)
(2) get actual type interface (S)
(3) access referenced target interface (S)
But this is exactly how polymorphic pointers work! 2-3 is dispatch on a
polymorphic pointer. And it is how C++ pointers work, they all are
polymorphic, when the target is a class [which is a design flaw].
BTW, there is a third variant ("fat pointers"):
(1) get actual type interface (S) [from the pointer value]
(2) get referenced identity (S*)
(3) access referenced target interface (S)
There exist:
1. T -- specific type
2. class T -- class of types derived from T
3. T* -- specific pointer to T
4. (class T)* -- pointer to class T, "thin" polymorphic pointer
5. class (T*) -- class of pointers to T, "fat" polymorphic pointer
Mixing 1 with 2 and 3 with 4, is asking for troubles.
The compiler could still hide the (3) indirection, but not the (2) step.
Not to mention the confusion it would create for the developer when T*
is actually pointing to an unrelated S type rather than the original T
type in some context. B-) That sort of anarchy puts software
developers in padded cells.
Only if you force them to "class = type" illusion. They aren't equivalent.
So pointer object is not an object? This is what I meant. You have to
postulate some things, which are not objects; have operations, which aren't
methods; have types, which may not have classes; related to each other, but
in some magic way. I don't want a language like this.
No, a pointer is not an object in an OO sense. It does not abstract an
identifiable problem space entity.
Really? Let I write a C compiler. In that case pointers are definitely in
the problem space.
Anyway, I wouldn't even call a language OO, if it has pointers, but those
aren't objects.
You don't allow aggregates of values?
Actually no (except in private object implementations at OOP time).
How can you create a container of values? Pointer values shouldn't be
aggregated as well, I presume.
Yes, but usually a computing space object. So the notion of 'object'
may be somewhat flexible across 3GLs.
An aggregate is not a reference (though is may be accessed by references
and it may aggregate only references).
So an aggregate containing a pointer to T is itself not a pointer. See, you
have to introduce a third class of things: values, pointers, and aggregates
of pointers. Where it will end?
Multiplicity in relationships is pretty fundamental to OO development
(and the relational data model). There is a fundamental difference
between 1 and many (functional and nonfunctional relationships in RDM
terms). So aggregates in 1:* and *:* relationships are <almost> always
handled specially with collection objects at OOP time.
References are an essential part of OOPL syntax because they support
identity by address in a memory model. Not to mention making things
like aggregates much easier to manage.
Values are of interest in the context of knowledge responsibilities.
One needs some mechanism for knowing something and 'value' is pretty
much it. Since knowledge and behavior are quite different sorts of
properties in an OO context and are handled quite differently (e.g.,
synchronous vs. asynchronous access models in OOA/D), it figures one
needs to distinguish values from operations.
So there are several "classes of things" that are relevant to the OO
paradigm. In fact, I would add objects, properties (subclassed to
values and operations), and relationships to the list of fundamentally
different sorts of things that coexist in an OO context. I could even
make a case for message.
What's the problem with that? Sorting things out by flavor is a common
technique for complexity management.
The problem is that it looks utterly complex and without any obvious need.
I doubt anybody would be able to formalize it. Even from "translationist"
point of view, how can you be sure in what you have translated?
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
.
- Follow-Ups:
- Re: Question on LSP
- From: H. S. Lahman
- Re: Question on LSP
- References:
- Re: Question on LSP
- From: Dmitry A. Kazakov
- Re: Question on LSP
- From: H. S. Lahman
- Re: Question on LSP
- From: Dmitry A. Kazakov
- Re: Question on LSP
- From: H. S. Lahman
- Re: Question on LSP
- From: Dmitry A. Kazakov
- Re: Question on LSP
- From: H. S. Lahman
- Re: Question on LSP
- From: Dmitry A. Kazakov
- Re: Question on LSP
- From: H. S. Lahman
- Re: Question on LSP
- From: Dmitry A. Kazakov
- Re: Question on LSP
- From: H. S. Lahman
- Re: Question on LSP
- From: Dmitry A. Kazakov
- Re: Question on LSP
- From: H. S. Lahman
- Re: Question on LSP
- From: Dmitry A. Kazakov
- Re: Question on LSP
- From: H. S. Lahman
- Re: Question on LSP
- From: Dmitry A. Kazakov
- Re: Question on LSP
- From: H. S. Lahman
- Re: Question on LSP
- From: Dmitry A. Kazakov
- Re: Question on LSP
- From: H. S. Lahman
- Re: Question on LSP
- Prev by Date: Re: Searching OO Associations with RDBMS Persistence Models
- Next by Date: Re: Tell, Don't Ask
- Previous by thread: Re: Question on LSP
- Next by thread: Re: Question on LSP
- Index(es):
Relevant Pages
|