* Ada Interfaces and the Liskov Substitution Principle @ 2007-05-23 19:47 Stefan Lucks 2007-05-23 20:32 ` Ludovic Brenta ` (2 more replies) 0 siblings, 3 replies; 81+ messages in thread From: Stefan Lucks @ 2007-05-23 19:47 UTC (permalink / raw) Hi all, to me, it seems as if Ada 2005 is bluntly violating the Liskov Substitution Prinicple. E.g., define ---Start--- package Parents is type Parent is Interface; -- primitive operation procedure Do_Something(Self: in out Parent) is abstract; -- class-wide operations procedure Do_Nothing(From: in Parent'Class; To: out Parent'Class); procedure Convert(From: in Parent'Class; To: out Parent'Class); end Parents; ----End---- with the primitve operations defined in ---Start--- package body Parents is procedure Do_Nothing(From: in Parent'Class; To: out Parent'Class) is begin null; -- warning: "To" is never assigned a value. end Do_Nothing; procedure Convert(From: in Parent'Class; To: out Parent'Class) is begin To := From; end Convert; end Parents; ----End---- Now, Do_Nothing is harmless (except for generating a compiler warning). But Convert uses the assignment ":=", which seems OK, as the interface Parents.Parent is not limited. Now there are two children to inherit from Parents. The first one is Child.Object: ---Start--- with Parents; package Child is type Object is new Parents.Parent with private; procedure Do_Something (Self: in out Object); private ... doesn't matter ... end Child; ----End---- Child.Object gets everything there are no limits to using Parents.Parent'Class. But look at Stepchild.Object: ---Start--- with Parents, Ada.Finalization; package Stepchild is type Object is new Ada.Finalization.Limited_Controlled and Parents.Parent with private; procedure Do_Something (Self: in out Object); private ... doesn't really matter ... end Stepchild; ----End---- Stepchild.Object is really a poor cousin, prohibited from using Parent.Convert: ---Start--- with Ada.Text_IO, Parents, Child, Stepchild; procedure Family is procedure All_Well(X: in out Parents.Parent'Class) is begin X.Do_Something; end All_Well; Procedure Not_So_Well(X, Y: in out Parents.Parent'Class) is begin Parents.Convert (X,Y); end Not_So_Well; Alice, Charles: Child.Object; Bob, Eve: Stepchild.Object; begin All_Well(Alice); All_Well(Bob); All_Well(Charles); All_Well(Eve); Ada.Text_IO.Put("first OK "); Not_So_Well(Alice, Charles); Ada.Text_IO.Put("second OK "); Not_So_Well(Bob, Eve); -- this raises Eception Constraint_Error -- with "... tag check failed". Ada.Text_IO.Put("will we ever get here?"); -- no, nay, never!!! end Family; ----End---- My understanding of the Liskov substitution principle, see http://en.wikipedia.org/wiki/Liskov_substitution_principle is that as Partens.Parent implicitely (by not being limited) provides certain primitve operations, such as ":=" and "=", and Stepchild.Object takes away these primitive operations, Stepchild.Object should not be in Parents.Parent'Class, i.e., Not_So_Well(Bob, Eve); and even All_Well(Bob); and All_Well(Eve); ought to be a syntax error. I think, it is a flaw that when calling Not_So_Well(X,Y) you need to know (a) of what type X and Y actually are (instead of just knowing that these are of type Parents.Parent'Class) and (b) and the implementation details of Not_So_Well (here the fact that it uses the assignment over Parents.Parent'Class). What do you guys think about this? -- Stefan Lucks (moved to Bauhaus-University Weimar, Germany) ------ I love the taste of Cryptanalysis in the morning! ------ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-23 19:47 Ada Interfaces and the Liskov Substitution Principle Stefan Lucks @ 2007-05-23 20:32 ` Ludovic Brenta 2007-05-23 22:00 ` Randy Brukardt 2007-05-24 6:57 ` Stefan Lucks 2007-05-23 20:54 ` Maciej Sobczak 2007-05-24 7:39 ` Dmitry A. Kazakov 2 siblings, 2 replies; 81+ messages in thread From: Ludovic Brenta @ 2007-05-23 20:32 UTC (permalink / raw) Stefan Lucks <lucks@th.informatik.uni-mannheim.de> writes: > package Parents is > type Parent is Interface; > > -- primitive operation > procedure Do_Something(Self: in out Parent) is abstract; > > -- class-wide operations > procedure Do_Nothing(From: in Parent'Class; > To: out Parent'Class); > procedure Convert(From: in Parent'Class; > To: out Parent'Class); > end Parents; > with Parents, Ada.Finalization; > > package Stepchild is > > type Object is > new Ada.Finalization.Limited_Controlled > and Parents.Parent > with private; > > procedure Do_Something (Self: in out Object); > > private > > ... doesn't really matter ... > > end Stepchild; > What do you guys think about this? I think the declaration of Stepchild.Object is illegal because ARM 3.9.4(12/2) states: "A type derived from a nonlimited interface shall be nonlimited." Since GNAT is currently the only compiler with Ada 2007 features, I presume it must be a bug in GNAT. What version are you using? -- Ludovic Brenta. ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-23 20:32 ` Ludovic Brenta @ 2007-05-23 22:00 ` Randy Brukardt 2007-05-24 0:56 ` Anh Vo 2007-05-24 18:27 ` Pascal Obry 2007-05-24 6:57 ` Stefan Lucks 1 sibling, 2 replies; 81+ messages in thread From: Randy Brukardt @ 2007-05-23 22:00 UTC (permalink / raw) "Ludovic Brenta" <ludovic@ludovic-brenta.org> wrote in message news:871wh7i8lq.fsf@ludovic-brenta.org... > Stefan Lucks <lucks@th.informatik.uni-mannheim.de> writes: ... > > What do you guys think about this? > > I think the declaration of Stepchild.Object is illegal because ARM > 3.9.4(12/2) states: "A type derived from a nonlimited interface shall > be nonlimited." Right. The most general type of interface is a limited interface; every interface should be declared limited if possible. (We didn't make that the default solely because it would be inconsistent with the rest of the language.) And if it is not possible to declare the interface limited (as in the example), then all types that include it must be nonlimited. At least interfaces don't have the rather limiting rule that regular tagged types do, where the limitedness can't be changed at all. Randy. ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-23 22:00 ` Randy Brukardt @ 2007-05-24 0:56 ` Anh Vo 2007-05-24 18:27 ` Pascal Obry 1 sibling, 0 replies; 81+ messages in thread From: Anh Vo @ 2007-05-24 0:56 UTC (permalink / raw) On May 23, 3:00 pm, "Randy Brukardt" <r...@rrsoftware.com> wrote: > "Ludovic Brenta" <ludo...@ludovic-brenta.org> wrote in message > > news:871wh7i8lq.fsf@ludovic-brenta.org... > > > Stefan Lucks <l...@th.informatik.uni-mannheim.de> writes: > ... > > > What do you guys think about this? > > > I think the declaration of Stepchild.Object is illegal because ARM > > 3.9.4(12/2) states: "A type derived from a nonlimited interface shall > > be nonlimited." > > Right. The most general type of interface is a limited interface; every > interface should be declared limited if possible. (We didn't make that the > default solely because it would be inconsistent with the rest of the > language.) And if it is not possible to declare the interface limited (as in > the example), then all types that include it must be nonlimited. At least > interfaces don't have the rather limiting rule that regular tagged types do, > where the limitedness can't be changed at all. Indeed, GNAT-GPL-2007 complains that "stepchild.ads:12:04: progenitor interface "Parent" of limited type must be limited". In addition, if the Object is changed Controlled type instead, there is no problem with compilation. AV ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-23 22:00 ` Randy Brukardt 2007-05-24 0:56 ` Anh Vo @ 2007-05-24 18:27 ` Pascal Obry 2007-05-24 18:39 ` Dmitry A. Kazakov 1 sibling, 1 reply; 81+ messages in thread From: Pascal Obry @ 2007-05-24 18:27 UTC (permalink / raw) To: Randy Brukardt Randy Brukardt a �crit : > Right. The most general type of interface is a limited interface; every > interface should be declared limited if possible. (We didn't make that the > default solely because it would be inconsistent with the rest of the > language.) Well a task type is limited by default, idem for a protected object types. Pascal. -- --|------------------------------------------------------ --| Pascal Obry Team-Ada Member --| 45, rue Gabriel Peri - 78114 Magny Les Hameaux FRANCE --|------------------------------------------------------ --| http://www.obry.net --| "The best way to travel is by means of imagination" --| --| gpg --keyserver wwwkeys.pgp.net --recv-key C1082595 ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 18:27 ` Pascal Obry @ 2007-05-24 18:39 ` Dmitry A. Kazakov 2007-05-24 18:51 ` Pascal Obry 2007-05-24 22:44 ` Randy Brukardt 0 siblings, 2 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-24 18:39 UTC (permalink / raw) On Thu, 24 May 2007 20:27:25 +0200, Pascal Obry wrote: > Randy Brukardt a �crit : >> Right. The most general type of interface is a limited interface; every >> interface should be declared limited if possible. (We didn't make that the >> default solely because it would be inconsistent with the rest of the >> language.) > > Well a task type is limited by default, idem for a protected object types. Randy probably meant that If it were limited then one would need to introduce "not limited" qualifier to be able to declare a non-limited interface. (In addition to awful "not null") -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 18:39 ` Dmitry A. Kazakov @ 2007-05-24 18:51 ` Pascal Obry 2007-05-24 22:44 ` Randy Brukardt 1 sibling, 0 replies; 81+ messages in thread From: Pascal Obry @ 2007-05-24 18:51 UTC (permalink / raw) To: mailbox Dmitry A. Kazakov a �crit : > On Thu, 24 May 2007 20:27:25 +0200, Pascal Obry wrote: > >> Randy Brukardt a �crit : >>> Right. The most general type of interface is a limited interface; every >>> interface should be declared limited if possible. (We didn't make that the >>> default solely because it would be inconsistent with the rest of the >>> language.) >> Well a task type is limited by default, idem for a protected object types. > > Randy probably meant that If it were limited then one would need to > introduce "not limited" qualifier to be able to declare a non-limited > interface. (In addition to awful "not null") Yep, reading the message again I think you're right. Thanks, Pascal. -- --|------------------------------------------------------ --| Pascal Obry Team-Ada Member --| 45, rue Gabriel Peri - 78114 Magny Les Hameaux FRANCE --|------------------------------------------------------ --| http://www.obry.net --| "The best way to travel is by means of imagination" --| --| gpg --keyserver wwwkeys.pgp.net --recv-key C1082595 ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 18:39 ` Dmitry A. Kazakov 2007-05-24 18:51 ` Pascal Obry @ 2007-05-24 22:44 ` Randy Brukardt 1 sibling, 0 replies; 81+ messages in thread From: Randy Brukardt @ 2007-05-24 22:44 UTC (permalink / raw) [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Type: text/plain, Size: 1202 bytes --] "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:tii7nk5e3998$.1xfer2lqmixvy.dlg@40tude.net... > On Thu, 24 May 2007 20:27:25 +0200, Pascal Obry wrote: > > > Randy Brukardt a �crit : > >> Right. The most general type of interface is a limited interface; every > >> interface should be declared limited if possible. (We didn't make that the > >> default solely because it would be inconsistent with the rest of the > >> language.) > > > > Well a task type is limited by default, idem for a protected object types. > > Randy probably meant that If it were limited then one would need to > introduce "not limited" qualifier to be able to declare a non-limited > interface. (In addition to awful "not null") Correct, that's what I meant. When Ada has a choice between non-limited and limited, it makes you write "limited" if you meant that. That's annoying in this case, but it's less annoying than having: type A is tagged record... type B is tagged limited record ... type C is tagged private; type D is tagged limited private; type E is not limited interface; type F is interface; which would be forever confusing. Randy. ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-23 20:32 ` Ludovic Brenta 2007-05-23 22:00 ` Randy Brukardt @ 2007-05-24 6:57 ` Stefan Lucks 1 sibling, 0 replies; 81+ messages in thread From: Stefan Lucks @ 2007-05-24 6:57 UTC (permalink / raw) > I think the declaration of Stepchild.Object is illegal because ARM > 3.9.4(12/2) states: "A type derived from a nonlimited interface shall > be nonlimited." Many thanks to you and to the other guys for pointing that out! I am pleased to hear this. > Since GNAT is currently the only compiler with Ada 2007 features, I > presume it must be a bug in GNAT. What version are you using? gnat GPL 2006 (I have not yet switched to 2007). -- Stefan Lucks (moved to Bauhaus-University Weimar, Germany) ------ I love the taste of Cryptanalysis in the morning! ------ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-23 19:47 Ada Interfaces and the Liskov Substitution Principle Stefan Lucks 2007-05-23 20:32 ` Ludovic Brenta @ 2007-05-23 20:54 ` Maciej Sobczak 2007-05-23 21:58 ` Randy Brukardt 2007-05-24 7:39 ` Dmitry A. Kazakov 2 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-05-23 20:54 UTC (permalink / raw) On 23 Maj, 21:47, Stefan Lucks <l...@th.informatik.uni-mannheim.de> wrote: > to me, it seems as if Ada 2005 is bluntly violating the Liskov > Substitution Prinicple. E.g., define [...] Interesting. My humble opinion: assignment of class-wide types should be prohibited in the first place. I mean - at compile time. It's just asking for troubles. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-23 20:54 ` Maciej Sobczak @ 2007-05-23 21:58 ` Randy Brukardt 2007-05-24 7:29 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Randy Brukardt @ 2007-05-23 21:58 UTC (permalink / raw) "Maciej Sobczak" <see.my.homepage@gmail.com> wrote in message news:1179953657.839272.160320@a26g2000pre.googlegroups.com... > On 23 Maj, 21:47, Stefan Lucks <l...@th.informatik.uni-mannheim.de> > wrote: > > > to me, it seems as if Ada 2005 is bluntly violating the Liskov > > Substitution Prinicple. E.g., define > > [...] > > Interesting. > > My humble opinion: assignment of class-wide types should be prohibited > in the first place. I mean - at compile time. > It's just asking for troubles. That doesn't make much sense. Yes, it is a bit weird that assignment is potentially a dispatching operation, but otherwise there is no semantic problem with it. (The OP having an illegal example, thus proving nothing.) It might be a bit hard to use usefully, but that's no reason for banning it. Randy. ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-23 21:58 ` Randy Brukardt @ 2007-05-24 7:29 ` Maciej Sobczak 2007-05-24 8:02 ` Dmitry A. Kazakov 2007-05-24 10:42 ` Georg Bauhaus 0 siblings, 2 replies; 81+ messages in thread From: Maciej Sobczak @ 2007-05-24 7:29 UTC (permalink / raw) On 23 Maj, 23:58, "Randy Brukardt" <r...@rrsoftware.com> wrote: > > My humble opinion: assignment of class-wide types should be prohibited > > in the first place. I mean - at compile time. > > It's just asking for troubles. > > That doesn't make much sense. Yes, it is a bit weird that assignment is > potentially a dispatching operation, but otherwise there is no semantic > problem with it. Can it be dispatching on both arguments? If not, how can you reasonably implement it? And even if it could, still, how can you *reasonably* implement it? Let's take a classic example with the hierarchy of geometric objects (Object, Rectlangle, Triangle, Circle, and so on) and this: procedure Do_Something(X : in Object'Class; Y : out Object'Class) is begin Y := X; -- ? end Do_Something; > (The OP having an illegal example, thus proving nothing.) The OP did not prove that Ada violates LSP, but he did point out where the problems can hide. Coming from C++, I have never seen any single example where assignment made sense with polymorphic hierarchies. Do you know any such example? Can you show it? > It might be a bit hard to use usefully, but that's no reason for banning it. C programmers can use the same logic to justify everything. ;-) -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 7:29 ` Maciej Sobczak @ 2007-05-24 8:02 ` Dmitry A. Kazakov 2007-05-24 12:58 ` Maciej Sobczak 2007-05-24 10:42 ` Georg Bauhaus 1 sibling, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-24 8:02 UTC (permalink / raw) On 24 May 2007 00:29:29 -0700, Maciej Sobczak wrote: > On 23 Maj, 23:58, "Randy Brukardt" <r...@rrsoftware.com> wrote: > >>> My humble opinion: assignment of class-wide types should be prohibited >>> in the first place. I mean - at compile time. >>> It's just asking for troubles. >> >> That doesn't make much sense. Yes, it is a bit weird that assignment is >> potentially a dispatching operation, but otherwise there is no semantic >> problem with it. > > Can it be dispatching on both arguments? It shall be. It is a pity that Ada does not have MD. > If not, how can you reasonably implement it? > > And even if it could, still, how can you *reasonably* implement it? > > Let's take a classic example with the hierarchy of geometric objects > (Object, Rectlangle, Triangle, Circle, and so on) and this: > > procedure Do_Something(X : in Object'Class; Y : out Object'Class) is > begin > Y := X; -- ? > end Do_Something; In the current version of Ada Y is constrained to its actual type. So in all cases of different types you will get Constraint_Error - no full MD. But if there were MD, then you could call Do_Something (Circle, Ellipse). And don't forget: declare X : T'Class := Read_From_File; -- T is non-limited, Adjust is called begin ... > Coming from C++, I have never seen any single example where assignment > made sense with polymorphic hierarchies. Real-life example. Consider a threaded library. Let you have an Ada-like rendezvous. You wanted to propagate an exception raised in the callee to the caller. Similarly, let an exception is propagated out of a child thread. You want to continue its propagation to the parent. > Do you know any such example? Can you show it? Marshaling objects. (All types of, like in the example above, like in view change of a small by-value object, like in making an object persistent or broadcasting it over the network) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 8:02 ` Dmitry A. Kazakov @ 2007-05-24 12:58 ` Maciej Sobczak 2007-05-24 13:42 ` Dmitry A. Kazakov ` (2 more replies) 0 siblings, 3 replies; 81+ messages in thread From: Maciej Sobczak @ 2007-05-24 12:58 UTC (permalink / raw) On 24 Maj, 10:02, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > Can it be dispatching on both arguments? > > It shall be. It is a pity that Ada does not have MD. Yes, but that would not automatically solve the problem, see below. > > Coming from C++, I have never seen any single example where assignment > > made sense with polymorphic hierarchies. > > Real-life example. Consider a threaded library. Let you have an Ada-like > rendezvous. You wanted to propagate an exception raised in the callee to > the caller. Similarly, let an exception is propagated out of a child > thread. You want to continue its propagation to the parent. > > > Do you know any such example? Can you show it? > > Marshaling objects. No, I'm not convinced. This is more like cloning. Assignment of class-wide type is strange/dangerous, beucause it might require dynamic change of the object's type. Assigning Circle to Triangle (through Object'Class) cannot reasonably work with preserving the types of each object. The Triangle would need to become Circle - and this action might change the ability of the object to respond to messages that are supposedly in its interface. Or change the invariants in the original type. declare C : Circle; T : Triangle; begin Do_Something(C, T); -- T := C; inside -- and here some code still thinks that T is a Triangle, -- which according to its declaration in the same scope -- must be true (but isn't!) end; There is no way to change the type of T in the code above and without it there is no way to execute the assignment. You can restrict the operation so that the arguments must have the same tag (and then MD is not needed anymore), but such restriction can be run-time only and cannot be expressed in the signature of Do_Something. Your marshalling example is interesting, but actually works differently: you don't have the left-hand object before assignment; you just make a copy of something and give the copy some identity. It's just a copy-construction basically, or cloning (if polimorphic). You can do this either with access variables or with initialization of class wide type (which is *not* an assignment). I was talking about real assignment - and this assumes that two objects already exist and we make one to be equal to the other. There is no way to do it right. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 12:58 ` Maciej Sobczak @ 2007-05-24 13:42 ` Dmitry A. Kazakov 2007-05-24 22:08 ` Robert A Duff 2007-05-24 22:58 ` Randy Brukardt 2 siblings, 0 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-24 13:42 UTC (permalink / raw) On 24 May 2007 05:58:27 -0700, Maciej Sobczak wrote: > On 24 Maj, 10:02, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>> Can it be dispatching on both arguments? >> >> It shall be. It is a pity that Ada does not have MD. > > Yes, but that would not automatically solve the problem, see below. > >>> Coming from C++, I have never seen any single example where assignment >>> made sense with polymorphic hierarchies. >> >> Real-life example. Consider a threaded library. Let you have an Ada-like >> rendezvous. You wanted to propagate an exception raised in the callee to >> the caller. Similarly, let an exception is propagated out of a child >> thread. You want to continue its propagation to the parent. >> >>> Do you know any such example? Can you show it? >> >> Marshaling objects. > > No, I'm not convinced. This is more like cloning. > > Assignment of class-wide type is strange/dangerous, beucause it might > require dynamic change of the object's type. No it cannot, because otherwise it weren't dispatching in the left argument. But you have explained it later on. > Assigning Circle to Triangle (through Object'Class) cannot reasonably > work with preserving the types of each object. The Triangle would need > to become Circle - and this action might change the ability of the > object to respond to messages that are supposedly in its interface. Or > change the invariants in the original type. > > declare > C : Circle; > T : Triangle; > begin > Do_Something(C, T); -- T := C; inside > -- and here some code still thinks that T is a Triangle, > -- which according to its declaration in the same scope > -- must be true (but isn't!) > end; > > There is no way to change the type of T in the code above and without > it there is no way to execute the assignment. No, you presume some meaning in the name ":=", but there is no one. It just is an operation to call as any other. Whatever Circle := Triangle might mean, is up to the designer of this types hierarchy. > You can restrict the operation so that the arguments must have the > same tag (and then MD is not needed anymore), but such restriction can > be run-time only and cannot be expressed in the signature of > Do_Something. > > Your marshalling example is interesting, but actually works > differently: you don't have the left-hand object before assignment; > you just make a copy of something and give the copy some identity. > It's just a copy-construction basically, or cloning (if polimorphic). > You can do this either with access variables or with initialization of > class wide type (which is *not* an assignment). > > I was talking about real assignment - and this assumes that two > objects already exist and we make one to be equal to the other. There > is no way to do it right. OK, for this the use case is [semi-]"equivalent" types hierarchies. They often appear when you have concurrent representations of the same thing. Typical example (poorly designed in Ada) is: String, UTF8_String, Wide_String, Wide_Wide_String, Unbounded_String, Unbounded_UTF8_String ... Surely you wanted to cross-assign each other of them. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 12:58 ` Maciej Sobczak 2007-05-24 13:42 ` Dmitry A. Kazakov @ 2007-05-24 22:08 ` Robert A Duff 2007-07-01 1:00 ` David Thompson 2007-05-24 22:58 ` Randy Brukardt 2 siblings, 1 reply; 81+ messages in thread From: Robert A Duff @ 2007-05-24 22:08 UTC (permalink / raw) Maciej Sobczak <see.my.homepage@gmail.com> writes: > You can do this either with access variables or with initialization of > class wide type (which is *not* an assignment). Well, actually, in Ada terminology, an initialization is an assignment. There are two kinds of assignment: initialization, and assignment_statement. I don't particularly like that terminology, but it's what the RM says. > I was talking about real assignment ... You're talking about an assignment_statement. Best to stick to the terms "initialization" and "assignment_statement" to avoid confusion. And avoid the term "assignment" entirely. ;-) I'm just nitpicking your terminology -- I agree with your point that there's a big difference between initialization (which creates a new object out of whole cloth) and assign_stm (which overwrites the left-hand side). - Bob ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 22:08 ` Robert A Duff @ 2007-07-01 1:00 ` David Thompson 0 siblings, 0 replies; 81+ messages in thread From: David Thompson @ 2007-07-01 1:00 UTC (permalink / raw) On Thu, 24 May 2007 18:08:05 -0400, Robert A Duff <bobduff@shell01.TheWorld.com> wrote: > Well, actually, in Ada terminology, an initialization is an assignment. > There are two kinds of assignment: initialization, and > assignment_statement. I don't particularly like that terminology, but > it's what the RM says. <snip> > You're talking about an assignment_statement. Best to stick to the > terms "initialization" and "assignment_statement" to avoid confusion. > And avoid the term "assignment" entirely. ;-) > Algol 68 called it 'assignation'. Sadly we didn't get rendezvous and assignation in one language; that could have been popular indeed. > I'm just nitpicking your terminology -- I agree with your point <snip> - formerly david.thompson1 || achar(64) || worldnet.att.net ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 12:58 ` Maciej Sobczak 2007-05-24 13:42 ` Dmitry A. Kazakov 2007-05-24 22:08 ` Robert A Duff @ 2007-05-24 22:58 ` Randy Brukardt 2007-05-25 7:52 ` Maciej Sobczak 2 siblings, 1 reply; 81+ messages in thread From: Randy Brukardt @ 2007-05-24 22:58 UTC (permalink / raw) "Maciej Sobczak" <see.my.homepage@gmail.com> wrote in message news:1180011507.159515.46920@o5g2000hsb.googlegroups.com... ... > I was talking about real assignment - and this assumes that two > objects already exist and we make one to be equal to the other. There > is no way to do it right. (Using the *correct* terminology as Bob noted) Given the current Ada rules, what classwide assignment is good for is *re*construction: procedure Do_Something(Y : out Object'Class) is begin Y := Constructor_Func (<some details of Y>); end Do_Something; that is, the replacement of an object with different one of the same type. The Constructor_Func is dispatching on the real type of the LHS object, so the new object will be constructed with the proper attributes of that type. Admittedly, this is not a common need, although I think I used it once in the Claw builder to change properties on the fly that Claw doesn't support changing on the fly. (Ultimately, though, we changed Claw to be more flexible.) Randy. ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 22:58 ` Randy Brukardt @ 2007-05-25 7:52 ` Maciej Sobczak 2007-05-25 8:21 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-05-25 7:52 UTC (permalink / raw) On 25 Maj, 00:58, "Randy Brukardt" <r...@rrsoftware.com> wrote: > > I was talking about real assignment - and this assumes that two > > objects already exist and we make one to be equal to the other. There > > is no way to do it right. > > (Using the *correct* terminology as Bob noted) Yes, thank you both for explaining it - I was using the C++ terminology. > Given the current Ada rules, what classwide assignment is good for is > *re*construction: > > procedure Do_Something(Y : out Object'Class) is > begin > Y := Constructor_Func (<some details of Y>); > end Do_Something; > > that is, the replacement of an object with different one of the same type. > The Constructor_Func is dispatching on the real type of the LHS object Interesting. In most of the cases, the factory function creates the object of whatever type *it* decides (based on its parameters or some other input values) and initializes the class-wide type accordingly. The one above means "remake me, please", but it's applicability must be greatly limited - you neatly write <some details of Y>, but it should be really <some details of whatever-Y-happens-to-be which we don't know here>. That complicates this scheme a bit. There is a very simple rule of thumb for checking whether any given OO pattern makes sense - try to stick it to the classical geometry example. If it falls off, beware. :-) -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-25 7:52 ` Maciej Sobczak @ 2007-05-25 8:21 ` Dmitry A. Kazakov 2007-05-25 20:27 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-25 8:21 UTC (permalink / raw) On 25 May 2007 00:52:21 -0700, Maciej Sobczak wrote: > On 25 Maj, 00:58, "Randy Brukardt" <r...@rrsoftware.com> wrote: > >> Given the current Ada rules, what classwide assignment is good for is >> *re*construction: >> >> procedure Do_Something(Y : out Object'Class) is >> begin >> Y := Constructor_Func (<some details of Y>); >> end Do_Something; >> >> that is, the replacement of an object with different one of the same type. >> The Constructor_Func is dispatching on the real type of the LHS object > > Interesting. In most of the cases, the factory function creates the > object of whatever type *it* decides (based on its parameters or some > other input values) and initializes the class-wide type accordingly. Not necessarily. In one case, I generate nodes of a graph. The nodes can be of different types regarding their persistence. For example, there are nodes resident in the database and nodes resident in the memory etc. Now, when creating a new node a factory moves along two axes: the standard root/branch/leaf hierarchy and persistence stuff. For the latter the factory receives an already existing node, to create "a new node like this." This is not copying. Ideally it should double dispatching along both axes. I have implemented it as: New_Node := Create (Get_Factory (Some_Existing_Node), ...); > There is a very simple rule of thumb for checking whether any given OO > pattern makes sense - try to stick it to the classical geometry > example. > If it falls off, beware. :-) You certainly mean the circle-ellipse LSP controversy... (:-)) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-25 8:21 ` Dmitry A. Kazakov @ 2007-05-25 20:27 ` Maciej Sobczak 2007-05-26 7:48 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-05-25 20:27 UTC (permalink / raw) On 25 Maj, 10:21, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > Interesting. In most of the cases, the factory function creates the > > object of whatever type *it* decides (based on its parameters or some > > other input values) and initializes the class-wide type accordingly. > > Not necessarily. In one case, I generate nodes of a graph. The nodes can be > of different types regarding their persistence. For example, there are > nodes resident in the database and nodes resident in the memory etc. Now, > when creating a new node a factory moves along two axes: the standard > root/branch/leaf hierarchy and persistence stuff. For the latter the > factory receives an already existing node, to create "a new node like > this." This is not copying. Ideally it should double dispatching along both > axes. I have implemented it as: > > New_Node := Create (Get_Factory (Some_Existing_Node), ...); Above, it is the factory function that is "parameterized" on the left- hand type - this is still much different from assignment_statement (wow!) between two class-wide types. I'm still for banning it. > > There is a very simple rule of thumb for checking whether any given OO > > pattern makes sense - try to stick it to the classical geometry > > example. > > If it falls off, beware. :-) > > You certainly mean the circle-ellipse LSP controversy... (:-)) Ontological relationships have nothing to do with behavioral substitutability. There is no controversy there, only confusion. ;-) -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-25 20:27 ` Maciej Sobczak @ 2007-05-26 7:48 ` Dmitry A. Kazakov 2007-05-27 8:30 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-26 7:48 UTC (permalink / raw) On 25 May 2007 13:27:47 -0700, Maciej Sobczak wrote: > On 25 Maj, 10:21, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>> Interesting. In most of the cases, the factory function creates the >>> object of whatever type *it* decides (based on its parameters or some >>> other input values) and initializes the class-wide type accordingly. >> >> Not necessarily. In one case, I generate nodes of a graph. The nodes can be >> of different types regarding their persistence. For example, there are >> nodes resident in the database and nodes resident in the memory etc. Now, >> when creating a new node a factory moves along two axes: the standard >> root/branch/leaf hierarchy and persistence stuff. For the latter the >> factory receives an already existing node, to create "a new node like >> this." This is not copying. Ideally it should double dispatching along both >> axes. I have implemented it as: >> >> New_Node := Create (Get_Factory (Some_Existing_Node), ...); > > Above, it is the factory function that is "parameterized" on the left- > hand type and the right-hand one, which was the point. But it would be enough to give just two examples, one for LHS, another for RHS: X : T; begin X := Read_From_File; --- LHS return X; -- RHS in the caller > - this is still much different from assignment_statement > (wow!) between two class-wide types. How is it different? Note that there is little room for how to define assignment. Within a type hierarchy there are only four variants: T'Class x T'Class -- non-dispatching T x T'Class -- dispatches on the target (C++) T'Class x T' -- dispatches on the source T x T -- Fully dispatching (Ada*) In all cases X:=Y will be legal on two class-wide objects. > I'm still for banning it. For this you have to make assignment contravariant (non-primitive operation) in one of its arguments. That would be a total mess, because it would require overloading assignment for each derived type. For the same reason all signatures with a class-wide parameter are bad, because they lead to ambiguities in trivial cases: type S is new T with ...; X, Y : S; X := Y; -- S'Class x S'Class vs T'Class x T'Class? You will need some sort of dominance rules to resolve that. P.S. Probably you have in mind a "stratified" assignment which statically checks that LHS and RHS are of the same type. Ada has it as well. This is achieved by types cloning upon derivation: type My_Float is new Float range ...; -- equivalent to -- subtype Anonymous is My_Float range ...; -- Same hierarchy -- type My_Float is new Anonymous; -- Clone it Unfortunately, which IMO was a big mistake, this mechanism war prohibited for tagged types. ----------- * Ada's assignment is doubly dispatching. The dispatching table is a square, the diagonal of consists of thunks: Finalize (LHS); bit-copy (LHS, RHS); Adjust (LHS); Non-diagonal elements are: raise Constraint_Error; -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-26 7:48 ` Dmitry A. Kazakov @ 2007-05-27 8:30 ` Maciej Sobczak 2007-05-27 10:04 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-05-27 8:30 UTC (permalink / raw) On 26 Maj, 09:48, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > I'm still for banning it. > > For this you have to make assignment contravariant (non-primitive > operation) in one of its arguments. That would be a total mess, because it > would require overloading assignment for each derived type. I don't get it. I said I would ban it. There is no "for this". :-) > For the same reason all signatures with a class-wide parameter are bad, > because they lead to ambiguities in trivial cases: > > type S is new T with ...; > X, Y : S; > > X := Y; -- S'Class x S'Class vs T'Class x T'Class? > > You will need some sort of dominance rules to resolve that. Why? Both X and Y are of the same type above and it is not a class wide type in the sense that no dispatching is needed here. In other words, the above assignment can be bound statically. > P.S. Probably you have in mind a "stratified" assignment which statically > checks that LHS and RHS are of the same type. Not necessarily. I would allow different types if the assignment_statement is overloaded for them for those who like to introduce sort of implicit conversions. What I worry about is the situation where *both* dynamic types are not known statically. This is a mess. > ----------- > * Ada's assignment is doubly dispatching. The dispatching table is a > square, the diagonal of consists of thunks: > > Finalize (LHS); > bit-copy (LHS, RHS); > Adjust (LHS); > > Non-diagonal elements are: > > raise Constraint_Error; Which is a run-time thingy. It's good if it's there, but I want stricter guarantees. You can get them by just banning assignment_statement between class-wide types. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-27 8:30 ` Maciej Sobczak @ 2007-05-27 10:04 ` Dmitry A. Kazakov 2007-05-29 8:03 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-27 10:04 UTC (permalink / raw) On 27 May 2007 01:30:36 -0700, Maciej Sobczak wrote: > On 26 Maj, 09:48, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>> I'm still for banning it. >> >> For this you have to make assignment contravariant (non-primitive >> operation) in one of its arguments. That would be a total mess, because it >> would require overloading assignment for each derived type. > > I don't get it. I said I would ban it. There is no "for this". :-) To ban something in the language you have to translate the ban into language terms. That could be either by syntax or by semantic rules. Because syntactically banned assignments are indistinguishable from legal ones, the only mechanics you could use for this is matching types. The types in question are from the same hierarchy (covariant). This lets mixing such types. So it *must* be contravariant to be banned = the operation (":=") will not be inherited in the argument of interest upon inheritance. >> For the same reason all signatures with a class-wide parameter are bad, >> because they lead to ambiguities in trivial cases: >> >> type S is new T with ...; >> X, Y : S; >> >> X := Y; -- S'Class x S'Class vs T'Class x T'Class? >> >> You will need some sort of dominance rules to resolve that. > > Why? Per definition/meaning of class-wide operations: they apply to *all* types from the class. > Both X and Y are of the same type above and it is not a class > wide type in the sense that no dispatching is needed here. Dispatching is about primitive operations acting on class-wide objects. Class-wide operations acting on specific objects is a way different story. >> P.S. Probably you have in mind a "stratified" assignment which statically >> checks that LHS and RHS are of the same type. > > Not necessarily. I would allow different types if the > assignment_statement is overloaded for them for those who like to > introduce sort of implicit conversions. > What I worry about is the situation where *both* dynamic types are not > known statically. This is a mess. You cannot express it in terms of the types system. So you have to roll up another formal types framework. And I don't see why it is a mess. What is wrong in assignment of UTF8_String to Wide_String, both from String'Class? The very definition of a class says that types there share class-wide and primitive operations. If that is not the case: ":=" is not shared, then either it is not a class (= don't derive! if need not) or there is no ":=" (= make it limited! if unsure). Looks much like a design problem. >> ----------- >> * Ada's assignment is doubly dispatching. The dispatching table is a >> square, the diagonal of consists of thunks: >> >> Finalize (LHS); >> bit-copy (LHS, RHS); >> Adjust (LHS); >> >> Non-diagonal elements are: >> >> raise Constraint_Error; > > Which is a run-time thingy. It's good if it's there, but I want > stricter guarantees. You can get them by just banning > assignment_statement between class-wide types. No, because that would be inconsistent. A doubly dispatching operation *has* a square table. There is nothing to do about it. You have to make it something else instead, I have listed possible variants earlier. These alternatives are mess. Hardwired rules imposed on assignments were an even bigger mess. It is bad enough that Ada has assignment defined as an statement, rather than a procedure call. There is nothing special in assignments. So your mechanism, if any, should be universally applied to all operations with multiple arguments from the same types hierarchy. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-27 10:04 ` Dmitry A. Kazakov @ 2007-05-29 8:03 ` Maciej Sobczak 2007-05-29 13:18 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-05-29 8:03 UTC (permalink / raw) On 27 Maj, 12:04, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > To ban something in the language you have to translate the ban into > language terms. That could be either by syntax or by semantic rules. > Because syntactically banned assignments are indistinguishable from legal > ones, the only mechanics you could use for this is matching types. The > types in question are from the same hierarchy (covariant). This lets mixing > such types. So it *must* be contravariant to be banned = the operation > (":=") will not be inherited in the argument of interest upon inheritance. OK, now I see your point. I have nothing against following this path - assignment statement *is* special anyway. What could go wrong if we say that assignment is not inherited? > And I don't see why it is a mess. What is wrong in assignment of > UTF8_String to Wide_String, both from String'Class? Probably design issue. Character encoding can/should be a strategy inside a String, you don't need to subclass the whole thing just for it. There are many aspects on which you could parameterize String and with some of them being orthogonal you could quickly end up with milion leaf classes. > The very definition of a class says that types there share class-wide and > primitive operations. If that is not the case: ":=" is not shared, then > either it is not a class (= don't derive! if need not) or there is no ":=" > (= make it limited! if unsure). Looks much like a design problem. Yes. That's why I say that I have never seen any reasonable hierarchy that supports assignments. > There is nothing special in > assignments. Yes, there is - they are generated. This is deceptive, because it makes one think that it should all work by magic. Another reason why they are special is that they are intuitively associated with some particular effects and these effects cannot be reasonably provided in an automated way. That's why I think that assignments of class-wide types should be forbidden. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-29 8:03 ` Maciej Sobczak @ 2007-05-29 13:18 ` Dmitry A. Kazakov 2007-05-29 13:32 ` Dmitry A. Kazakov 2007-05-29 15:34 ` Maciej Sobczak 0 siblings, 2 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-29 13:18 UTC (permalink / raw) On 29 May 2007 01:03:23 -0700, Maciej Sobczak wrote: > On 27 Maj, 12:04, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >> To ban something in the language you have to translate the ban into >> language terms. That could be either by syntax or by semantic rules. >> Because syntactically banned assignments are indistinguishable from legal >> ones, the only mechanics you could use for this is matching types. The >> types in question are from the same hierarchy (covariant). This lets mixing >> such types. So it *must* be contravariant to be banned = the operation >> (":=") will not be inherited in the argument of interest upon inheritance. > > OK, now I see your point. I have nothing against following this path - > assignment statement *is* special anyway. What could go wrong if we > say that assignment is not inherited? Then you will have a messy bunch of overloaded ones. There are two variants of: 1. Use of T'Class, I considered this in a previous post, it is ambiguous. 2. Non-primitive operation (ugly hack). This will not work on class-wide arguments: X : T'Class := Y; -- Illegal, ":=" isn't defined on LHS T'Class >> And I don't see why it is a mess. What is wrong in assignment of >> UTF8_String to Wide_String, both from String'Class? > > Probably design issue. Character encoding can/should be a strategy > inside a String, you don't need to subclass the whole thing just for > it. There are many aspects on which you could parameterize String and > with some of them being orthogonal you could quickly end up with > milion leaf classes. So? This is exactly the reason why I want to keep them all in one class. Encoding is a constraint put on the object. It is mapped to a type to be able to implement string operations independently, for each encoding through dispatching rather than by ugly nested case-statements. And what about fixed vs. bounded vs. unbounded axis? Why a fixed string shouldn't be assigned to an unbounded one and reverse? How are you going to design such stuff without polymorphism? By cut'n'paste, as RM does? At some point they too had got tired: "... For each of the packages Strings.Fixed, Strings.Bounded, Strings.Unbounded, and Strings.Maps.Constants the corresponding wide string package has the same contents except that � Wide_Space replaces Space � Wide_Character replaces Character � Wide_String replaces String ..." (:-)) >> The very definition of a class says that types there share class-wide and >> primitive operations. If that is not the case: ":=" is not shared, then >> either it is not a class (= don't derive! if need not) or there is no ":=" >> (= make it limited! if unsure). Looks much like a design problem. > > Yes. That's why I say that I have never seen any reasonable hierarchy > that supports assignments. Strings above. >> There is nothing special in >> assignments. > > Yes, there is - they are generated. This is deceptive, because it > makes one think that it should all work by magic. It means that the language is unable to express assignments in its own terms. I don't want such language. In fact, the rules you propose for assignments can be expressed in Ada terms: type X is private; type Y is new Y; -- Done Don't use classes if you don't want to assign them. > Another reason why they are special is that they are intuitively > associated with some particular effects and these effects cannot be > reasonably provided in an automated way. That's why I think that > assignments of class-wide types should be forbidden. No, it is much simpler to provide multi-methods and make it consistent. The only problem Ada has with assignment is that it is a fake MD. If it were a true MD, then upon inheritance (per extension) from a tagged private T, the compiler would complain: "hey, I don't know how to assign T to S!" This would force you to override S := T in a reasonable way (existing for String) or else to rethink your allegedly wrong design. Presently Ada silently generates raise Constraint_Error, which is not good. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-29 13:18 ` Dmitry A. Kazakov @ 2007-05-29 13:32 ` Dmitry A. Kazakov 2007-05-29 15:34 ` Maciej Sobczak 1 sibling, 0 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-29 13:32 UTC (permalink / raw) On Tue, 29 May 2007 15:18:58 +0200, Dmitry A. Kazakov wrote: > type X is private; > type Y is new Y; -- Done type Y is new X; -- Done -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-29 13:18 ` Dmitry A. Kazakov 2007-05-29 13:32 ` Dmitry A. Kazakov @ 2007-05-29 15:34 ` Maciej Sobczak 2007-05-29 17:07 ` Dmitry A. Kazakov 1 sibling, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-05-29 15:34 UTC (permalink / raw) On 29 Maj, 15:18, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > OK, now I see your point. I have nothing against following this path - > > assignment statement *is* special anyway. What could go wrong if we > > say that assignment is not inherited? > > Then you will have a messy bunch of overloaded ones. If I need them and can reasonably implement them, yes. The design complexity of this will be somewhere there anyway. > There are two variants > of: > > 1. Use of T'Class, I considered this in a previous post, it is ambiguous. I would ban it. :-) > 2. Non-primitive operation (ugly hack). This will not work on class-wide > arguments: > > X : T'Class := Y; -- Illegal, ":=" isn't defined on LHS T'Class This is not assignment_statement. This is initialization and I have nothing against it. Actually, it is no different from initialization of subprogram parameters and as such is not only harmless, it is essential. We are talking about real: X := Y; :-) > >> And I don't see why it is a mess. What is wrong in assignment of > >> UTF8_String to Wide_String, both from String'Class? > > > Probably design issue. [...] > So? This is exactly the reason why I want to keep them all in one class. That's what I'm talking about (but then, we might be using different terminology). I say that you need one type String, that possibly uses strategy internally to delegate details like encoding. You don't need encoding to "leak out" at the level of types that the final user operates on. > Encoding is a constraint put on the object. It is mapped to a type to be > able to implement string operations independently, for each encoding > through dispatching rather than by ugly nested case-statements. You can (and should) have dispatching internally in the implementation of operations of String. I'm not proposing any case statements here! > And what about fixed vs. bounded vs. unbounded axis? This is also interesting, but in a different way. :-) Treating everything in pure OO way, these might be again internal strategies of a single String type. Think of stream buffers in streams (C++, Java). On the other hand, purity is not always beneficial and from the performance point of view fixed string provides the opportunity to get rid of dynamic allocation. But to benefit from this opportunity, you'd better not mess with OO but distinguish them using compile-time polymorphism. In C++ we have policy-based class design for it. > Why a fixed string > shouldn't be assigned to an unbounded one and reverse? Of course it should! What about template methods? ;-) (hint: constructors and assignments in STL containers are templated exactly to allow freedom in this aspect) > How are you going to > design such stuff without polymorphism? Compile-time polymorphism works just fine and if you really need run- time parameterization, you can get it with internal strategies (again, think streambuf in streams in C++). > By cut'n'paste, as RM does? At some > point they too had got tired: [...] Yes, I have noticed. :-) Looks like you guys need *real* templates. ;-) > > Another reason why they are special is that they are intuitively > > associated with some particular effects and these effects cannot be > > reasonably provided in an automated way. That's why I think that > > assignments of class-wide types should be forbidden. > > No, it is much simpler to provide multi-methods and make it consistent. But then the compiler would need to either force you to implement the whole square of assignment operations, or use run-time checks to discover whether the assignment within a given pair of leaf types is provided. The former is unrealistic with evolving or open-ended hierarchies, the latter smells more like Python than Ada. > Presently Ada > silently generates raise Constraint_Error, which is not good. Agreed. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-29 15:34 ` Maciej Sobczak @ 2007-05-29 17:07 ` Dmitry A. Kazakov 2007-05-30 7:40 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-29 17:07 UTC (permalink / raw) On 29 May 2007 08:34:18 -0700, Maciej Sobczak wrote: > On 29 Maj, 15:18, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>> OK, now I see your point. I have nothing against following this path - >>> assignment statement *is* special anyway. What could go wrong if we >>> say that assignment is not inherited? >> >> Then you will have a messy bunch of overloaded ones. > > If I need them and can reasonably implement them, yes. > The design complexity of this will be somewhere there anyway. And for the reason of complexity you drop any compiler support it might provide you? (:-)) >> There are two variants >> of: >> >> 1. Use of T'Class, I considered this in a previous post, it is ambiguous. > > I would ban it. :-) Sure >> 2. Non-primitive operation (ugly hack). This will not work on class-wide >> arguments: >> >> X : T'Class := Y; -- Illegal, ":=" isn't defined on LHS T'Class > > This is not assignment_statement. This is initialization and I have > nothing against it. Actually, it is no different from initialization > of subprogram parameters and as such is not only harmless, it is > essential. > > We are talking about real: > > X := Y; Huh, how are going to design a non-referential container of T'Class? You need: procedure Insert (..., Element : T'Class) is begin ... Slot := new T'Class'(Element); -- Slot := Element; -- in a better Ada ... >>>> And I don't see why it is a mess. What is wrong in assignment of >>>> UTF8_String to Wide_String, both from String'Class? >> >>> Probably design issue. > [...] >> So? This is exactly the reason why I want to keep them all in one class. > > That's what I'm talking about (but then, we might be using different > terminology). > I say that you need one type String, that possibly uses strategy > internally to delegate details like encoding. You don't need encoding > to "leak out" at the level of types that the final user operates on. That would be indeed a mess. How would you pass an UTF-8 string to GTK+ which knows nothing about your fancy patterns? Why don't you use the advantages of the types system? And anyway, let's forget about public interfaces, if privately it is still one class, then where is a difference? The only real alternative to classes is a time machine, back to 70s, back to case-statements. (:-)) >> Encoding is a constraint put on the object. It is mapped to a type to be >> able to implement string operations independently, for each encoding >> through dispatching rather than by ugly nested case-statements. > > You can (and should) have dispatching internally in the implementation > of operations of String. I'm not proposing any case statements here! Here you are. What is the difference between internally and externally dispatching assignments? >> And what about fixed vs. bounded vs. unbounded axis? > > This is also interesting, but in a different way. :-) Why? It is all same: a set of types considered equivalent, hence allowing cross assignments... >> Why a fixed string >> shouldn't be assigned to an unbounded one and reverse? > > Of course it should! q.e.d. > What about template methods? ;-) Static polymorphism is exactly what I am trying to get rid of... (:-)) >> How are you going to >> design such stuff without polymorphism? > > Compile-time polymorphism works just fine and if you really need run- > time parameterization, you can get it with internal strategies (again, > think streambuf in streams in C++). Static polymorphism does not allow mixing types. Further you cannot design a library for formatting strings which would not be generic itself. Generics is a dead end. >> By cut'n'paste, as RM does? At some >> point they too had got tired: > [...] > > Yes, I have noticed. :-) > Looks like you guys need *real* templates. ;-) Surely they have them ... for formatting RM texts. See the difference? (:-)) >>> Another reason why they are special is that they are intuitively >>> associated with some particular effects and these effects cannot be >>> reasonably provided in an automated way. That's why I think that >>> assignments of class-wide types should be forbidden. >> >> No, it is much simpler to provide multi-methods and make it consistent. > > But then the compiler would need to either force you to implement the > whole square of assignment operations, or use run-time checks to > discover whether the assignment within a given pair of leaf types is > provided. > The former is unrealistic with evolving or open-ended hierarchies, the > latter smells more like Python than Ada. The latter is what Ada does now, and I agree that this is not Ada (TM). The former is quite possible and IMO is the only right way to go. Note that the language should also allow declaring symmetries of the methods to reduce the number of independent variants. For example, by declaring an operation commutative. IMO, multi-methods is not a trick. What I really don't know how to do is true multiple dispatch (on different types hierarchies). The requirement is same: dispatch never fails to Constraint_Error. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-29 17:07 ` Dmitry A. Kazakov @ 2007-05-30 7:40 ` Maciej Sobczak 2007-05-30 8:43 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-05-30 7:40 UTC (permalink / raw) On 29 Maj, 19:07, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > We are talking about real: > > > X := Y; > > Huh, how are going to design a non-referential container of T'Class? I don't. :-) Polymorphism and references come hand in hand if you need the ability to reassign. Copy-initialization is the only place where you can safely get away with "values" of T'Class. I think that the mess has its source in the push to have T'Class behaving like normal value. > > I say that you need one type String, that possibly uses strategy > > internally to delegate details like encoding. You don't need encoding > > to "leak out" at the level of types that the final user operates on. > > That would be indeed a mess. How would you pass an UTF-8 string to GTK+ > which knows nothing about your fancy patterns? Then it should know. Otherwise there is no way it can interpret correctly what I pass as parameters, unless you want to have "implicit" conversions for parameters. > Why don't you use the > advantages of the types system? I do use it, I just don't elevate implementation details to the level of type that is handled directly by the user. > > You can (and should) have dispatching internally in the implementation > > of operations of String. I'm not proposing any case statements here! > > Here you are. What is the difference between internally and externally > dispatching assignments? Assignment is an operation that is meaningful syntactically - that's why it is so tempting. Internally you can have anything else, including regular subprogram calls that will do necessary conversions. > >> Why a fixed string > >> shouldn't be assigned to an unbounded one and reverse? > > > Of course it should! > > q.e.d. > > > What about template methods? ;-) > > Static polymorphism is exactly what I am trying to get rid of... (:-)) Here you are. :-) Then we will never get into any agreement. > Static polymorphism does not allow mixing types. ? > Further you cannot design > a library for formatting strings which would not be generic itself. 1. So? 2. Yes, I can. Just use arbitrary string type for formatting and then convert to the destination type. > Generics is a dead end. Hm... > > Looks like you guys need *real* templates. ;-) > > Surely they have them ... for formatting RM texts. See the difference? > (:-)) :-) > > But then the compiler would need to either force you to implement the > > whole square of assignment operations, or use run-time checks to > > discover whether the assignment within a given pair of leaf types is > > provided. > > The former is unrealistic with evolving or open-ended hierarchies, the > > latter smells more like Python than Ada. > > The latter is what Ada does now, and I agree that this is not Ada (TM). Agreed. > The former is quite possible and IMO is the only right way to go. Note that > the language should also allow declaring symmetries of the methods to > reduce the number of independent variants. Yes, but that reduces the complexity by a constant only, the problem is still fundamentally squared. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-30 7:40 ` Maciej Sobczak @ 2007-05-30 8:43 ` Dmitry A. Kazakov 2007-05-30 12:54 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-30 8:43 UTC (permalink / raw) On 30 May 2007 00:40:14 -0700, Maciej Sobczak wrote: > On 29 Maj, 19:07, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>> We are talking about real: >> >>> X := Y; >> >> Huh, how are going to design a non-referential container of T'Class? > > I don't. :-) > Polymorphism and references come hand in hand if you need the ability > to reassign. Copy-initialization is the only place where you can > safely get away with "values" of T'Class. Are you going to sell me pointers, right here, in c.l.a? (:-)) Referential semantics is an implementation detail. You propose to expose it to defend a fiction. But it would be in vain, because assigning class-wide references in this context is semantically equivalent to assigning the targets. > I think that the mess has its source in the push to have T'Class > behaving like normal value. This is all meta-programming is about: dealing with polymorphic values = programming in terms of sets of types. >>> I say that you need one type String, that possibly uses strategy >>> internally to delegate details like encoding. You don't need encoding >>> to "leak out" at the level of types that the final user operates on. >> >> That would be indeed a mess. How would you pass an UTF-8 string to GTK+ >> which knows nothing about your fancy patterns? > > Then it should know. It cannot, it is ANSI C. > Otherwise there is no way it can interpret > correctly what I pass as parameters, unless you want to have > "implicit" conversions for parameters. To interpret, to convert etc, all this requires a distinct type. Which is why different representations have to be mapped to different types. >> Why don't you use the >> advantages of the types system? > > I do use it, I just don't elevate implementation details to the level > of type that is handled directly by the user. I don't see any handling required. But let it be, then how is it different from your void * approach? When you create a void * you have to somehow specify the hidden parameter of the case-statement: void * X = Create_UTF8 ("foo"); With types you just specify the type of the object instead: X : UTF8_String := "foo"; >>> You can (and should) have dispatching internally in the implementation >>> of operations of String. I'm not proposing any case statements here! >> >> Here you are. What is the difference between internally and externally >> dispatching assignments? > > Assignment is an operation that is meaningful syntactically - that's > why it is so tempting. Internally you can have anything else, > including regular subprogram calls that will do necessary conversions. But not assignments? The question is how do I do dispatching assignment? Your point was that I shall not do it publicly. But, may I dispatch privately? If yes then how? And where is any difference? If not, then the only way left is a case-statement. >> Static polymorphism does not allow mixing types. > > ? Instances from a statically polymorphic class of types are unrelated types. You cannot have any polymorphic object from that class, only specific objects. For the same reason you cannot have any class-wide operation from that class. >> Further you cannot design >> a library for formatting strings which would not be generic itself. > > 1. So? Write an editor for such strings, store a string, send it over the network, do anything after uninstalling the compiler ... > 2. Yes, I can. Just use arbitrary string type for formatting and then > convert to the destination type. Why should I bother to have UTF-8 or ASCII strings if anything is Wide_..._Wide_Unbounded anyway? What are you going to do if the endianess of Wide x n Character does not fit your machine? >> The former is quite possible and IMO is the only right way to go. Note that >> the language should also allow declaring symmetries of the methods to >> reduce the number of independent variants. > > Yes, but that reduces the complexity by a constant only, the problem > is still fundamentally squared. It is how it is. No matter how you would handle them, you have to all of them. Whatever pattern you use, you will have to deal with this number of cases. Do you seriously propose to define some of the cases improperly, just because there are too many of them? (:-)) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-30 8:43 ` Dmitry A. Kazakov @ 2007-05-30 12:54 ` Maciej Sobczak 2007-05-30 13:56 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-05-30 12:54 UTC (permalink / raw) On 30 Maj, 10:43, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > Polymorphism and references come hand in hand if you need the ability > > to reassign. Copy-initialization is the only place where you can > > safely get away with "values" of T'Class. > > Are you going to sell me pointers, right here, in c.l.a? (:-)) Don't you call them access variables? :-) > Referential semantics is an implementation detail. No, it is not. It gives you explicitly the possibility to "reseat" the referer as well as sharing semantics. These are not details. Even with strings, you might want to share the strategy for character encoding. > But it would be in vain, because assigning class-wide > references in this context is semantically equivalent to assigning the > targets. Which I say should be banned, because the targets are not statically known. > >> That would be indeed a mess. How would you pass an UTF-8 string to GTK+ > >> which knows nothing about your fancy patterns? > > > Then it should know. > > It cannot, it is ANSI C. Then you need to call it somehow - anyway. How is the proliferation of string types supposed to help here? > I don't see any handling required. But let it be, then how is it different > from your void * approach? When did I say void*? It is spelled "interface". > When you create a void * you have to somehow > specify the hidden parameter of the case-statement: > > void * X = Create_UTF8 ("foo"); > > With types you just specify the type of the object instead: > > X : UTF8_String := "foo"; Which - again - is not an assignment. Just a reminder - we are talking about: X := Y; > The question is how do I do dispatching assignment? Just don't. :-) > Your point was that I shall not do it publicly. But, may I dispatch > privately? Privately you can dispatch to your internal strategies to get character conversions, buffers, etc. > >> Static polymorphism does not allow mixing types. > > > ? > > Instances from a statically polymorphic class of types are unrelated types. > You cannot have any polymorphic object from that class, only specific > objects. For the same reason you cannot have any class-wide operation from > that class. So? > >> Further you cannot design > >> a library for formatting strings which would not be generic itself. > > > 1. So? > > Write an editor for such strings, store a string, send it over the network, > do anything after uninstalling the compiler ... Still don't see the problem. > > 2. Yes, I can. Just use arbitrary string type for formatting and then > > convert to the destination type. > > Why should I bother to have UTF-8 or ASCII strings if anything is > Wide_..._Wide_Unbounded anyway? The Wide_..._Wide_Unbounded can be useful as an intermediary format for conversion between other types, which themselves might still be very useful providing their own characteristics. > What are you going to do if the endianess > of Wide x n Character does not fit your machine? And why should I bother with endianness here? > Whatever pattern you use, you will have to deal with this number of > cases. Or I just delegate to locale and conversion library that is part of my operating system. On my system I can have LOTS of different locales: $ locale -a | wc -l 502 Do you really expect me to have 502 classes in my program just for strings? -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-30 12:54 ` Maciej Sobczak @ 2007-05-30 13:56 ` Dmitry A. Kazakov 2007-05-30 16:49 ` vgodunko 2007-05-30 20:52 ` Maciej Sobczak 0 siblings, 2 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-30 13:56 UTC (permalink / raw) On 30 May 2007 05:54:02 -0700, Maciej Sobczak wrote: > On 30 Maj, 10:43, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >> Referential semantics is an implementation detail. > > No, it is not. It gives you explicitly the possibility to "reseat" the > referer as well as sharing semantics. These are not details. No shared semantics in this context. The values identify themselves, otherwise the type shall be limited and no assignment would come in question. > Even with strings, you might want to share the strategy for character > encoding. I don't. String has a value, only this counts. You claim that these values *semantically* cannot be assigned. This is obviously wrong. Semantically a string is a chain of code positions. I can assign them, I want do it. But even if that were semantically wrong, even so, why using pointer should magically change anything here? What was wrong without pointers stay wrong with them. Reverse is untrue. >> But it would be in vain, because assigning class-wide >> references in this context is semantically equivalent to assigning the >> targets. > > Which I say should be banned, because the targets are not statically > known. Why should I *statically* know string bounds and encoding? Any reason? >> When you create a void * you have to somehow >> specify the hidden parameter of the case-statement: >> >> void * X = Create_UTF8 ("foo"); >> >> With types you just specify the type of the object instead: >> >> X : UTF8_String := "foo"; > > Which - again - is not an assignment. > Just a reminder - we are talking about: > > X := Y; Remember, you have just allowed this, both are void *. You will need to dereference the pointer. At that point you will face your void. (:-)) It has to be a class to accommodate values of different representation. There is no escape. >> Your point was that I shall not do it publicly. But, may I dispatch >> privately? > > Privately you can dispatch to your internal strategies to get > character conversions, buffers, etc. How? To dispatch you need a class, which you have just scraped. >>>> Static polymorphism does not allow mixing types. >> >>> ? >> >> Instances from a statically polymorphic class of types are unrelated types. >> You cannot have any polymorphic object from that class, only specific >> objects. For the same reason you cannot have any class-wide operation from >> that class. > > So? Unrelated types cannot be mixed in a strongly typed language. >>>> Further you cannot design >>>> a library for formatting strings which would not be generic itself. >> >>> 1. So? >> >> Write an editor for such strings, store a string, send it over the network, >> do anything after uninstalling the compiler ... > > Still don't see the problem. How to instantiate a template when the type is unknown? >> What are you going to do if the endianess >> of Wide x n Character does not fit your machine? > > And why should I bother with endianness here? Because of the number of possible permutations of the bytes in Wide x n Character. I presume that Character, Wide x n Character are pointers to ... mmm, don't know what. Anyway, have fun! >> Whatever pattern you use, you will have to deal with this number of >> cases. > > Or I just delegate to locale and conversion library that is part of my > operating system. And to whom delegates the conversion library? (:-)) > On my system I can have LOTS of different locales: > > $ locale -a | wc -l > 502 > > Do you really expect me to have 502 classes in my program just for > strings? Yes, if you wanted to prevent locales from mixing, if you wanted to deal with text orientation in different languages, if you wanted locale-dependent sorting etc. There must be a deep elaborated hierarchy I suppose. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-30 13:56 ` Dmitry A. Kazakov @ 2007-05-30 16:49 ` vgodunko 2007-05-30 20:52 ` Maciej Sobczak 1 sibling, 0 replies; 81+ messages in thread From: vgodunko @ 2007-05-30 16:49 UTC (permalink / raw) On May 30, 5:56 pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > Yes, if you wanted to prevent locales from mixing, if you wanted to deal > with text orientation in different languages, if you wanted > locale-dependent sorting etc. There must be a deep elaborated hierarchy I > suppose. > If you need deal with text orientation, locale-independent and locale specific sorting you may just to use Unicode standards. Unicode address this and many others things. ;-) All you need is unified text representation (use one of UTF-* encoding) inside your program. Implementation of some Unicode algoriphms you may found at Ada-RU site ;-) http://www.ada-ru.org/src_ais.html http://www.ada-ru.org/files/ais-0.0.1.tar.gz ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-30 13:56 ` Dmitry A. Kazakov 2007-05-30 16:49 ` vgodunko @ 2007-05-30 20:52 ` Maciej Sobczak 2007-05-31 8:15 ` Dmitry A. Kazakov 1 sibling, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-05-30 20:52 UTC (permalink / raw) On 30 Maj, 15:56, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > >> Referential semantics is an implementation detail. > > > No, it is not. It gives you explicitly the possibility to "reseat" the > > referer as well as sharing semantics. These are not details. > > No shared semantics in this context. The values identify themselves, > otherwise the type shall be limited and no assignment would come in > question. When I think about it, it seems to me that you have just pinned down the very essence of this problem. There is no need to artificially banning assignments for class-wide types. Just force them all to be limited. Problem solved. I'm not joking. There is a distinction between value types and object types. These names might not be meaningful within the Ada terminology, but the border line is basically between types which instances represent themselves and those which instances represent external concepts or entities (but not necessarily). The latter often form hierarchies. When I said that I've never seen reasonable assignment with a hierarchy, it expressed exactly this distinction. Assignment is for values, not for objects. And guess what? You often need generics (templates, etc.) for value types. Don't get rid of them yet. ;-) > > Even with strings, you might want to share the strategy for character > > encoding. > > I don't. String has a value, only this counts. According to the above distinction, string is a value type. > You claim that these values > *semantically* cannot be assigned. No. String can (and should) have assignment. But its internal strategy for character encoding might not. It might be replaceable (yes, we have references!), but the strategy object itself is not necessarily assignable - and shouldn't be if it's class- wide. > Why should I *statically* know string bounds and encoding? Any reason? You might simply want to. For example, to separate domains. But you don't need to know and then you can have replaceable strategies within a single string type. > > Just a reminder - we are talking about: > > > X := Y; > > Remember, you have just allowed this, both are void *. It is you who has introduced void* into this discussion. I have no idea why. > >> Your point was that I shall not do it publicly. But, may I dispatch > >> privately? > > > Privately you can dispatch to your internal strategies to get > > character conversions, buffers, etc. > > How? To dispatch you need a class, which you have just scraped. Internal strategy can be a class. That's a good place for dispatch (note: I use the term "strategy" from the OO design pattern with the same name). > >> Write an editor for such strings, store a string, send it over the network, > >> do anything after uninstalling the compiler ... > > > Still don't see the problem. > > How to instantiate a template when the type is unknown? You don't. What was the problem? > >> What are you going to do if the endianess > >> of Wide x n Character does not fit your machine? > > > And why should I bother with endianness here? > > Because of the number of possible permutations of the bytes in Wide x n > Character. I presume that Character, Wide x n Character are pointers to ... > mmm, don't know what. Anyway, have fun! I still have no idea why you have introduced endianness into this discussion. What does it have to do with assigning class-wide types? The concept of endianness is meaningful only for data marshalling (when the data is in transit, not when it's stored), which is outside of string itself. Again - "strategy" is the keyword here. > >> Whatever pattern you use, you will have to deal with this number of > >> cases. > > > Or I just delegate to locale and conversion library that is part of my > > operating system. > > And to whom delegates the conversion library? (:-)) Why should I bother? Does it bother you that the mechanics of writing to NFS file are different from writing to FIFO pipe? Same Ada code can do both. We have operating systems to provide services, right? (Unless we write operating systems, that is. :-) ) > > On my system I can have LOTS of different locales: > > > $ locale -a | wc -l > > 502 > > > Do you really expect me to have 502 classes in my program just for > > strings? > > Yes, if you wanted to prevent locales from mixing, if you wanted to deal > with text orientation in different languages, if you wanted > locale-dependent sorting etc. There must be a deep elaborated hierarchy I > suppose. Good luck, then. :-) -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-30 20:52 ` Maciej Sobczak @ 2007-05-31 8:15 ` Dmitry A. Kazakov 2007-05-31 13:46 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-31 8:15 UTC (permalink / raw) On 30 May 2007 13:52:16 -0700, Maciej Sobczak wrote: > On 30 Maj, 15:56, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>>> Referential semantics is an implementation detail. >> >>> No, it is not. It gives you explicitly the possibility to "reseat" the >>> referer as well as sharing semantics. These are not details. >> >> No shared semantics in this context. The values identify themselves, >> otherwise the type shall be limited and no assignment would come in >> question. > > When I think about it, it seems to me that you have just pinned down > the very essence of this problem. > There is no need to artificially banning assignments for class-wide > types. Just force them all to be limited. Problem solved. > > I'm not joking. You cannot make a class of non-limited types limited. It would be inconsistent. A specific object is just a constrained instance of a class-wide object. This is the model of what is going on. Whether the objects from the class should or not have assignment or any other operation depends solely on the problem domain. It is the programmer's choice. > There is a distinction between value types and object types. Between objects with and without identity. > These > names might not be meaningful within the Ada terminology, but the > border line is basically between types which instances represent > themselves and those which instances represent external concepts or > entities (but not necessarily). The latter often form hierarchies. > When I said that I've never seen reasonable assignment with a > hierarchy, it expressed exactly this distinction. Assignment is for > values, not for objects. No. In fact, it is much simpler. There is no magic in identity, which is just a mapping object->ID value. You cannot assign an object with identity in terms of its "value" only. This is quite obvious, because such object is a pair: (ID, Value). How to build a pair using only one component of? But the pair itself is again a plain value which can be assigned, why not? The source of confusion is only in an unsound type system people have in mind. The types of a pair and its component are different. The value of a polymorphic object is a pair (Type-ID, Specific-value). When a type is made limited, this not because its class-wides will have Type-ID, called Tag in Ada. It is because there is an identity of be it plain or polymorphic object in the problem domain. These are independent identities. > And guess what? You often need generics (templates, etc.) for value > types. Don't get rid of them yet. ;-) I *never* need generics in the following sense. If a language L cannot be used without some meta language M (templates), then scrap L and use M instead: L := M; -- (:-)) If neither L nor M is really usable alone, then guess what? Redesign one of them! >>> Even with strings, you might want to share the strategy for character >>> encoding. >> >> I don't. String has a value, only this counts. > > According to the above distinction, string is a value type. As well as the polymorphic string from the class of: (UTF-8, "abc") := (ASCII, "def"); -- Why is it a problem? >> Why should I *statically* know string bounds and encoding? Any reason? > > You might simply want to. For example, to separate domains. If I wanted this I would put a corresponding constraint, not otherwise. I don't want the language to impose arbitrary constraints on my design. >>>> Your point was that I shall not do it publicly. But, may I dispatch >>>> privately? >> >>> Privately you can dispatch to your internal strategies to get >>> character conversions, buffers, etc. >> >> How? To dispatch you need a class, which you have just scraped. > > Internal strategy can be a class. That's a good place for dispatch > (note: I use the term "strategy" from the OO design pattern with the > same name). This is what I meant. Let's move to the internals. How the internal class can be assigned? Can it? >>>> Write an editor for such strings, store a string, send it over the network, >>>> do anything after uninstalling the compiler ... >> >>> Still don't see the problem. >> >> How to instantiate a template when the type is unknown? > > You don't. What was the problem? That I can't. Show me an implementation of an editor based generic strings: generic with type Char is private; Length : Natural; package Mess is type String is array (1..Length) of Char; ... >>>> Whatever pattern you use, you will have to deal with this number of >>>> cases. >> >>> Or I just delegate to locale and conversion library that is part of my >>> operating system. >> >> And to whom delegates the conversion library? (:-)) > > Why should I bother? You should not, except that delegating is not about work, it is management. I suppose, MS Project is the "language" for that... (:-)) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-31 8:15 ` Dmitry A. Kazakov @ 2007-05-31 13:46 ` Maciej Sobczak 2007-06-01 7:29 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-05-31 13:46 UTC (permalink / raw) On 31 Maj, 10:15, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > You cannot make a class of non-limited types limited. Why not? Let's just say so or write it in RM. > It would be > inconsistent. Protected objects are limited even if they contain only Integers. The inconsistencies are there anyway, so why fighting for unrealistic purity? > A specific object is just a constrained instance of a > class-wide object. Nothing prevents it from having additional operations (in the specific view, not class-wide). > This is the model of what is going on. Whether the > objects from the class should or not have assignment or any other operation > depends solely on the problem domain. It is the programmer's choice. Exactly, but the language "helps" the programmer to make some choices. In Ada most of the time the justification for these is "it's safer". In my opinion it is safer to not have assignments in class-wide types and I have nothing against the language to remind me. > >>> Even with strings, you might want to share the strategy for character > >>> encoding. > > >> I don't. String has a value, only this counts. > > > According to the above distinction, string is a value type. > > As well as the polymorphic string from the class of: > > (UTF-8, "abc") := (ASCII, "def"); -- Why is it a problem? It assumes too much. To do this operation you need to dispatch to some encoding or converting strategy anyway - but then, why elevate it so high? Keep it internally: declare X, Y : String; -- one String type is enough begin X.setEncoding(ASCII); X := "def"; Y.setEncoding(UTF8); Y := X; end; The above of course cannot work with the current semantics of assignment in Ada. This is unfortunately broken. > > Internal strategy can be a class. That's a good place for dispatch > > (note: I use the term "strategy" from the OO design pattern with the > > same name). > > This is what I meant. Let's move to the internals. How the internal class > can be assigned? Can it? You don't need to assign it. It is enough if you can replace it. Yes, referential semantics is useful sometimes. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-31 13:46 ` Maciej Sobczak @ 2007-06-01 7:29 ` Dmitry A. Kazakov 2007-06-01 13:32 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-06-01 7:29 UTC (permalink / raw) On 31 May 2007 06:46:51 -0700, Maciej Sobczak wrote: > On 31 Maj, 10:15, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >> You cannot make a class of non-limited types limited. > > Why not? Let's just say so or write it in RM. > >> It would be inconsistent. > > Protected objects are limited even if they contain only Integers. > The inconsistencies are there anyway, so why fighting for unrealistic > purity? Container /= what it occasionally contains. The identity of protected object is essential for synchronization mechanism, for obvious reasons. This is why the protected object has identity and consequently its type was made limited. You cannot abstract Integer from a protected object of. >> A specific object is just a constrained instance of a >> class-wide object. > > Nothing prevents it from having additional operations (in the specific > view, not class-wide). Not in the same package, where adding operations after the freezing point is forbidden. The reason is same - inconsistency. You are trying to invent some alternative meaning for class-wide. What for? If you think that the concept of values from types sets is wrong, say why. >> This is the model of what is going on. Whether the >> objects from the class should or not have assignment or any other operation >> depends solely on the problem domain. It is the programmer's choice. > > Exactly, but the language "helps" the programmer to make some choices. > In Ada most of the time the justification for these is "it's safer". > In my opinion it is safer to not have assignments in class-wide types > and I have nothing against the language to remind me. It is safe, when it functions as I described earlier. Again, whatever the compiler does, the code generated shall not render to "raise Constraint_Error" or for that matter "format C: /q". >>>>> Even with strings, you might want to share the strategy for character >>>>> encoding. >> >>>> I don't. String has a value, only this counts. >> >>> According to the above distinction, string is a value type. >> >> As well as the polymorphic string from the class of: >> >> (UTF-8, "abc") := (ASCII, "def"); -- Why is it a problem? > > It assumes too much. I don't understand this. Is it semantically wrong? >>> Internal strategy can be a class. That's a good place for dispatch >>> (note: I use the term "strategy" from the OO design pattern with the >>> same name). >> >> This is what I meant. Let's move to the internals. How the internal class >> can be assigned? Can it? > > You don't need to assign it. It is enough if you can replace it. Care to explain difference? Doesn't assignment replace one value by another? -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-01 7:29 ` Dmitry A. Kazakov @ 2007-06-01 13:32 ` Maciej Sobczak 2007-06-01 14:53 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-06-01 13:32 UTC (permalink / raw) On 1 Cze, 09:29, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > You are trying to invent some alternative meaning for class-wide. Not really. There is a difference between this: X : T'Class := Y; and this: X := Y; -- with X, Y : T'Class; The difference is coming from the fact that T'Class gets bound at initialization only and cannot change its binding (tag for you) later on (X cannot change the actual type). In other words, X can become equivalent to Y by initialization, but not by assignment. The latter is already too late - you might be assigning Triangles to Circles. This is what makes T'Class different from T. Of course, you can say that the meaning of assignment and equivalence are both user-defined and can go together, but this way of thinking usually subverts the regular meaning of :=. Not the Ada way. > > You don't need to assign it. It is enough if you can replace it. > > Care to explain difference? You can replace a Circle with a Triangle but you better not try to assign them. Note that you normally replace a Circle with a Triangle by *assignments* of references of the appropriate interface type. ;-) This indirection solves the problem, because references (pointers) themselves are values. No, hierarchies and values don't go together. I might lack the formalism to prove it, but my observations are confirming: I've never seen it in the wild. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-01 13:32 ` Maciej Sobczak @ 2007-06-01 14:53 ` Dmitry A. Kazakov 2007-06-01 20:31 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-06-01 14:53 UTC (permalink / raw) On Fri, 01 Jun 2007 06:32:30 -0700, Maciej Sobczak wrote: > On 1 Cze, 09:29, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >> You are trying to invent some alternative meaning for class-wide. > > Not really. > There is a difference between this: > > X : T'Class := Y; > > and this: > > X := Y; -- with X, Y : T'Class; > > The difference is coming from the fact that T'Class gets bound at > initialization only and cannot change its binding (tag for you) later > on (X cannot change the actual type). This is a constraint. It does not change the meaning of T'Class. The pragmatic argument is that this constraint cannot be checked at compile time, so you cannot make it illegal. The formal argument is that values of T'Class are supposed to mimic ones of the specific types from class T (generic programming etc). So IF T (the root type of the class) has a primitive subprogram F then T'Class shall act on it. This is the definition of class and primitive subprograms of. In particular, if T has ":=" that is equivalent to polymorphic ":=" being defined on T'Class. There is no obvious way to change this, otherwise than by providing another definition of T'Class and class T and primitive subprograms of or claiming that there is a semantic difference between X:=Y and Assign(X,Y). I don't want a language with magical, semantically non-decomposable assignments. [Contravariant operations of T'Class have been discussed earlier] > In other words, X can become equivalent to Y by initialization, but > not by assignment. The latter is already too late - you might be > assigning Triangles to Circles. > This is what makes T'Class different from T. No, it makes different construction from example and assignment. They are indeed different, however one could be defined in terms of another. But this by no means influences the semantics of T'Class. >>> You don't need to assign it. It is enough if you can replace it. >> >> Care to explain difference? > > You can replace a Circle with a Triangle but you better not try to > assign them. This is not an explanation. Show a semantic difference between changing a name binding from one value to another by A) "replacement" and B) "assignment." Ah, wouldn't replacement double dispatching? Then let "assignment" := "replacement," and we are back where we started from. (:-)) > No, hierarchies and values don't go together. I might lack the > formalism to prove it, but my observations are confirming: I've never > seen it in the wild. Lacking formalism is not an excuse, it is an indictment. (:-)) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-01 14:53 ` Dmitry A. Kazakov @ 2007-06-01 20:31 ` Maciej Sobczak 2007-06-02 8:19 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-06-01 20:31 UTC (permalink / raw) On 1 Cze, 16:53, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > or claiming that there is a semantic difference between X:=Y > and Assign(X,Y). I don't want a language with magical, semantically > non-decomposable assignments. You have them at the level of fundamental types. You can treat X:=Y and Assign(X,Y) to be equivalent for *your* T (and if you have complete control over overloading of assignment then that might be actually the case), but at some level of detail they have to be implemented in terms of *non-decomposable* assignments of fundamental types anyway. Now, the difference between T and T'Class is that operations with T can be statically decomposed down to this non- decomposable bricks. With T'Class it's just impossible to do this mechanically and it's unrealistic to expect it from the programmer for any non-trivial hierarchy. > > You can replace a Circle with a Triangle but you better not try to > > assign them. > > This is not an explanation. Show a semantic difference between changing a > name binding from one value to another by A) "replacement" and B) > "assignment." No problem. Replacement is when you just stop refering to X and start refering to Y (or its clone if the objects are mutable). Assignment is when you decompose down to real, hard-core-metal-and-concrete ':=' on fundamental types. The difference is that (in Ada terms) replacement allows you to change the tag. Assignment cannot do this. > Ah, wouldn't replacement double dispatching? No, it wouldn't and that's the second difference. Replacement just gets rid of the current value before the new value is refered to, which means that from the dispatching point of view this operation is simply linear on right-hand-side - it's a pure single dispatch. Assignment has to be '2D' with both axes, or O(n2) in terms of implementation complexity. It has to be double-dispatching to make any sense and this is unrealistic implementation-wise. That's why T'Class, even though very interesting from the language construction point of view, cannot entirely include all functionality of access variable to T'Class (or pointer to base class in C++ jargon). > Then let "assignment" := > "replacement," Do you mean: - let's replace assignment by replacement, or - let's assign replacement to assignment? ;-) > > No, hierarchies and values don't go together. I might lack the > > formalism to prove it, but my observations are confirming: I've never > > seen it in the wild. > > Lacking formalism is not an excuse, it is an indictment. (:-)) I don't really think I will ever convince you, but this discussion is an interesting exercise anyway. ;-) T'Class should be limited. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-01 20:31 ` Maciej Sobczak @ 2007-06-02 8:19 ` Dmitry A. Kazakov 2007-06-02 16:49 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-06-02 8:19 UTC (permalink / raw) On Fri, 01 Jun 2007 13:31:04 -0700, Maciej Sobczak wrote: > On 1 Cze, 16:53, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >> or claiming that there is a semantic difference between X:=Y >> and Assign(X,Y). I don't want a language with magical, semantically >> non-decomposable assignments. > > You have them at the level of fundamental types. So what? > You can treat X:=Y and Assign(X,Y) to be equivalent for *your* T (and > if you have complete control over overloading of assignment then that > might be actually the case), but at some level of detail they have to > be implemented in terms of *non-decomposable* assignments of > fundamental types anyway. How a fundamental X:=Y differs from fundamental Assign(X,Y)? The question is not about implementation of, but about semantic differences. (It is the first time I meet somebody who tries to deny procedural decomposition. You have to chance.) > Now, the difference between T and T'Class is > that operations with T can be statically decomposed down to this non- > decomposable bricks. With T'Class it's just impossible to do this > mechanically and it's unrealistic to expect it from the programmer for > any non-trivial hierarchy. The operations of T'Class are perfectly decomposable. There are two of them (in Ada) 1. Class-wide operation has one body. 2. Primitive operation. The body is composed out for the bodies of the operations defined for the specific types. This precisely means: 2.a. The specific body is explicitly overridden 2.b. It is inherited, and thus, it is built as a composition of a type view conversion and the body of the parent type. >>> You can replace a Circle with a Triangle but you better not try to >>> assign them. >> >> This is not an explanation. Show a semantic difference between changing a >> name binding from one value to another by A) "replacement" and B) >> "assignment." > > No problem. Replacement is when you just stop refering to X and start > refering to Y (or its clone if the objects are mutable). Assignment is > when you decompose down to real, hard-core-metal-and-concrete ':=' on > fundamental types. Mixing abstraction levels is a logical fallacy. You have to choose one, which would define the semantics. Take any of above and show a difference: either between "start to refer" and "start to have", or else between the sequences of machine implementing "start to what." > The difference is that (in Ada terms) replacement > allows you to change the tag. Assignment cannot do this. Neither can. But to show this we'd need to formalize it. Briefly: types access T and T are different types. The semantic of assignment of access T is not one of T, and neither is of the assignment of T'Class! It is a typed language, after all... >> Ah, wouldn't replacement double dispatching? > > No, it wouldn't and that's the second difference. Replacement just > gets rid of the current value before the new value is refered to, > which means that from the dispatching point of view this operation is > simply linear on right-hand-side - it's a pure single dispatch. No, this does not work. Apart from being semantically wrong in general case, it is technically wrong. You have to dispatch to finalize LHS. You have to dispatch to re-construct it from RHS. See above, in Ada there is no choice beyond class-wide and primitive operation. The latter dispatches immediately, the former postpones the dispatch until its body. In both cases it ultimately dispatches on both arguments. So semantically there is no difference. Also see my earlier posts about ambiguity of the signatures T x T'Class and T'Class x T in presence of derived types. > Assignment has to be '2D' with both axes, or O(n2) in terms of > implementation complexity. It has to be double-dispatching to make any > sense and this is unrealistic implementation-wise. It is semantically this way. There is nothing to do about it, but to scrap either assignment or class. (We are going in circles) > That's why T'Class, even though very interesting from the language > construction point of view, cannot entirely include all functionality > of access variable to T'Class (or pointer to base class in C++ > jargon). This is irrelevant as they are different types. In fact an access type could be treated as a member of the class, but then its assignment would have a deep copy semantics and you are back where you have started from. (C++ OO model is really poisoning. Don't mix types, just don't.) >> Then let "assignment" := >> "replacement," > > Do you mean: > - let's replace assignment by replacement, or > - let's assign replacement to assignment? > > ;-) Take what you wish, but referential semantic is fundamentally insufficient, in the sense that you cannot have a language without values, but only references. This is the conceptual fault of Java et al. Once you had at least one value at the end of the referential chains, you'd have to assign (:-)) it and give a type to it. (It is about typed languages, right?) Then because your types algebra has classes one could create a class of. In the end you will face the same question again: what is the assignment on the class. All this referential stuff is just shifting subjects. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-02 8:19 ` Dmitry A. Kazakov @ 2007-06-02 16:49 ` Maciej Sobczak 2007-06-03 7:09 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-06-02 16:49 UTC (permalink / raw) On 2 Cze, 10:19, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > The difference is that (in Ada terms) replacement > > allows you to change the tag. Assignment cannot do this. > > Neither can. Yes, replacement can change the tag. It's the tag of referred object that can change. An access variable can point to Circle and then to Triangle. This is the kind of replacement I'm talking about. But you cannot assign Triangle to Circle. > But to show this we'd need to formalize it. Briefly: types > access T and T are different types. Sure. It's just not the type of reference I'm interested in in a regular program, but the type of referred object. > The semantic of assignment of access T > is not one of T, and neither is of the assignment of T'Class! It is a typed > language, after all... Of course. That's why it is useful to distinguish them. And since we came here from the notion of strategies (in strings, but that was so long ago that it barely matters now), then replacing one strategy with another can be implemented in terms of assigning access variables. It is just not the level of abstraction I'm interested in. I don't care about access variables. What I care about is the fact that I can replace one strategy with another. I can also replace a wheel in my car but it's not assignment of wheels. Replacement and assignment are different things from the design point of view, even if replacement for T is implemented in terms of assignment for access T - that's just not the level of detail I care about. > >> Ah, wouldn't replacement double dispatching? > > > No, it wouldn't and that's the second difference. Replacement just > > gets rid of the current value before the new value is refered to, > > which means that from the dispatching point of view this operation is > > simply linear on right-hand-side - it's a pure single dispatch. > > No, this does not work. Apart from being semantically wrong in general > case, it is technically wrong. Why is it semantically wrong? Consider replacing wheels in your car. > You have to dispatch to finalize LHS. You > have to dispatch to re-construct it from RHS. It doesn't change anything in my reasoning. Two linear dispatches don't magically give you double dispatch! In terms of implementation complexity, it is O(n+m) problem, not O(n*m). The difference comes from the fact that finalizing LHS has no influence on re-constructing from RHS. > > Assignment has to be '2D' with both axes, or O(n2) in terms of > > implementation complexity. It has to be double-dispatching to make any > > sense and this is unrealistic implementation-wise. > > It is semantically this way. There is nothing to do about it, but to scrap > either assignment or class. (We are going in circles) That's exactly what I'm talking about from the very beginning - T'Class should be limited. Is it only my impression that we actually agree? :-) > (C++ OO model is really poisoning. Don't mix types, just don't.) In what way is the C++ OO model relevant here? I'm talking about replacing wheels in my car. Twice a year I *replace* summer wheels for winter ones and the other way round. I *don't assign* them. > Take what you wish, but referential semantic is fundamentally insufficient, > in the sense that you cannot have a language without values, but only > references. Of course, I've never said that. You need both to do anything useful. > Once you had at > least one value at the end of the referential chains, you'd have to assign > (:-)) it and give a type to it. Yes. > Then > because your types algebra has classes one could create a class of. In the > end you will face the same question again: what is the assignment on the > class. All this referential stuff is just shifting subjects. No, it's not shifting subjects. It's actually what makes it possible to replace wheels in a car. Or encoding strategies in a string object. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-02 16:49 ` Maciej Sobczak @ 2007-06-03 7:09 ` Dmitry A. Kazakov 2007-06-03 22:04 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-06-03 7:09 UTC (permalink / raw) On Sat, 02 Jun 2007 09:49:38 -0700, Maciej Sobczak wrote: > On 2 Cze, 10:19, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>> The difference is that (in Ada terms) replacement >>> allows you to change the tag. Assignment cannot do this. >> >> Neither can. > > Yes, replacement can change the tag. It's the tag of referred object > that can change. An access variable can point to Circle and then to > Triangle. No, this is illegal. At least in Ada,: access Circle and access Triangle are different types. They cannot be assigned to each other. > I can also replace a wheel in my car but it's not assignment of > wheels. If you want to bring the issue of identity (the wheel) then that's again subject shifting. >>>> Ah, wouldn't replacement double dispatching? >> >>> No, it wouldn't and that's the second difference. Replacement just >>> gets rid of the current value before the new value is refered to, >>> which means that from the dispatching point of view this operation is >>> simply linear on right-hand-side - it's a pure single dispatch. >> >> No, this does not work. Apart from being semantically wrong in general >> case, it is technically wrong. > > Why is it semantically wrong? Because variables of T'Class can be assigned with the values of T'Class if T has assignment. > Consider replacing wheels in your car. You should have annotated that example with types, values and variables. >> You have to dispatch to finalize LHS. You >> have to dispatch to re-construct it from RHS. > > It doesn't change anything in my reasoning. Two linear dispatches > don't magically give you double dispatch! Of course it does. A function of two arguments is still a function of two arguments. Decomposability of the function into a product is irrelevant to the issue. It dispatches in both arguments. >>> Assignment has to be '2D' with both axes, or O(n2) in terms of >>> implementation complexity. It has to be double-dispatching to make any >>> sense and this is unrealistic implementation-wise. >> >> It is semantically this way. There is nothing to do about it, but to scrap >> either assignment or class. (We are going in circles) > > That's exactly what I'm talking about from the very beginning - > T'Class should be limited. > Is it only my impression that we actually agree? :-) Not at all. The point is: limited T <=> limited T'Class No more, no less. > I'm talking about replacing wheels in my car. Twice a year I *replace* > summer wheels for winter ones and the other way round. I *don't > assign* them. If you mean assignment of pointers, then pointers are typed in Ada. If the pointers here have the same type (like P is access Wheel'Class) then there is nothing to talk about. You assign P to P. That is just irrelevant to the problem at hand. The issue is about assigning *different* types from a class, like ones from Wheel'Class (Goodyear, Michelin), or like ones from (access Wheel)'Class. Note brackets! The latter is illegal in Ada, but if it were legal, you'd have exactly the same thing. >> Then >> because your types algebra has classes one could create a class of. In the >> end you will face the same question again: what is the assignment on the >> class. All this referential stuff is just shifting subjects. > > No, it's not shifting subjects. Of course it is, because the types are same. The issue of identity / references you are trying to bring in is irrelevant. Multiple dispatch assignment is fully applicable to wheel and car objects. When you "replace" wheels, you could perform different tasks depending on the types of the old and new wheels .How these tasks can be encapsulated into one replacement action? -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-03 7:09 ` Dmitry A. Kazakov @ 2007-06-03 22:04 ` Maciej Sobczak 2007-06-04 8:08 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-06-03 22:04 UTC (permalink / raw) On 3 Cze, 09:09, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > Yes, replacement can change the tag. It's the tag of referred object > > that can change. An access variable can point to Circle and then to > > Triangle. > > No, this is illegal. At least in Ada,: access Circle and access Triangle > are different types. They cannot be assigned to each other. I was talking about access Object'Class with Circle and Triangle being both derived from Object. The access variable can point to object with different tag during its lifetime. > >>>> Ah, wouldn't replacement double dispatching? > > >>> No, it wouldn't and that's the second difference. Replacement just > >>> gets rid of the current value before the new value is refered to, > >>> which means that from the dispatching point of view this operation is > >>> simply linear on right-hand-side - it's a pure single dispatch. > > >> No, this does not work. Apart from being semantically wrong in general > >> case, it is technically wrong. > > > Why is it semantically wrong? > > Because variables of T'Class can be assigned with the values of T'Class if > T has assignment. We are going in circles. I say T'Class should be limited. Even if T is not. > >> You have to dispatch to finalize LHS. You > >> have to dispatch to re-construct it from RHS. > > > It doesn't change anything in my reasoning. Two linear dispatches > > don't magically give you double dispatch! > > Of course it does. A function of two arguments is still a function of two > arguments. Decomposability of the function into a product is irrelevant to > the issue. It dispatches in both arguments. It doesn't change anything. Think about extensibility of the system. One of the virtues of OO is the ability of the system to be reused with new types. If you have a hierarchy with N types then adding new type means you have to implement 1 finalizer and 1 cloning (if you want them, of course) and all replacement semantics in the system still works fine. In other words, you don't have to involve all other types in the hierarchy when implementing a new type. With doubly-dispatching assignment, adding new type to the same hierarchy means you have to implement N assignments for each potential situation. Or even 2*N. Now think about the hierarchy that has many branches which are extended independently... You can theoretize as long as you want and you can say (even rightly) that replacement is doubly-dispatching, but there *is* a difference between O(n+m) and O(n*m) - and this difference is what makes assignment of T'Class simply unrealistic. > Not at all. The point is: > > limited T <=> limited T'Class > > No more, no less. That's your point. Mine is T'Class should be limited. > The issue of identity / references you are trying to bring in is > irrelevant. Multiple dispatch assignment is fully applicable to wheel and > car objects. When you "replace" wheels, you could perform different tasks > depending on the types of the old and new wheels .How these tasks can be > encapsulated into one replacement action? They are not. That's the feature of replacement. This, however, brings another point - if you want to execute different assignment tasks depending on both LHS and RHS *types*, then let's extend it a bit: I want to execute different tasks depending on current *values* of both LHS and RHS. After all, if assignment is supposed to be doubly-dispatching and if it's also supposed to be no different than hypothetical Assign(X, Y), then I should be able to do this, right? Right? Unfortunately, this is completely broken in Ada. This is just another reason why doubly-dispatching assignment cannot exist. Not only it's unrealistic to implement correctly (remember the complexity), there is simply no reasonable structure to build upon. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-03 22:04 ` Maciej Sobczak @ 2007-06-04 8:08 ` Dmitry A. Kazakov 2007-06-04 17:02 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-06-04 8:08 UTC (permalink / raw) On Sun, 03 Jun 2007 15:04:00 -0700, Maciej Sobczak wrote: > On 3 Cze, 09:09, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>> Yes, replacement can change the tag. It's the tag of referred object >>> that can change. An access variable can point to Circle and then to >>> Triangle. >> >> No, this is illegal. At least in Ada,: access Circle and access Triangle >> are different types. They cannot be assigned to each other. > > I was talking about access Object'Class with Circle and Triangle being > both derived from Object. What is the difference between the types access T'Class and T'Class? > We are going in circles. I say T'Class should be limited. > Even if T is not. If I'd say access T'Class should be limited even if T is not? > You can theoretize as long as you want and you can say (even rightly) > that replacement is doubly-dispatching, but there *is* a difference > between O(n+m) and O(n*m) - and this difference is what makes > assignment of T'Class simply unrealistic. No, that makes it unrealistic to manage this complexity using crude patterns instead of a consistent approach. The problem is here, the choice is between language support and manual cascaded case-statements. >> The issue of identity / references you are trying to bring in is >> irrelevant. Multiple dispatch assignment is fully applicable to wheel and >> car objects. When you "replace" wheels, you could perform different tasks >> depending on the types of the old and new wheels .How these tasks can be >> encapsulated into one replacement action? > > They are not. That's the feature of replacement. > > This, however, brings another point - if you want to execute different > assignment tasks depending on both LHS and RHS *types*, then let's > extend it a bit: I want to execute different tasks depending on > current *values* of both LHS and RHS. After all, if assignment is > supposed to be doubly-dispatching and if it's also supposed to be no > different than hypothetical Assign(X, Y), then I should be able to do > this, right? Wrong. These are independent. Double dispatch controls the choice of the specific body, it tells nothing about how this body has to be assembled. There could be different ways of doing this, with advantages and disadvantages. In some cases you might really want LHS being finalized before the overridden part of the assignment, like when it holds an expensive resource. Then you have an issue of enforcing finalization on LHS. My approach, not shared here, is that the programmer should have more control over this, when he wants. The constructors, destructors, assignments and aggregates are all polymorphic subroutines composed differently from normal primitive and class-wide operations. In a better language one should be able to describe all these decompositions uniformly, with a higher-level mechanism of polymorphic subprograms. Handling discriminants and types tags is a separate issue of constraint description and propagation. > Right? > Unfortunately, this is completely broken in Ada. It is not broken, it does not exist. Adjust is not an assignment, it is hack to provide functionality without much rethinking. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-04 8:08 ` Dmitry A. Kazakov @ 2007-06-04 17:02 ` Maciej Sobczak 2007-06-05 8:35 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-06-04 17:02 UTC (permalink / raw) On 4 Cze, 10:08, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > I was talking about access Object'Class with Circle and Triangle being > > both derived from Object. > > What is the difference between the types access T'Class and T'Class? Access T'Class can be reseated to point to some other object from T'Class, even if that other object has different tag. Thanks to this, access T'Class can be used to provide replacement semantics for T'Class, where assignment wouldn't work. I believe I get the Ada basics right. > > We are going in circles. I say T'Class should be limited. > > Even if T is not. > > If I'd say access T'Class should be limited even if T is not? No, access Whatever should not be unconditionally limited, otherwise there would be little justification for its existence in the type system. The raison d'etre for access T is the ability to *point* - and be *reseated*. Of course, there is a place for constant access Whatever, but this is another story. > > You can theoretize as long as you want and you can say (even rightly) > > that replacement is doubly-dispatching, but there *is* a difference > > between O(n+m) and O(n*m) - and this difference is what makes > > assignment of T'Class simply unrealistic. > > No, that makes it unrealistic to manage this complexity using crude > patterns instead of a consistent approach. The problem is here, the choice > is between language support and manual cascaded case-statements. No, there is no choice whatsoever. There is nothing that cascaded case- statements can buy you that you couldn't do with dispatch (except maybe static coverage tests - but these don't exist in open hierarchies). There are fundamental reasons for which assignment for T'Class doesn't make sense (it would lead to objects that can change type at runtime - you cannot get further away from Ada than that) and no matter what magic you try, you will always hit the same wall. Consider (again): procedure Swap(X : in out Object'Class; X in out Object'Class) begin -- whatever end Swap; and then: declare X : Triangle; Y : Circle; begin Swap(X, Y); -- oops end; It does not matter what is inside Swap. Double-dispatch, cascading case, unchecked magic, whatever. These are all low-level details of something that just doesn't fly anyway. > > This, however, brings another point - if you want to execute different > > assignment tasks depending on both LHS and RHS *types*, then let's > > extend it a bit: I want to execute different tasks depending on > > current *values* of both LHS and RHS. After all, if assignment is > > supposed to be doubly-dispatching and if it's also supposed to be no > > different than hypothetical Assign(X, Y), then I should be able to do > > this, right? > > Wrong. These are independent. Double dispatch controls the choice of the > specific body, it tells nothing about how this body has to be assembled. Yes, but this shows that assignment in Ada cannot be modeled by any hypothetical Assign subprogram. No analogies here. This in turn means that introducing full double-dispatch to the discussion doesn't help much, because implementing it would not be at all similar to anything. > In some cases you might really want LHS being finalized > before the overridden part of the assignment, like when it holds an > expensive resource. 1. In *some* cases. In those cases I can take this decision on my own. 2. Actually, in those cases when LHS keeps expensive resource I would very much welcome the ability to reuse it, if possible. Consider: declare S1 : My_String_Type := "abcd"; S2 : My_String_Type := "xyz"; begin S1 := S2; end; No need to finalize LHS here (and reallocate again to hold a copy of RHS). Other examples abound. Sorry to say this, but when I see things like *this*, I have an impression that Ada is SbyC (Slow by Construction - you can quote me :-) ). This is *obvious* optimization opportunity! But that was aside, nothing about T'Class being limited. > My approach, not shared here, is that the programmer should have more > control over this, when he wants. Hm... do we agree again? :-) > The constructors, destructors, > assignments and aggregates are all polymorphic subroutines composed > differently from normal primitive and class-wide operations. Agreed with exceptions: - constructors cannot be polymorphic, since there is no tag yet (you might dispatch on something different, though, but this is irrelevant) - assignments are banned anyway ;-) Destructors are obviously polymorphic. > In a better > language one should be able to describe all these decompositions uniformly, > with a higher-level mechanism of polymorphic subprograms. Not really. The problem is that these things work on the border of object's life and death and for this reason they cannot be first-class citizens in the world of polymorphic operations. > > Unfortunately, this is completely broken in Ada. > > It is not broken, it does not exist. Adjust is not an assignment, it is > hack to provide functionality without much rethinking. Hacks make things broken. Every language has its hacks, but this one is just too fundamental to be easily forgiven. ;-) -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-04 17:02 ` Maciej Sobczak @ 2007-06-05 8:35 ` Dmitry A. Kazakov 2007-06-05 22:12 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-06-05 8:35 UTC (permalink / raw) On Mon, 04 Jun 2007 10:02:48 -0700, Maciej Sobczak wrote: > On 4 Cze, 10:08, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>> I was talking about access Object'Class with Circle and Triangle being >>> both derived from Object. >> >> What is the difference between the types access T'Class and T'Class? > > Access T'Class can be reseated to point to some other object from > T'Class, even if that other object has different tag. So what? According to you that is wrong because there exist n*n possible combinations of such assignments for different tags. In what sense these combinations are different for T'Class and access T'Class? >>> We are going in circles. I say T'Class should be limited. >>> Even if T is not. >> >> If I'd say access T'Class should be limited even if T is not? > > No, access Whatever should not be unconditionally limited, otherwise > there would be little justification for its existence in the type > system. > The raison d'etre for access T is the ability to *point* - and be > *reseated*. While for T'Class it is the ability to accommodate any specific T. Where is a difference? > There are fundamental reasons for which assignment for T'Class doesn't > make sense (it would lead to objects that can change type at runtime - > you cannot get further away from Ada than that) and no matter what > magic you try, you will always hit the same wall. > Consider (again): > > procedure Swap(X : in out Object'Class; X in out Object'Class) > begin > -- whatever > end Swap; > > and then: > > declare > X : Triangle; > Y : Circle; > begin > Swap(X, Y); procedure Swap (X, Y : in out String); declare X : String := "abc"; Y : String := "klmnoprst"; begin Swap (X, Y); -- oops Why your "fundamental" reasons do not apply here? The only truly fundamental reason is that if Object was declared non-limited, then its contract is to support assignment. It is then the programmer responsibility to fulfill this contract for Triangle and Circle. Don't blame the language for your own design faults. >> Wrong. These are independent. Double dispatch controls the choice of the >> specific body, it tells nothing about how this body has to be assembled. > > Yes, but this shows that assignment in Ada cannot be modeled by any > hypothetical Assign subprogram. No analogies here. This in turn means > that introducing full double-dispatch to the discussion doesn't help > much, because implementing it would not be at all similar to anything. It is still independent. The thing is not fully decomposable into subprograms because that would require type violation, which is impossible in a typed language. Therefore some magic is needed to convert raw memory to an object and back. This is irrelevant to dispatch. And if that were a reason to forbid assignments, then *any* assignment would become illegal. Because magic is needed, assignment, construction and destruction will always be compiler-generated with some injections of user-defined subprograms at the points where necessary [sub]objects are fully constructed/destructed. This is no different from polymorphic subprograms, they are also compiler-generated. >> My approach, not shared here, is that the programmer should have more >> control over this, when he wants. > > Hm... do we agree again? :-) No, because you want to disallow assignment altogether, others want to keep it albeit sometimes inconsistent, but I want to change the language to have a way to enforce its consistency. > - constructors cannot be polymorphic, since there is no tag yet (you > might dispatch on something different, though, but this is irrelevant) There is the tag, it exists before the object and comes from the type of. A constructor (actually a part of) dispatches on the bare tag. I posted a description of my view on this some time ago: http://groups.google.com/group/comp.lang.ada/browse_thread/thread/72c34c66b38e0e05/b4b6668d530912ac?lnk=gst&q=kazakov+construction&rnum=6#b4b6668d530912ac >> In a better >> language one should be able to describe all these decompositions uniformly, >> with a higher-level mechanism of polymorphic subprograms. > > Not really. The problem is that these things work on the border of > object's life and death and for this reason they cannot be first-class > citizens in the world of polymorphic operations. The same applies to primitive operations. Dispatch happens per magic. I suppose there is a more general mechanism based on types constraints. But in any case it will be "magical." But that will be the only magic necessary. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-05 8:35 ` Dmitry A. Kazakov @ 2007-06-05 22:12 ` Maciej Sobczak 2007-06-06 8:21 ` Dmitry A. Kazakov 2007-06-06 15:32 ` Markus E Leypold 0 siblings, 2 replies; 81+ messages in thread From: Maciej Sobczak @ 2007-06-05 22:12 UTC (permalink / raw) On 5 Cze, 10:35, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > >>> I was talking about access Object'Class with Circle and Triangle being > >>> both derived from Object. > > >> What is the difference between the types access T'Class and T'Class? > > > Access T'Class can be reseated to point to some other object from > > T'Class, even if that other object has different tag. > > So what? According to you that is wrong because there exist n*n possible > combinations of such assignments for different tags. In what sense these > combinations are different for T'Class and access T'Class? In the sense that access T'Class has only one combination to cover and since access is a fundamental type, this is handled by the language. You don't have to do anything to make assignment for access T'Class work! > > No, access Whatever should not be unconditionally limited, otherwise > > there would be little justification for its existence in the type > > system. > > The raison d'etre for access T is the ability to *point* - and be > > *reseated*. > > While for T'Class it is the ability to accommodate any specific T. Where is > a difference? In assignment. (We are really going in circles.) > procedure Swap (X, Y : in out String); > > declare > X : String := "abc"; > Y : String := "klmnoprst"; > begin > Swap (X, Y); > -- oops OK, even better. Let's ban assignment for all unconstrained types. :-) > Why your "fundamental" reasons do not apply here? The "fundamental" reasons apply here as well. The problem is of a general nature. > The only truly fundamental reason is that if Object was declared > non-limited, then its contract is to support assignment. It is then the > programmer responsibility to fulfill this contract for Triangle and Circle. > Don't blame the language for your own design faults. No. Regular objects are declared as Triangles and Circles (and these types may have reasonable assignments - after all, assigning one Triangle to another makes sense). The problem is when you declare something as T'Class. T'Class /= T and this is one of the things that make them different. I know that it shakes part of the concept of a class. But you have the same problem with unconstrained types. They might not support all operations of their constrained equivalents. > > - constructors cannot be polymorphic, since there is no tag yet (you > > might dispatch on something different, though, but this is irrelevant) > > There is the tag, it exists before the object and comes from the type of. A > constructor (actually a part of) dispatches on the bare tag. I don't agree on this. X : T; Y : T'Class := Whatever; Do_Something(X); -- (1) Do_Something(Y); -- (2) Which of the two above are dispatching (assume Do_Something is primitive)? My understanding of the concepts is that only (2) is a dispatching call, (1) is not. The constructors are non-dispatching, no matter how much of the "fetus object" is there when the call is made. Even if there is a tag, it's known statically and then this is just overloading, not polymorphism in the OO sense. > > Not really. The problem is that these things work on the border of > > object's life and death and for this reason they cannot be first-class > > citizens in the world of polymorphic operations. > > The same applies to primitive operations. Dispatch happens per magic. I > suppose there is a more general mechanism based on types constraints. But > in any case it will be "magical." But that will be the only magic > necessary. Still, there is a difference between T and T'Class. As far as I understand it, constructor calls are not dispatching. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-05 22:12 ` Maciej Sobczak @ 2007-06-06 8:21 ` Dmitry A. Kazakov 2007-06-06 14:46 ` Maciej Sobczak 2007-06-06 15:32 ` Markus E Leypold 1 sibling, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-06-06 8:21 UTC (permalink / raw) On Tue, 05 Jun 2007 15:12:29 -0700, Maciej Sobczak wrote: > On 5 Cze, 10:35, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>>>> I was talking about access Object'Class with Circle and Triangle being >>>>> both derived from Object. >> >>>> What is the difference between the types access T'Class and T'Class? >> >>> Access T'Class can be reseated to point to some other object from >>> T'Class, even if that other object has different tag. >> >> So what? According to you that is wrong because there exist n*n possible >> combinations of such assignments for different tags. In what sense these >> combinations are different for T'Class and access T'Class? > > In the sense that access T'Class has only one combination to cover and > since access is a fundamental type, this is handled by the language. > You don't have to do anything to make assignment for access T'Class > work! Hence you agree that when assignment is defined then it is no matter how many combination of exists. Therefore your argument about the number of combinations is irrelevant for any user- or compiler-generated assignment. In our earlier discussion I assumed this obvious, as it follows from separation of implementation and interface. Anyway, now, when the implementation is finally left to the problem domain, what is the *semantic* difference between access T'Class and T'Class beyond that? >>> No, access Whatever should not be unconditionally limited, otherwise >>> there would be little justification for its existence in the type >>> system. >>> The raison d'etre for access T is the ability to *point* - and be >>> *reseated*. >> >> While for T'Class it is the ability to accommodate any specific T. Where is >> a difference? > > In assignment. Nope, you cannot say that the difference is in assignment, because now we are talking about "ability to point" and "ability to hold." What makes "point" different to "hold", semantically I mean? >> procedure Swap (X, Y : in out String); >> >> declare >> X : String := "abc"; >> Y : String := "klmnoprst"; >> begin >> Swap (X, Y); >> -- oops > > OK, even better. Let's ban assignment for all unconstrained types. :-) My congratulations! But that's not all: procedure Swap (X, Y : in out Integer); declare X : Positive := 1; Y : Integer := -1; begin Swap (X, Y); -- oops I hope you see now, why your proposal is inconsistent with any type system with constrained [sub]types (classes is just a specific case of)? Further, it is incompatible with specialization and generic programming on sets of specialized types. It can be easily shown, that it is also incompatible with generalization and in the end with generic programming on any non-trivial sets of types. >> Why your "fundamental" reasons do not apply here? > > The "fundamental" reasons apply here as well. The problem is of a > general nature. Huh, speaking of general natures, the problem is a misconception shared by LSP-ers. The issue can be summarized in three lines: 1. Specialization breaks all out-methods of the base type 2. Generalization breaks all in-.methods of the base type 3. Variation breaks everything The rest is consequences. This cannot be fixed and need not to. >> The only truly fundamental reason is that if Object was declared >> non-limited, then its contract is to support assignment. It is then the >> programmer responsibility to fulfill this contract for Triangle and Circle. >> Don't blame the language for your own design faults. > > No. Regular objects are declared as Triangles and Circles (and these > types may have reasonable assignments - after all, assigning one > Triangle to another makes sense). The problem is when you declare > something as T'Class. Wrong. Assignment is a primitive operation <=> it is defined on T'Class. Specific assignments are irrelevant here. > T'Class /= T and this is one of the things that make them different. > I know that it shakes part of the concept of a class. Yep, it demolishes class and primitive operations of. > But you have the same problem with unconstrained types. They might not > support all operations of their constrained equivalents. What does it mean "not support"? You have to translate this into illegal ("type error") / legal ("no type error"). See above, there is no way out. Either you scrap the type system, by forbidding any intertype relations or you have to live with the cross cases. In the middle there exists a wide range of semi-consistent languages like C++ and, alas, Ada. >>> - constructors cannot be polymorphic, since there is no tag yet (you >>> might dispatch on something different, though, but this is irrelevant) >> >> There is the tag, it exists before the object and comes from the type of. A >> constructor (actually a part of) dispatches on the bare tag. > > I don't agree on this. > > X : T; > Y : T'Class := Whatever; > > Do_Something(X); -- (1) > Do_Something(Y); -- (2) > > Which of the two above are dispatching (assume Do_Something is > primitive)? So? Following your argument destructor of X isn't dispatching either, because it will not dispatch in this piece of code. A part of the constructor of T'Class is certainly dispatching. The constructor of T is specific for T. This makes constructor a primitive operation. On the other hand, constructors are composed by extension, which makes them class-wide operations as well (you have to construct T'Class for a specific S derived from T). This controversy is the reason why constructors aren't easy to get right. The idea of my proposal was to make it typed and factor out specific and class-wide parts a constructors as user-defined subprograms. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-06 8:21 ` Dmitry A. Kazakov @ 2007-06-06 14:46 ` Maciej Sobczak 2007-06-06 15:11 ` Maciej Sobczak 0 siblings, 1 reply; 81+ messages in thread From: Maciej Sobczak @ 2007-06-06 14:46 UTC (permalink / raw) On 6 Cze, 10:21, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > In the sense that access T'Class has only one combination to cover and > > since access is a fundamental type, this is handled by the language. > > You don't have to do anything to make assignment for access T'Class > > work! > > Hence you agree that when assignment is defined then it is no matter how > many combination of exists. No, you are jumping to wrong conclusions. If there exists exactly one combination (for access T'Class), then you can define a clear meaning for its implementation. In the case of access T'Class, the meaning is referential replacement. > Therefore your argument about the number of > combinations is irrelevant for any user- or compiler-generated assignment. It is very relevant. With the number of combinations in the order of O(n*n) you are not going to make it right. > what is the > *semantic* difference between access T'Class and T'Class beyond that? For example sharing. This is beyond the fundamental difference that exists between replacement and assignment. > Nope, you cannot say that the difference is in assignment, because now we > are talking about "ability to point" and "ability to hold." What makes > "point" different to "hold", semantically I mean? Ability to share? Let's keep sharing aside for the moment, though. The real difference is that object is not a container and the "holding" doesn't work there. Objects *have* values, they don't "hold" values. Containers can "hold" objects that *have* some values themselves. And so on, up to and including the fact that access variable can *point* to an object (that *has* some value). (yes, I know - you will now ask me what is the *semantic* difference between holding and having... nevermind :-) ) Pointing and having value are different - ability to share is one of the consequences, replacement-is-not-an-assignment is another. > > OK, even better. Let's ban assignment for all unconstrained types. :-) > > My congratulations! But that's not all: > > procedure Swap (X, Y : in out Integer); > > declare > X : Positive := 1; > Y : Integer := -1; > begin > Swap (X, Y); > -- oops Yes, let's ban it too. Inout parameters should match exactly in terms of constraints (see below). > I hope you see now, why your proposal is inconsistent with any type system > with constrained [sub]types (classes is just a specific case of)? No, I don't see why it's inconsistent. The above code gives CONSTRAINT_ERROR at run-time. It's obvious and the code is just dangerous. 1. for "in" formal parameters the actual parameter can have stricter constraints (it can be subrange, it can be subclass, etc.) 2. for "out" formal parameters (and return values) the actual parameter can have looser constraints (it can be superrange, it can be a superclass, etc.) 3. for "in out" formal parameters the actual parameter must have matching constraints. Nothing new. Just a regular pre- and post-condition stuff. If this is not guaranteed at compile-time, it has to be checked at run- time, but then why not writing everything in Python. All our examples so far fell into pieces with point 3. above. > Further, > it is incompatible with specialization and generic programming on sets of > specialized types. No, there is no incompatibility. At least I don't see it (and you didn't show any). > Huh, speaking of general natures, the problem is a misconception shared by > LSP-ers. The issue can be summarized in three lines: > > 1. Specialization breaks all out-methods of the base type > 2. Generalization breaks all in-.methods of the base type > 3. Variation breaks everything Elaborate please. > > No. Regular objects are declared as Triangles and Circles (and these > > types may have reasonable assignments - after all, assigning one > > Triangle to another makes sense). The problem is when you declare > > something as T'Class. > > Wrong. Assignment is a primitive operation <=> it is defined on T'Class. That's bad. And we are going in circles. > > T'Class /= T and this is one of the things that make them different. > > I know that it shakes part of the concept of a class. > > Yep, it demolishes class and primitive operations of. You have just thrown assignment together with other (real) primitive operations and then come to common conclusions. My point is that assignment is *not* a regular primitive operation. > > But you have the same problem with unconstrained types. They might not > > support all operations of their constrained equivalents. > > What does it mean "not support"? You have to translate this into illegal > ("type error") / legal ("no type error"). / conditionally_legal ("let's raise CONSTRAINT_ERROR later")? > Either you scrap the type system, by forbidding any intertype relations One of your discussion strategies is to force others to believe that they have only those choices that you arbitrariliy give them. :-) > In the middle there exists a wide > range of semi-consistent languages like C++ and, alas, Ada. Yes, that's why we have this discussion. :-) > > X : T; > > Y : T'Class := Whatever; > > > Do_Something(X); -- (1) > > Do_Something(Y); -- (2) > > > Which of the two above are dispatching (assume Do_Something is > > primitive)? > > So? Following your argument destructor of X isn't dispatching either, > because it will not dispatch in this piece of code. It will dispatch with Unchecked_Deallocation on access T'Class. And, basically, this is the only way to have dispatching destructor call. But there *is* a way to have polymorphic behavior for destructor, whereas there is no way to have polymorphic constructor. > A part of the constructor of T'Class is certainly dispatching. No. There is nothing to dispatch on and with T'Class there is even nothing to overload on: function Make return Triangle; function Make return Circle; X : Object'Class := Make; -- ? -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-06 14:46 ` Maciej Sobczak @ 2007-06-06 15:11 ` Maciej Sobczak 0 siblings, 0 replies; 81+ messages in thread From: Maciej Sobczak @ 2007-06-06 15:11 UTC (permalink / raw) On 6 Cze, 16:46, Maciej Sobczak <see.my.homep...@gmail.com> wrote: > > My congratulations! But that's not all: > > > procedure Swap (X, Y : in out Integer); > > > declare > > X : Positive := 1; > > Y : Integer := -1; > > begin > > Swap (X, Y); > > -- oops > > Yes, let's ban it too. Inout parameters should match exactly in terms > of constraints (see below). Here I was confused by something different. This subject is unrelated to assignment. If we want class-wide types to behave in all aspects like unconstrained types (or like constrained types that can have additional constraints added), then indeed this example shows serious problem with my reasoning. I will come back to this in a while. -- Maciej Sobczak http://www.msobczak.com/ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-05 22:12 ` Maciej Sobczak 2007-06-06 8:21 ` Dmitry A. Kazakov @ 2007-06-06 15:32 ` Markus E Leypold 1 sibling, 0 replies; 81+ messages in thread From: Markus E Leypold @ 2007-06-06 15:32 UTC (permalink / raw) <... snipped ...> > (We are really going in circles.) Welcome to the Kazakov Continuum. :-). Regards -- Markus ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 7:29 ` Maciej Sobczak 2007-05-24 8:02 ` Dmitry A. Kazakov @ 2007-05-24 10:42 ` Georg Bauhaus 2007-05-24 13:41 ` Dmitry A. Kazakov 2007-05-25 16:59 ` Markus E Leypold 1 sibling, 2 replies; 81+ messages in thread From: Georg Bauhaus @ 2007-05-24 10:42 UTC (permalink / raw) On Thu, 2007-05-24 at 10:02 +0200, Dmitry A. Kazakov wrote: > > Coming from C++, I have never seen any single example where assignment > > made sense with polymorphic hierarchies. ... > > > Do you know any such example? Can you show it? > > Marshaling objects. In fact, Eiffel has a relative, the assignment attempt (written "?="). And the Eiffel arguments versus Liskov/Wing are that the principles guiding program design should come from the solution to a problem, not from models when these cannot capture the solution. In a sense, it is argued that that L/W substitution (and also co/contra-variance) violate programming principles! For example, you can make a primitive operation abstract ("deferred", "take it away") in a derived type. This might make sense when for some good reasons, a *real* *world* heir cannot (a) have the inherited operation (b) change its parents. ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 10:42 ` Georg Bauhaus @ 2007-05-24 13:41 ` Dmitry A. Kazakov 2007-05-25 16:59 ` Markus E Leypold 1 sibling, 0 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-24 13:41 UTC (permalink / raw) On Thu, 24 May 2007 12:42:16 +0200, Georg Bauhaus wrote: > In fact, Eiffel has a relative, the assignment attempt > (written "?="). And the Eiffel arguments versus Liskov/Wing > are that the principles guiding program design should come > from the solution to a problem, not from models when these > cannot capture the solution. In a sense, it is argued that > that L/W substitution (and also co/contra-variance) violate > programming principles! Actually [absolute] LSP violates common sense. IF A were fully and unconditionally substitutable for B in all possible contexts, then As and Bs would be equivalent in any thinkable sense. Because then they would be indistinguishable without magic. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 10:42 ` Georg Bauhaus 2007-05-24 13:41 ` Dmitry A. Kazakov @ 2007-05-25 16:59 ` Markus E Leypold 2007-05-28 9:52 ` Georg Bauhaus 1 sibling, 1 reply; 81+ messages in thread From: Markus E Leypold @ 2007-05-25 16:59 UTC (permalink / raw) > On Thu, 2007-05-24 at 10:02 +0200, Dmitry A. Kazakov wrote: > >> > Coming from C++, I have never seen any single example where assignment >> > made sense with polymorphic hierarchies. > ... >> >> > Do you know any such example? Can you show it? >> >> Marshaling objects. > > In fact, Eiffel has a relative, the assignment attempt > (written "?="). And the Eiffel arguments versus Liskov/Wing > are that the principles guiding program design should come > from the solution to a problem, not from models when these > cannot capture the solution. In a sense, it is argued that > that L/W substitution (and also co/contra-variance) violate > programming principles! Actually I'm a strong disbeliever in your approach. I doubt, that a solution that isn't mathematically beautiful and simple (i.e. fits a model) is viable in the long run. Intellectual friction will hampoer maintenance and optimization. Regards -- Markus ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-25 16:59 ` Markus E Leypold @ 2007-05-28 9:52 ` Georg Bauhaus 2007-05-28 11:50 ` Dmitry A. Kazakov ` (2 more replies) 0 siblings, 3 replies; 81+ messages in thread From: Georg Bauhaus @ 2007-05-28 9:52 UTC (permalink / raw) Markus E Leypold wrote: > the Eiffel arguments versus Liskov/Wing >> are that the principles guiding program design should come >> from the solution to a problem, not from models when these >> cannot capture the solution. In a sense, it is argued that >> that L/W substitution (and also co/contra-variance) violate >> programming principles! > > Actually I'm a strong disbeliever in your approach. I doubt, that a > solution that isn't mathematically beautiful and simple (i.e. fits a > model) is viable in the long run. Intellectual friction will hampoer > maintenance and optimization. There is nothing wrong with a mathematically "beautiful" and simple solution. But when (1) there is a perfectly simple, straight forward, working solution, (2) the solution matches the problem specification 1:1, (3) the solution is partially incongruent with a mathematical model, (4) because of (3), the solution (1)+(2) is discarded, there is something wrong. First, because a belief in the model as a conditio sine qua non is not justifiable, neither on business terms, nor on technical terms (both because of (1)+(2)). Second, while the existing solution suggests that there might be a better model still the other model is preferred, and the existing solution runs the risk of being discarded. I bet that as soon as someone popularizes a mathematical model that does away with variance issues for example, then people will stop fighting over contravariance versus covariance. Until then, only this or that kind of variance is allowed to exist, for either mathematical or problem domain reasons, because the other solution cannot but create an unmaintainable mess... If I had the money, I'd put up a challange that triggers some programming oriented model research (as opposed to research that will move the focus of modelling to formal properties of models only.) ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-28 9:52 ` Georg Bauhaus @ 2007-05-28 11:50 ` Dmitry A. Kazakov 2007-05-28 23:32 ` Georg Bauhaus 2007-05-28 13:47 ` Markus E Leypold 2007-05-28 13:56 ` Markus E Leypold 2 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-28 11:50 UTC (permalink / raw) On Mon, 28 May 2007 11:52:22 +0200, Georg Bauhaus wrote: > There is nothing wrong with a mathematically "beautiful" > and simple solution. But when > > (1) there is a perfectly simple, straight forward, working > solution, > > (2) the solution matches the problem specification 1:1, > > (3) the solution is partially incongruent with a mathematical > model, > > (4) because of (3), the solution (1)+(2) is discarded, > > there is something wrong. Nothing is wrong except naive LSP. When understood naively it is mathematically unsound. The problem of is that it tries to make very strong statements about sets (of values) as whole rather than individual elements of. A mathematical interpretation of naive LSP is: Any true proposition involving the name of the set T stays true when this name is substituted by the name of another set S This is extremely strong. Controversies like Circle-Ellipse are predictable: forall X,Y exists Ellipse with the axis X,Y "Circle" cannot substitute "Ellipse" in above. All usual ways to save LSP are about limiting the substitution context and protecting some appearances of the type from substitution (e.g. contravariance). > If I had the money, I'd put up a challange that triggers some > programming oriented model research (as opposed to research that > will move the focus of modelling to formal properties of models > only.) Difference? -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-28 11:50 ` Dmitry A. Kazakov @ 2007-05-28 23:32 ` Georg Bauhaus 2007-05-29 12:05 ` Dmitry A. Kazakov 2007-05-29 13:33 ` Georg Bauhaus 0 siblings, 2 replies; 81+ messages in thread From: Georg Bauhaus @ 2007-05-28 23:32 UTC (permalink / raw) Dmitry A. Kazakov wrote: >> If I had the money, I'd put up a challange that triggers some >> programming oriented model research (as opposed to research that >> will move the focus of modelling to formal properties of models >> only.) > > Difference? The idealized difference between programming oriented (model) research and researching formal properties of models is that, in the first case, you do not start from models only and then find different models, refine existing models etc.. You start in the programming shop, so to speak. In fact, this is happening. For example, timing properties of co-operating procedures are captured in mathematical models. This is different from the ubiquitously alleged mathematicality of programming: that programs model mathematical functions. Some programs do, but is this all that matters? Shouldn't the relation between programs and mathematical functions be reversed? Wait, it is! ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-28 23:32 ` Georg Bauhaus @ 2007-05-29 12:05 ` Dmitry A. Kazakov 2007-05-29 13:33 ` Georg Bauhaus 1 sibling, 0 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-29 12:05 UTC (permalink / raw) On Tue, 29 May 2007 01:32:50 +0200, Georg Bauhaus wrote: > Dmitry A. Kazakov wrote: > >>> If I had the money, I'd put up a challange that triggers some >>> programming oriented model research (as opposed to research that >>> will move the focus of modelling to formal properties of models >>> only.) >> >> Difference? > > The idealized difference between programming oriented (model) research > and researching formal properties of models is that, in the first case, > you do not start from models only and then find different models, > refine existing models etc.. You start in the programming shop, so to > speak. But an application program is always a model of some physical reality. > In fact, this is happening. For example, timing properties of > co-operating procedures are captured in mathematical models. This is > different from the ubiquitously alleged mathematicality of programming: > that programs model mathematical functions. Some programs do, but is this > all that matters? Yes, because application programming deals with mathematical models of the reality. I doubt you could skip this abstraction layer. > Shouldn't the relation between programs and mathematical > functions be reversed? Wait, it is! You can consider mathematics or parts of as programming on some "axiomatic hardware". But this is not programming in usual, narrow sense, which applies only to physical hardware. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-28 23:32 ` Georg Bauhaus 2007-05-29 12:05 ` Dmitry A. Kazakov @ 2007-05-29 13:33 ` Georg Bauhaus 2007-05-29 17:29 ` Dmitry A. Kazakov 1 sibling, 1 reply; 81+ messages in thread From: Georg Bauhaus @ 2007-05-29 13:33 UTC (permalink / raw) On Tue, 2007-05-29 at 14:05 +0200, Dmitry A. Kazakov wrote: > > The idealized difference between programming oriented (model) research > > and researching formal properties of models is that, in the first case, > > you do not start from models only and then find different models, > > refine existing models etc.. You start in the programming shop, so to > > speak. > > But an application program is always a model of some physical reality. Yes. I just think that programs should be written with (a model of) physical reality in mind, but employ Off-the-Shelf standard models only if applicable. > > In fact, this is happening. For example, timing properties of > > co-operating procedures are captured in mathematical models. This is > > different from the ubiquitously alleged mathematicality of programming: > > that programs model mathematical functions. Some programs do, but is this > > all that matters? > > Yes, because application programming deals with mathematical models of the > reality. I doubt you could skip this abstraction layer. Programming deals with models of reality and, empirically, most of the time these models have properties that can be described using mathematics. Which is a good thing. However, asking for *mathematical* models of reality is asking for a clean room definition of the physical model in terms of mathematics. Many programs just happen to be a model of reality, and they still work well, even though they were not mathematically designed. The programmers are hardly aware of every conceivable mathematical property of their model. Still, they write sound programs. Can mathematics capture this process? Next, how much detail can you reasonably place in a formal specification of a model? Programming in UML seems to be over the top, IMHO, whereas modeling in UML using a reasonably small set of L isn't, of course. > > Shouldn't the relation between programs and mathematical > > functions be reversed? Wait, it is! > > You can consider mathematics or parts of as programming on some "axiomatic > hardware". What I want is the mathematics of program models, more than programming to satisfy mathematics. BTW, what is the best suited mathematical structure that (minimally) describes the loop while k < 100 loop k := Integer'succ(k); end loop; and how does it help? ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-29 13:33 ` Georg Bauhaus @ 2007-05-29 17:29 ` Dmitry A. Kazakov 2007-05-29 20:46 ` Georg Bauhaus 0 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-29 17:29 UTC (permalink / raw) On Tue, 29 May 2007 15:33:54 +0200, Georg Bauhaus wrote: > On Tue, 2007-05-29 at 14:05 +0200, Dmitry A. Kazakov wrote: > >>> In fact, this is happening. For example, timing properties of >>> co-operating procedures are captured in mathematical models. This is >>> different from the ubiquitously alleged mathematicality of programming: >>> that programs model mathematical functions. Some programs do, but is this >>> all that matters? >> >> Yes, because application programming deals with mathematical models of the >> reality. I doubt you could skip this abstraction layer. > > Programming deals with models of reality and, empirically, most of the > time these models have properties that can be described using > mathematics. Which is a good thing. > However, asking for *mathematical* models of reality is asking for > a clean room definition of the physical model in terms of mathematics. Any mathematical model is still an approximation. The reason why mathematics is better here than ad-hocery of outsourced programmers, is that the mathematical "hardware" is far more powerful than one of Intel. Hence there is a far less constraints imposed upon you when you model it mathematically. So even if you had a very good mathematical model it might be quite difficult to implement it on the machine hardware. This is all programming is about - porting mathematics. (:-)) To model the reality in just one hop might be just impossible for a normal programmer. You cannot rely on Nobel Price laureates, they are rare, expensive and uninterested in our problems. > Many programs just happen to be a model of reality, and they still > work well, even though they were not mathematically designed. > The programmers are hardly aware of every conceivable mathematical > property of their model. Still, they write sound programs. How can you know that? (:-)) Let's take the braking control system for your next car... >>> Shouldn't the relation between programs and mathematical >>> functions be reversed? Wait, it is! >> >> You can consider mathematics or parts of as programming on some "axiomatic >> hardware". > > What I want is the mathematics of program models, more than programming > to satisfy mathematics. BTW, what is the best suited mathematical > structure that (minimally) describes the loop > > while k < 100 loop > k := Integer'succ(k); > end loop; Ordered set, mathematical induction etc. > and how does it help? A lot. For example it can tell the code reviewer that the above program is equivalent to: if k < 100 then k := 100; end if; -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-29 17:29 ` Dmitry A. Kazakov @ 2007-05-29 20:46 ` Georg Bauhaus 2007-05-30 7:53 ` Dmitry A. Kazakov 2007-05-30 13:18 ` Georg Bauhaus 0 siblings, 2 replies; 81+ messages in thread From: Georg Bauhaus @ 2007-05-29 20:46 UTC (permalink / raw) Dmitry A. Kazakov wrote: > This is all > programming is about - porting mathematics. (:-)) To model the reality in > just one hop might be just impossible for a normal programmer. You cannot > rely on Nobel Price laureates, they are rare, expensive and uninterested in > our problems. OK. So why should a normal programmer like me consider it a cost effective idea to try to construct a proven mathematical model for my daily work? I do sometimes run Prolog or SETL to help me with higher level specifications and find this quite helpful. OTOH, in these cases I am drawing upon models that are different from, say, LSP. They are more like general purpose mathematical building blocks. LSP is a formal and well defined whole. Arguably, it isn't a good model from some points of view, even though there is "mathematics" in it which, you say, programming will port to a program. >> Many programs just happen to be a model of reality, and they still >> work well, even though they were not mathematically designed. >> The programmers are hardly aware of every conceivable mathematical >> property of their model. Still, they write sound programs. > > How can you know that? (:-)) Let's take the braking control system for your > next car... I hear that Mercedes engineers do not yet want to replace the steering wheel with a embedded computer joy stick thing ... :-) I think the argument is that a car must make quicker moves in more constraining environments than your average airliner. >> What I want is the mathematics of program models, more than programming >> to satisfy mathematics. BTW, what is the best suited mathematical >> structure that (minimally) describes the loop >> >> while k < 100 loop >> k := Integer'succ(k); >> end loop; > > Ordered set, mathematical induction etc. Uhm, can we have less than a group? >> and how does it help? > > A lot. For example it can tell the code reviewer that the above program is > equivalent to: > > if k < 100 then > k := 100; > end if; Making assumptions about k ... This is how mathematics can make us prefer default perspectives by moving all important points of view behind Formal Mountains. The above change wouldn't have helped at all, on the contrary, for k'Address use ...; What's the mathematics of that, then? :-) ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-29 20:46 ` Georg Bauhaus @ 2007-05-30 7:53 ` Dmitry A. Kazakov 2007-05-30 13:18 ` Georg Bauhaus 1 sibling, 0 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-30 7:53 UTC (permalink / raw) On Tue, 29 May 2007 22:46:12 +0200, Georg Bauhaus wrote: > Dmitry A. Kazakov wrote: >> This is all >> programming is about - porting mathematics. (:-)) To model the reality in >> just one hop might be just impossible for a normal programmer. You cannot >> rely on Nobel Price laureates, they are rare, expensive and uninterested in >> our problems. > > OK. So why should a normal programmer like me consider it a cost effective > idea to try to construct a proven mathematical model for my daily work? Why are you sure that your daily work isn't based on such models? You could use them unconsciously. >> How can you know that? (:-)) Let's take the braking control system for your >> next car... > > I hear that Mercedes engineers do not yet want to replace > the steering wheel with a embedded computer joy stick thing ... :-) > I think the argument is that a car must make quicker moves in more > constraining environments than your average airliner. Come on, embedded controllers operate the valves and ignition of the motor right now. Be sure burning is a lot faster than steering / braking. But that is not the point. The point one is - how would you enjoy the "spinal programming" model of things you do care? The second point is - why to program anything we don't care? >>> What I want is the mathematics of program models, more than programming >>> to satisfy mathematics. BTW, what is the best suited mathematical >>> structure that (minimally) describes the loop >>> >>> while k < 100 loop >>> k := Integer'succ(k); >>> end loop; >> >> Ordered set, mathematical induction etc. > > Uhm, can we have less than a group? Yes, as you don't mention the operation of. >>> and how does it help? >> >> A lot. For example it can tell the code reviewer that the above program is >> equivalent to: >> >> if k < 100 then >> k := 100; >> end if; > > Making assumptions about k ... This is how mathematics can make us prefer > default perspectives by moving all important points of view behind Formal > Mountains. The above change wouldn't have helped at all, on the contrary, > > for k'Address use ...; > > What's the mathematics of that, then? That k is a pair of functions mapping the system state to the value of k and reverse: Get_k : S -> Integer Set_k : S x Integer -> S Thank you for making my point! What mathematics makes here is all the rest, which could well be incomputable. Consider k a hardware random generator. You could not describe it in any programming language, but you can in the language of probability theory. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-29 20:46 ` Georg Bauhaus 2007-05-30 7:53 ` Dmitry A. Kazakov @ 2007-05-30 13:18 ` Georg Bauhaus 2007-05-31 10:27 ` Dmitry A. Kazakov 2007-05-31 11:44 ` Georg Bauhaus 1 sibling, 2 replies; 81+ messages in thread From: Georg Bauhaus @ 2007-05-30 13:18 UTC (permalink / raw) On Wed, 2007-05-30 at 09:53 +0200, Dmitry A. Kazakov wrote: > > OK. So why should a normal programmer like me consider it a cost effective > > idea to try to construct a proven mathematical model for my daily work? > > Why are you sure that your daily work isn't based on such models? You could > use them unconsciously. Hm. If you are suggesting that we have a working model of unconscious mathematics, then in a sense, empirical programming science would try to observe its working patterns then. > The point one is - how would you enjoy the > "spinal programming" model of things you do care? The second point is - why > to program anything we don't care? I'm assuming programmers who work carefully. Let them be guided by principles. The principles need not be the best thing since sliced bread only because they have been described standard math terms. > >>> What I want is the mathematics of program models, more than programming > >>> to satisfy mathematics. BTW, what is the best suited mathematical > >>> structure that (minimally) describes the loop > >>> > >>> while k < 100 loop > >>> k := Integer'succ(k); > >>> end loop; > >> > >> Ordered set, mathematical induction etc. > > > > Uhm, can we have less than a group? > > Yes, as you don't mention the operation of. I mean, what mathematical model (such as LSP) is helpful in describing this particular loop, such that the math of the model rules out writing k := k + 1; for example? I would start with a model that isn't originally inspired by mathematics (because higher math typically frowns upon operations, that is, the things that computers do). My vague model has loop invariants, loop termination, and the fact that the type Integer happens to include a successor function in its interface. Now, the type Integer offers much more than that, e.g. "*". Still, it seems reasonable to start from the mathematical structure that maps to a type like Integer, even when I do not need either of the inverse, the neutral element, or commutativity, which Integer types have (mostly), among other things. > >>> and how does it help? > >> > >> A lot. For example it can tell the code reviewer that the above program is > >> equivalent to: > >> > >> if k < 100 then > >> k := 100; > >> end if; > > > > Making assumptions about k ... This is how mathematics can make us prefer > > default perspectives by moving all important points of view behind Formal > > Mountains. The above change wouldn't have helped at all, on the contrary, > > > > for k'Address use ...; > > > > What's the mathematics of that, then? > > That k is a pair of functions mapping the system state to the value of k > and reverse: > > Get_k : S -> Integer > Set_k : S x Integer -> S That's not a mathematical model. It is a pair of functions that can be mapped to some aspects of how k is supposedly operating, when amended with sequencing. I bet that few programmers writing the above loop will have functions in mind that map from and to world state. > Thank you for making my point! What mathematics makes here is all the rest, > which could well be incomputable. Consider k a hardware random generator. > You could not describe it in any programming language, but you can in the > language of probability theory. Thank you for making my point! :-) Probability theoretic constructs can be mapped into executable computer constructs only in very limited ways, some of them using clever bit shuffling, others relying on something *outside* the reach of mathematics, such as /dev/random (as opposed to /dev/urandom). Outside insofar as there is no practical way of mapping noise to a precise, hence programmable, formula. This doesn't make them any less useful. ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-30 13:18 ` Georg Bauhaus @ 2007-05-31 10:27 ` Dmitry A. Kazakov 2007-05-31 11:44 ` Georg Bauhaus 1 sibling, 0 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-31 10:27 UTC (permalink / raw) On Wed, 30 May 2007 15:18:27 +0200, Georg Bauhaus wrote: > I would start with a model that isn't originally inspired > by mathematics (because higher math typically frowns upon operations, > that is, the things that computers do). > My vague model has loop invariants, loop termination, > and the fact that the type Integer happens to include a successor > function in its interface. Now, the type Integer offers much > more than that, e.g. "*". Still, it seems reasonable to start from > the mathematical structure that maps to a type like Integer, > even when I do not need either of the inverse, > the neutral element, or commutativity, which Integer types have > (mostly), among other things. I don't see your point. You are saying that you don't need mathematics to program, yet you illustrate your point using mathematical vocabulary. There is something wrong with that. If programming should be based on, say, mobile phone throwing, then why are you talking about "neutral elements"? >> Thank you for making my point! What mathematics makes here is all the rest, >> which could well be incomputable. Consider k a hardware random generator. >> You could not describe it in any programming language, but you can in the >> language of probability theory. > > Thank you for making my point! :-) Probability theoretic constructs > can be mapped into executable computer constructs only in very limited > ways, No, the point is that they cannot be mapped (implemented), this is why mathematics is necessary to describe what is going on, because no machine would be sufficient for that. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-30 13:18 ` Georg Bauhaus 2007-05-31 10:27 ` Dmitry A. Kazakov @ 2007-05-31 11:44 ` Georg Bauhaus 2007-06-01 7:37 ` Dmitry A. Kazakov 1 sibling, 1 reply; 81+ messages in thread From: Georg Bauhaus @ 2007-05-31 11:44 UTC (permalink / raw) On Thu, 2007-05-31 at 12:27 +0200, Dmitry A. Kazakov wrote: > On Wed, 30 May 2007 15:18:27 +0200, Georg Bauhaus wrote: > > > I would start with a model that isn't originally inspired > > by mathematics (because higher math typically frowns upon operations, > > that is, the things that computers do). > > My vague model has loop invariants, loop termination, > > and the fact that the type Integer happens to include a successor > > function in its interface. Now, the type Integer offers much > > more than that, e.g. "*". Still, it seems reasonable to start from > > the mathematical structure that maps to a type like Integer, > > even when I do not need either of the inverse, > > the neutral element, or commutativity, which Integer types have > > (mostly), among other things. > > I don't see your point. You are saying that you don't need mathematics to > program, yet you illustrate your point using mathematical vocabulary. > There is something wrong with that. I'm saying that programmers do not necessarily write programs with mathematical structures in their head, but they still succeed. I don't call that unconscious mathematics, because we can't know that. It doesn't matter. When they write, a := a + 1; the may not be thinking about a commutative group of integers, and never use "-" or "0" in their program text. We may suggest, and many do (in particular in the non-Scheme FPL camp?), that program design should start from established pure mathematical structures only (or LSP, or ...). This way we will profit from the wealth of mathematical knowledge but we might also ignore successful solutions just because (1) they do not usually appear on math radar screens, or (2) contradict beliefs in known models (see co/contravariance). > >> Consider k a hardware random generator. > >> You could not describe it in any programming language, but you can in the > >> language of probability theory. > > > > Thank you for making my point! :-) Probability theoretic constructs > > can be mapped into executable computer constructs only in very limited > > ways, > No, the point is that they cannot be mapped (implemented), this is why > mathematics is necessary to describe what is going on, because no machine > would be sufficient for that. But what is actually going on in /dev/random? ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-31 11:44 ` Georg Bauhaus @ 2007-06-01 7:37 ` Dmitry A. Kazakov 2007-06-01 10:07 ` Markus E Leypold 2007-06-01 11:41 ` Georg Bauhaus 0 siblings, 2 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-06-01 7:37 UTC (permalink / raw) On Thu, 31 May 2007 13:44:40 +0200, Georg Bauhaus wrote: > I'm saying that programmers do not necessarily write programs > with mathematical structures in their head, but they still succeed. > I don't call that unconscious mathematics, because we can't know > that. It doesn't matter. When they write, > a := a + 1; > the may not be thinking about a commutative group of integers, It is no matter what they think, it matters what they write. [...] > but we might also ignore successful solutions > just because > (1) they do not usually appear on math radar screens, or > (2) contradict beliefs in known models (see co/contravariance). No solution is successful if not based on technological / scientific basis which would make "success" reproducible. That's apart from the question of measurement of "success". We don't talk about consuming programs, but about designing them. >> No, the point is that they cannot be mapped (implemented), this is why >> mathematics is necessary to describe what is going on, because no machine >> would be sufficient for that. > > But what is actually going on in /dev/random? Realization of a random variable. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-01 7:37 ` Dmitry A. Kazakov @ 2007-06-01 10:07 ` Markus E Leypold 2007-06-01 11:41 ` Georg Bauhaus 1 sibling, 0 replies; 81+ messages in thread From: Markus E Leypold @ 2007-06-01 10:07 UTC (permalink / raw) > On Thu, 31 May 2007 13:44:40 +0200, Georg Bauhaus wrote: >>> No, the point is that they cannot be mapped (implemented), this is why >>> mathematics is necessary to describe what is going on, because no machine >>> would be sufficient for that. >> >> But what is actually going on in /dev/random? > > Realization of a random variable. But is that trivial or not? - M ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-01 7:37 ` Dmitry A. Kazakov 2007-06-01 10:07 ` Markus E Leypold @ 2007-06-01 11:41 ` Georg Bauhaus 2007-06-01 13:07 ` Dmitry A. Kazakov 1 sibling, 1 reply; 81+ messages in thread From: Georg Bauhaus @ 2007-06-01 11:41 UTC (permalink / raw) Dmitry A. Kazakov wrote: > On Thu, 31 May 2007 13:44:40 +0200, Georg Bauhaus wrote: > >> When they write, >> a := a + 1; >> the may not be thinking about a commutative group of integers, > > It is no matter what they think, it matters what they write. This seems a contradiction to what you say below, about basing solutions on technology/science. OTOH, when we started from LSP as a design principle, we started from a guiding principle, I think. This means programmers who do not accidentally write following the principle. So what are the principles they have actually been following when producing a solution that works even though the math behind it is not known/mainstream/acceptable? > [...] >> but we might also ignore successful solutions >> just because >> (1) they do not usually appear on math radar screens, or >> (2) contradict beliefs in known models (see co/contravariance). > > No solution is successful if not based on technological / scientific basis > which would make "success" reproducible. That's apart from the question of > measurement of "success". > > We don't talk about consuming programs, but about designing them. >>> No, the point is that they cannot be mapped (implemented), this is why >>> mathematics is necessary to describe what is going on, because no machine >>> would be sufficient for that. >> But what is actually going on in /dev/random? > > Realization of a random variable. What is realization in a machine if not an implementation? ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-06-01 11:41 ` Georg Bauhaus @ 2007-06-01 13:07 ` Dmitry A. Kazakov 0 siblings, 0 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-06-01 13:07 UTC (permalink / raw) On Fri, 01 Jun 2007 13:41:11 +0200, Georg Bauhaus wrote: > Dmitry A. Kazakov wrote: >> On Thu, 31 May 2007 13:44:40 +0200, Georg Bauhaus wrote: >> >>> When they write, >>> a := a + 1; >>> the may not be thinking about a commutative group of integers, >> >> It is no matter what they think, it matters what they write. > > This seems a contradiction to what you say below, about basing > solutions on technology/science. Why? This basing is about an ability to communicate the solution = using some formalism (like one of mathematics). > OTOH, when we started from LSP as a design principle, we started > from a guiding principle, I think. Which indicates the actual problem... > This means programmers who > do not accidentally write following the principle. So what are > the principles they have actually been following when > producing a solution that works even though the math behind it > is not known/mainstream/acceptable? LSP as a vague principle is just useless and confusing. It results in countless silly discussions about Circles and Ellipses. Similarly, one could discuss designs of a perpetual-motion machine. A minimal formalism would immediately clarify it. >>>> No, the point is that they cannot be mapped (implemented), this is why >>>> mathematics is necessary to describe what is going on, because no machine >>>> would be sufficient for that. >>> But what is actually going on in /dev/random? >> >> Realization of a random variable. > > What is realization in a machine if not an implementation? Implementation of a realization is a number (stored/computed). Note the difference between a number and a random variable, hence the implementations of. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-28 9:52 ` Georg Bauhaus 2007-05-28 11:50 ` Dmitry A. Kazakov @ 2007-05-28 13:47 ` Markus E Leypold 2007-05-28 23:12 ` Georg Bauhaus 2007-05-28 13:56 ` Markus E Leypold 2 siblings, 1 reply; 81+ messages in thread From: Markus E Leypold @ 2007-05-28 13:47 UTC (permalink / raw) > Markus E Leypold wrote: >> the Eiffel arguments versus Liskov/Wing >>> are that the principles guiding program design should come >>> from the solution to a problem, not from models when these >>> cannot capture the solution. In a sense, it is argued that >>> that L/W substitution (and also co/contra-variance) violate >>> programming principles! >> Actually I'm a strong disbeliever in your approach. I doubt, that a >> solution that isn't mathematically beautiful and simple (i.e. fits a >> model) is viable in the long run. Intellectual friction will hampoer >> maintenance and optimization. > > There is nothing wrong with a mathematically "beautiful" > and simple solution. But when > > (1) there is a perfectly simple, straight forward, working > solution, > > (2) the solution matches the problem specification 1:1, > > (3) the solution is partially incongruent with a mathematical > model, > > (4) because of (3), the solution (1)+(2) is discarded, > > there is something wrong. But now you're arguing quite another case. You said >>> that the principles guiding program design should come from the >>> solution to a problem, not from models (But perhaps I'm reading even that quote wrongly). You've been basically advocating to invent new program architectures for every problem/solution pair, i.e. have a solution first than fit the "model" (language architecture, whatever) on your solution. If you can afford to design a domain language for every problem you have -- fine. If not, I suggest that the language designers hae Basically the language design is a contract: If you can fit your programming in a certain conceptual pattern (be it typing, OO or functional programming) the compiler builder promises that the compiler will produce a correct and (often) fast or efficient program with the advertised semantics. If you don't want that contract, I don't know what you get. Probably C, but even that has a "model" of sorts. Or SAP ... Bashing theory and inisiting on "fudged" ad hoc solutions ("it just works, so why not" has brought programming to the place where it is today: A mess. "If you do this you will get that, but we don't know why that is so and how that fits in with the rest which follows a different perhaps not even contradicting rule". I repeat, that I'm a strong believer in complete, consistent and "closed" solutions in programming language or system design without "undefined" borderline cases. Only such a design guarantees that you know where you stand, are prepared for the next step in evolution and can take it without breaking everything from the past. Regards -- Markus ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-28 13:47 ` Markus E Leypold @ 2007-05-28 23:12 ` Georg Bauhaus 0 siblings, 0 replies; 81+ messages in thread From: Georg Bauhaus @ 2007-05-28 23:12 UTC (permalink / raw) Markus E Leypold wrote: >>>> that the principles guiding program design should come from the >>>> solution to a problem, not from models > > (But perhaps I'm reading even that quote wrongly). You've been > basically advocating to invent new program architectures for every > problem/solution pair, i.e. have a solution first than fit the "model" > (language architecture, whatever) on your solution. No, not at all. Rather, much like biology starts from collecting observations of living things, "prographology" may again start from collecting observations of source texts and their authors. Then we will see a number of cases (1) the source texts do not solve the problems (2) the source texts do solve the problems, but we can't see how (3) the source texts solve the problems and we discover a pattern Now, if a model contradicts (3), are you saying that the model is right nevertheless and (3) is not relevant? ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-28 9:52 ` Georg Bauhaus 2007-05-28 11:50 ` Dmitry A. Kazakov 2007-05-28 13:47 ` Markus E Leypold @ 2007-05-28 13:56 ` Markus E Leypold 2007-05-28 23:00 ` Georg Bauhaus 2 siblings, 1 reply; 81+ messages in thread From: Markus E Leypold @ 2007-05-28 13:56 UTC (permalink / raw) > I bet that as soon as someone popularizes a mathematical model > that does away with variance issues for example, then people will > stop fighting over contravariance versus covariance. > Until then, only this or that kind of variance is allowed to exist, > for either mathematical or problem domain reasons, because the > other solution cannot but create an unmaintainable mess... > > If I had the money, I'd put up a challange that triggers some > programming oriented model research (as opposed to research that > will move the focus of modelling to formal properties of models > only.) Somehow you seem to think, we can just do away with the models, if we just approach it right. That ignores (a) historical experience -- see structured programming which has become so commonplace that we don't percieve it as exceptional any more, and (b) that there is no alternative[tm]: As you can't do quantum mechnics without a certain amount of a certain sort of mathematics you cannot state a contract or specify a design without some sort of logical and basically mathematical model behind. Without you don't even have a language to complain to your compiler writer about bugs. Progress, well, progresses. We get used to it, but it usually doesn't go away. The hope to get rid of "the variance issue" (which actually is just a point in how contracts can be stated precisely) after we discovered it, is just misguided. Regards -- Markus ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-28 13:56 ` Markus E Leypold @ 2007-05-28 23:00 ` Georg Bauhaus 0 siblings, 0 replies; 81+ messages in thread From: Georg Bauhaus @ 2007-05-28 23:00 UTC (permalink / raw) Markus E Leypold wrote: > >> I bet that as soon as someone popularizes a mathematical model >> If I had the money, I'd put up a challange that triggers some >> programming oriented model research > Somehow you seem to think, we can just do away with the models, if we > just approach it right. No. We might profit from mathematical models that are even better because they better reflect what programming staff needs. (Reminds me of the intent that has led to Ada.) Perhaps this was misleading: >> Until then, only this or that kind of variance is allowed to exist, >> for either mathematical or problem domain reasons, because the >> other solution cannot but create an unmaintainable mess... WRT variance, one of Meyer's examples is the allocation of boys and girls to dormitories.(*) Boys and girls are two sexes of the human kind, hence Boy and Girl are derived from the type Human. Girls' dormitories and boys' dormitories are two derived types of domitories. There is a procedure for each Human object, say Person, Person.Accomodate(dorm => ?Dormitory?); that will place a person in a dormitory, such that girls sleep in the girls' dormitory and boys sleep in the boys' dormitory. Shall we have a procedure for boys (or girls) that just takes a person of the respective sex and places the person in a dormitory'class? procedure Accomodate(who: Boy; dorm: Dormitory'class); (Of course, the "moral rule" depends on who you ask, but this example presumes gender segregation in any sexual sense.) There is a subsection in the corresponding section (OOSC2, �17) entitled "Polymorphic perversity". It alludes to the consequences of different "co/contravariance policies", speaking about a procedure that expresses "sharing a room". type Boy is new Human with private; overriding procedure Share(self: in out Boy; other: in out Boy); This definition meets the requirement that a boy and a girl shall not share a room. OTOH, there are also technical arguments in favor of defining a procedure that doesn't vary in the same "direction" as the type, thus type Boy is new Human with private; overriding procedure Share(self: in out Boy; other: in out Human'class); Which co/contravariance policy is right? And therefore, can we decide which model is the right one if all that we can say about a model is that it is mathematically sound in some sense? Of course it is, but soundness is very likely just a necessary precondition. Whether a model is sufficient, and more precisely, in which cases it is sufficient, these are the more interesting questions (to me, at least). (*) (There are certainly other solutions that do not involve inheritance, but this is the illustration given. I'm varying it slightly.) ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-23 19:47 Ada Interfaces and the Liskov Substitution Principle Stefan Lucks 2007-05-23 20:32 ` Ludovic Brenta 2007-05-23 20:54 ` Maciej Sobczak @ 2007-05-24 7:39 ` Dmitry A. Kazakov 2007-05-24 11:12 ` Stefan Lucks 2 siblings, 1 reply; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-24 7:39 UTC (permalink / raw) On Wed, 23 May 2007 21:47:32 +0200, Stefan Lucks wrote: > to me, it seems as if Ada 2005 is bluntly violating the Liskov > Substitution Prinicple. Huh, as well as Ada 83 did. There is no need in much code, just write subtype Non_LSP_Subtype is String (1..80); or subtype Positive is Integer range 1..Integer'Last; or, for that matter "constant", "in", "out", "not null", etc. All these are examples of non-LSP subtypes. > My understanding of the Liskov substitution principle, see > http://en.wikipedia.org/wiki/Liskov_substitution_principle > is that as Partens.Parent implicitely (by not being limited) provides > certain primitve operations, such as ":=" and "=", and Stepchild.Object > takes away these primitive operations, It is illegal, but if it were legal, then yes, disallowing operations breaks LSP. However, see above, mere passing a variable as "in" does it as well in the sense that "in T" is not an LSP-subtype of T. > What do you guys think about this? LPS is totally irrelevant as long as substitutability violation can be detected at compile time. This is why "constant" does not worry anybody. A method disallowing is perfectly OK, if you cannot call it. LSP violation becomes a problem when substitutability is indeterminable until run-time. In may cases we still choose to live with that. Constrained Ada subtypes is an example of. Another is multi-methods Foo (X, Y : T), when called on different children of T. In such cases Ada adds Constraint_Error to the interface of each subprogram and things become "substitutable" again. LSP violation is catastrophic when undetected. I think that renaming array objects with changing bounds falls under this category. (Apart from "unchecked" stuff) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 7:39 ` Dmitry A. Kazakov @ 2007-05-24 11:12 ` Stefan Lucks 2007-05-24 13:56 ` Dmitry A. Kazakov 0 siblings, 1 reply; 81+ messages in thread From: Stefan Lucks @ 2007-05-24 11:12 UTC (permalink / raw) Dmitry A. Kazakov wrote: > [...] mere passing a variable as "in" does it as > well in the sense that "in T" is not an LSP-subtype of T. You are using a very broad and generalised interpretation of the LSP. My interpretation -- and I believe this is the common and usual one -- is that "X: in T" in the parameterlist of a subprogram does not deal with some "artificial" type "in T", just with "T". The "in" is part of the subprogram's contract, not a part of X's contract. So there is no conflict with LSP. > LPS is totally irrelevant as long as substitutability violation can be > detected at compile time. This is why "constant" does not worry anybody. A > method disallowing is perfectly OK, if you cannot call it. So your very broad and generalised interpretation of the LSP is totally irrelevant, except for the special case where it overlaps with the more narrow usual interpretation. Perhaps you should follow the crowd and narrow your interpretation as well? Whatever interpretation, the stuff below is right. > LSP violation becomes a problem when substitutability is indeterminable > until run-time. In may cases we still choose to live with that. Constrained > Ada subtypes is an example of. Another is multi-methods Foo (X, Y : T), > when called on different children of T. In such cases Ada adds > Constraint_Error to the interface of each subprogram and things become > "substitutable" again. Yes, that is an ugly patch. But it appears tricky to come up with a better solution ... > LSP violation is catastrophic when undetected. [...] It is bad enough if detected after lengthy testing and debugging sessions. -- Stefan Lucks (moved to Bauhaus-University Weimar, Germany) ------ I love the taste of Cryptanalysis in the morning! ------ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 11:12 ` Stefan Lucks @ 2007-05-24 13:56 ` Dmitry A. Kazakov 2007-05-24 14:41 ` Stefan Lucks 2007-05-24 15:00 ` Georg Bauhaus 0 siblings, 2 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-24 13:56 UTC (permalink / raw) On Thu, 24 May 2007 13:12:56 +0200, Stefan Lucks wrote: > Dmitry A. Kazakov wrote: > >> [...] mere passing a variable as "in" does it as >> well in the sense that "in T" is not an LSP-subtype of T. > > You are using a very broad and generalised interpretation of the LSP. My > interpretation -- and I believe this is the common and usual one -- is > that "X: in T" in the parameterlist of a subprogram does not deal with > some "artificial" type "in T", just with "T". If it dealt with T, then the following program were legal: procedure Foo (X : in out T); procedure Bar (X : in T) is begin Foo (X): -- Fortunately illegal in Ada end Bar; > The "in" is part of the > subprogram's contract, not a part of X's contract. So there is no conflict > with LSP. But: 1. either the subprogram is a primitive operation then its contract is a part of the type contract => subject of LSP. 2. or it is not, and then substitutability does not apply (the type does not change) => absolutely substitutable anyway. > Perhaps you should follow the crowd and > narrow your interpretation as well? Yes, but then LSP should be re-formulated appropriately. And there is nothing automatically wrong in disallowing operations. I would like to see it in Ada. >> LSP violation becomes a problem when substitutability is indeterminable >> until run-time. In may cases we still choose to live with that. Constrained >> Ada subtypes is an example of. Another is multi-methods Foo (X, Y : T), >> when called on different children of T. In such cases Ada adds >> Constraint_Error to the interface of each subprogram and things become >> "substitutable" again. > > Yes, that is an ugly patch. But it appears tricky to come up with a better > solution ... Yes, this is a problem. In particular, when some property (like Constraint_Error propagation because of substitutability violation) becomes determinable in some, but all context. The language designer has no choice to make it illegal. The best thing he can do is to spill a warning. This is a way too little. There should be a way to have a finer grained classification of substitutability contexts than everywhere vs nowhere, such that the programmer could help the compiler by specifying the contexts where indeterminable substitutability were an error. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 13:56 ` Dmitry A. Kazakov @ 2007-05-24 14:41 ` Stefan Lucks 2007-05-24 15:46 ` Dmitry A. Kazakov 2007-05-24 15:00 ` Georg Bauhaus 1 sibling, 1 reply; 81+ messages in thread From: Stefan Lucks @ 2007-05-24 14:41 UTC (permalink / raw) On Thu, 24 May 2007, Dmitry A. Kazakov wrote: > On Thu, 24 May 2007 13:12:56 +0200, Stefan Lucks wrote: >> You are using a very broad and generalised interpretation of the LSP. My >> interpretation -- and I believe this is the common and usual one -- is >> that "X: in T" in the parameterlist of a subprogram does not deal with >> some "artificial" type "in T", just with "T". > > If it dealt with T, then the following program were legal: > > procedure Foo (X : in out T); > > procedure Bar (X : in T) is > begin > Foo (X): -- Fortunately illegal in Ada > end Bar; The "X: in T" part in Bar's contract means "I (Bar) will abstain from using certain properties X might have" (where "certain properties" are well defined, but I am too lazy to describe them explicitely). When calling "Foo(X)", Bar is trying to break this contract -- and fortunately, the compiler stops that attemtped fraud. Note that ther is a difference between "I will abstain from using" and "I don't provide", and the LSP is all about the second. The fact that inside Bar, the programmer is forced to obey the promise made in the parameter list is not in violation of the LSP. > Yes, but then LSP should be re-formulated appropriately. And there is > nothing automatically wrong in disallowing operations. I would like to see > it in Ada. Is there any language that allows that? I am dreaming of something like type Base is new Some_Tagged_Type with ...; procedure Primitive_1(...); procedure Primitive_2(...); type Super is new Base with out Primitive_2; -- this is not Ada! -- Super "inherits" Primitive_1 from Base, but not Primitive_2. B: Base; S: Super; procedure Class_Wide_Base (Object: Base'Class); procedure Class_Wide_Super(Object: Super'Class); Class_Wide_Base(B); -- legal, of course; Class_Wide_Super(S); -- also legal, of course; Class_Wide_Super(B); -- this should be legal!! Class_Wide_Base(S); -- this should be illegal! This would allow to "take away" operations without actually violating LSP (as I understand LSP). Perhaps the following would in also do the job (of course, I could not define S of type Super, but well ...): type Super is interface Base with out Primitive_2; -- Stefan Lucks (moved to Bauhaus-University Weimar, Germany) ------ I love the taste of Cryptanalysis in the morning! ------ ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 14:41 ` Stefan Lucks @ 2007-05-24 15:46 ` Dmitry A. Kazakov 0 siblings, 0 replies; 81+ messages in thread From: Dmitry A. Kazakov @ 2007-05-24 15:46 UTC (permalink / raw) On Thu, 24 May 2007 16:41:45 +0200, Stefan Lucks wrote: > On Thu, 24 May 2007, Dmitry A. Kazakov wrote: > >> On Thu, 24 May 2007 13:12:56 +0200, Stefan Lucks wrote: > >>> You are using a very broad and generalised interpretation of the LSP. My >>> interpretation -- and I believe this is the common and usual one -- is >>> that "X: in T" in the parameterlist of a subprogram does not deal with >>> some "artificial" type "in T", just with "T". >> >> If it dealt with T, then the following program were legal: >> >> procedure Foo (X : in out T); >> >> procedure Bar (X : in T) is >> begin >> Foo (X): -- Fortunately illegal in Ada >> end Bar; > > The "X: in T" part in Bar's contract means "I (Bar) will abstain from > using certain properties X might have" (where "certain properties" are > well defined, but I am too lazy to describe them explicitely). > > When calling "Foo(X)", Bar is trying to break this contract -- and > fortunately, the compiler stops that attemtped fraud. > > Note that ther is a difference between "I will abstain from using" and "I > don't provide", A contract is imposed on both sides. It would be ridiculous to abstain from anything without another party being informed, to get something in return. > and the LSP is all about the second. The fact that inside > Bar, the programmer is forced to obey the promise made in the parameter > list is not in violation of the LSP. It makes no sense to consider type contracts which are a subject of LSP and ones that don't. What is the type of an in T parameter? It leaks, you couldn't save it. >> Yes, but then LSP should be re-formulated appropriately. And there is >> nothing automatically wrong in disallowing operations. I would like to see >> it in Ada. > > Is there any language that allows that? I am dreaming of something like > > type Base is new Some_Tagged_Type with ...; > procedure Primitive_1(...); > procedure Primitive_2(...); > > type Super is new Base with out Primitive_2; -- this is not Ada! Some years ago I proposed the syntax: procedure Primitive_2 (...) is null; > -- Super "inherits" Primitive_1 from Base, but not Primitive_2. > > B: Base; > S: Super; > > procedure Class_Wide_Base (Object: Base'Class); > procedure Class_Wide_Super(Object: Super'Class); > > Class_Wide_Base(B); -- legal, of course; > Class_Wide_Super(S); -- also legal, of course; > Class_Wide_Super(B); -- this should be legal!! > Class_Wide_Base(S); -- this should be illegal! > > This would allow to "take away" operations without actually violating LSP > (as I understand LSP). Perhaps the following would in also do the job (of > course, I could not define S of type Super, but well ...): > > type Super is interface Base with out Primitive_2; Yep, looks like supertyping. Apart from disallowing operations one also need an ability to drop the implementation (representation). So that you could make Super having members you wished, or none. The latter is stripping interfaces from concrete types. Also if the language allowed abstract record members, then that again would become just disallowing: type X is record I : Integer; ... end record; type Y is new X with ...; function Y.I return Integer is null; procedure Y.I (I : Integer) is null; -- Down with it! -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: Ada Interfaces and the Liskov Substitution Principle 2007-05-24 13:56 ` Dmitry A. Kazakov 2007-05-24 14:41 ` Stefan Lucks @ 2007-05-24 15:00 ` Georg Bauhaus 1 sibling, 0 replies; 81+ messages in thread From: Georg Bauhaus @ 2007-05-24 15:00 UTC (permalink / raw) On Thu, 2007-05-24 at 16:41 +0200, Stefan Lucks wrote: > On Thu, 24 May 2007, Dmitry A. Kazakov wrote: > > Yes, but then LSP should be re-formulated appropriately. And there is > > nothing automatically wrong in disallowing operations. I would like to see > > it in Ada. > > Is there any language that allows that? I am dreaming of something like > > type Base is new Some_Tagged_Type with ...; > procedure Primitive_1(...); > procedure Primitive_2(...); Interesting. Not exactly the same, but http://www.lisp.org/HyperSpec/Body/sec_7-2.html I guess many of the dynamically typed languages have similar features. ^ permalink raw reply [flat|nested] 81+ messages in thread
end of thread, other threads:[~2007-07-01 1:00 UTC | newest] Thread overview: 81+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2007-05-23 19:47 Ada Interfaces and the Liskov Substitution Principle Stefan Lucks 2007-05-23 20:32 ` Ludovic Brenta 2007-05-23 22:00 ` Randy Brukardt 2007-05-24 0:56 ` Anh Vo 2007-05-24 18:27 ` Pascal Obry 2007-05-24 18:39 ` Dmitry A. Kazakov 2007-05-24 18:51 ` Pascal Obry 2007-05-24 22:44 ` Randy Brukardt 2007-05-24 6:57 ` Stefan Lucks 2007-05-23 20:54 ` Maciej Sobczak 2007-05-23 21:58 ` Randy Brukardt 2007-05-24 7:29 ` Maciej Sobczak 2007-05-24 8:02 ` Dmitry A. Kazakov 2007-05-24 12:58 ` Maciej Sobczak 2007-05-24 13:42 ` Dmitry A. Kazakov 2007-05-24 22:08 ` Robert A Duff 2007-07-01 1:00 ` David Thompson 2007-05-24 22:58 ` Randy Brukardt 2007-05-25 7:52 ` Maciej Sobczak 2007-05-25 8:21 ` Dmitry A. Kazakov 2007-05-25 20:27 ` Maciej Sobczak 2007-05-26 7:48 ` Dmitry A. Kazakov 2007-05-27 8:30 ` Maciej Sobczak 2007-05-27 10:04 ` Dmitry A. Kazakov 2007-05-29 8:03 ` Maciej Sobczak 2007-05-29 13:18 ` Dmitry A. Kazakov 2007-05-29 13:32 ` Dmitry A. Kazakov 2007-05-29 15:34 ` Maciej Sobczak 2007-05-29 17:07 ` Dmitry A. Kazakov 2007-05-30 7:40 ` Maciej Sobczak 2007-05-30 8:43 ` Dmitry A. Kazakov 2007-05-30 12:54 ` Maciej Sobczak 2007-05-30 13:56 ` Dmitry A. Kazakov 2007-05-30 16:49 ` vgodunko 2007-05-30 20:52 ` Maciej Sobczak 2007-05-31 8:15 ` Dmitry A. Kazakov 2007-05-31 13:46 ` Maciej Sobczak 2007-06-01 7:29 ` Dmitry A. Kazakov 2007-06-01 13:32 ` Maciej Sobczak 2007-06-01 14:53 ` Dmitry A. Kazakov 2007-06-01 20:31 ` Maciej Sobczak 2007-06-02 8:19 ` Dmitry A. Kazakov 2007-06-02 16:49 ` Maciej Sobczak 2007-06-03 7:09 ` Dmitry A. Kazakov 2007-06-03 22:04 ` Maciej Sobczak 2007-06-04 8:08 ` Dmitry A. Kazakov 2007-06-04 17:02 ` Maciej Sobczak 2007-06-05 8:35 ` Dmitry A. Kazakov 2007-06-05 22:12 ` Maciej Sobczak 2007-06-06 8:21 ` Dmitry A. Kazakov 2007-06-06 14:46 ` Maciej Sobczak 2007-06-06 15:11 ` Maciej Sobczak 2007-06-06 15:32 ` Markus E Leypold 2007-05-24 10:42 ` Georg Bauhaus 2007-05-24 13:41 ` Dmitry A. Kazakov 2007-05-25 16:59 ` Markus E Leypold 2007-05-28 9:52 ` Georg Bauhaus 2007-05-28 11:50 ` Dmitry A. Kazakov 2007-05-28 23:32 ` Georg Bauhaus 2007-05-29 12:05 ` Dmitry A. Kazakov 2007-05-29 13:33 ` Georg Bauhaus 2007-05-29 17:29 ` Dmitry A. Kazakov 2007-05-29 20:46 ` Georg Bauhaus 2007-05-30 7:53 ` Dmitry A. Kazakov 2007-05-30 13:18 ` Georg Bauhaus 2007-05-31 10:27 ` Dmitry A. Kazakov 2007-05-31 11:44 ` Georg Bauhaus 2007-06-01 7:37 ` Dmitry A. Kazakov 2007-06-01 10:07 ` Markus E Leypold 2007-06-01 11:41 ` Georg Bauhaus 2007-06-01 13:07 ` Dmitry A. Kazakov 2007-05-28 13:47 ` Markus E Leypold 2007-05-28 23:12 ` Georg Bauhaus 2007-05-28 13:56 ` Markus E Leypold 2007-05-28 23:00 ` Georg Bauhaus 2007-05-24 7:39 ` Dmitry A. Kazakov 2007-05-24 11:12 ` Stefan Lucks 2007-05-24 13:56 ` Dmitry A. Kazakov 2007-05-24 14:41 ` Stefan Lucks 2007-05-24 15:46 ` Dmitry A. Kazakov 2007-05-24 15:00 ` Georg Bauhaus
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox