|
From: Dmitry A. Kazakov on 28 Mar 2006 05:34 On Tue, 28 Mar 2006 10:43:10 +0200, Laurent Deniau wrote: > Dmitry A. Kazakov wrote: >> On Mon, 27 Mar 2006 18:21:14 +0200, Laurent Deniau wrote: >>>If you do not understand (at least part of) the underlying >>>implementation of a language (not of a given implementation of the >>>language) then your programming level will be limited. >> >> Not at all. In most cases I don't care about the compiler's choice. > > Compiler choice is "a given" implementation of the language. The one I > don't care too. But the language should leave some space for the choice. By-reference vs. by-value should remain open. >> OK, let's say, it is an observation of a common misconception. (:-)) >> Nothing in polymorphic objects imply by-reference semantics. > > As soon as you agree that by-reference semantics is more than just a > pointer to the object. Yes, but it is called "identity", if it is a part of behavior and program semantics. By-reference is rather a way of parameter passing, i.e. a part of implementation. > On the other hand, passing objects by-address is > the only way to deal with their polymorphism nature, whatever the > semantic is by-value or by-reference. No, this is wrong. Whatever identity is required for polymorphism, it need not to be bound to memory address. Don't you send polymorphic objects over the network? Don't you store them in files? >>>>You don't do that if you indeed can go on with >>>>the old object. Though, then, you have also answer a design question, what >>>>a hell have all these different representation seek here, if you can use >>>>them interchangeable? >>> >>>Because it simplifies a lot the design and the extendability of a projet >>>like in the example shown in the Cecil documentation previously mentionned. >> >> Hmm, it sounds illogical. If A has the same interface as B >> (=interchangeable), then I cannot distinguish implementations of A from >> ones of B in my client code. > > This is one possibility. But A and B could be interchangeable also if > they do not have the same interface. This would mean an untyped language. I don't want it. The intent (to use A and B interchangeable) has to be expressed in the program. It must be done, not by an occasional use of an A where a B is expected, but in some formal way. Otherwise, neither the compiler, nor the program reader would be able to decide whether it was a bug or designer's intent. This is a fundamental disagreement. > And changing types may change > interface. This is one of the useful thing (i.e. IOStream simply changes > between OutStream <-> InStream interface since the object is the same). See above. I want wall sockets designed in a way that phase (InStream) could not be plugged into the ground (OutStream). >>>Therefore you consider Dolphins to be stupid and not able to call >>>friends to fight together some Sharks? >> >> Dolphins are mammals! Even if my biology is quite rusted, you hadn't caught >> me on that! (:-)) > > Yes (it was where I wanted to go, that is having a new type close to the > Fish behavior, but more cleaver), so create a class mammals, derive > dolphin, and create another class boat and let see what Encounter becomes... No problem, because Fishes and Mammals should have a common ancestor (Vertebrata?) Encounter was in Fish'Class it wasn't in Vertebrata'Class. That was perhaps a design fault, which has to be analyzed and corrected rather than just patched. The compiler in a properly typed language will immediately dismiss your attempt to call Encounter on Dolphins. This is *good*! If you put Encounter into Vertebrata'Class, I wouldn't propose a class-wide interface for it, because MD would be a better choice there. Note also that your solution will not work for Vertebrata either, because their behavior isn't covered by simple Eat/Flee dichotomy. Between eating and fleeing they also read and write to USENET news groups... (:-)) >> I see. But my hope is that the same flexibility could become achievable in >> the model I've described. > > Let me remind the context of my question, I am developping a framework > in C, not a compiler nor a translator. So for sure I will not be able to > do that. Maybe. However, I think, I have said it before, there seems to be a limited number of feasible ways to get a types system right in presence of MD and MI. Perhaps, there is only one, or even none. And you can't say - I'm not going to build an aircraft, I just want to attach wings to my bicycle. OK, technically you can, it is a free land... >> In my view the real challenge is to have both MD and static types. I >> strongly believe that it should be possible. > > It is, Cecil has it up to some extend. See the manual of Cecil to see > the limits of static type checks when combined with MD. C. Chambers and > M. Ernst did an incredible work on this topic! I'm not convinced that there should be any limits. At least it should be shown that these limits are indeed required. >> You (and other proponents of >> dynamic typing) give up to easy! (:-)) > > Well, we were waiting for you ;-) Yep, there is always somebody to spoil the party! (:-)) > Flexibility of dynamic typing is rather nice, and the experience has > shown that very large scale projects can be written in dynamically typed > langage with no more bugs than in other languages but with a > productivity increased a factor 2 or 3. Despite that I have been > convince during 15 years that static typing is essential, recent large > scale projects in Python, PHP, Perl, Lisp combined with agile > development techniques teached me that it is maybe not so much > essential. The other point they teached me (with Java too) is that > simplicity matters more than speed. Well, software developing is in the phase of handicraft. So goes the attitude towards it, expressed in statements as above. It should really happen some large-scale disasters caused by software, before it become engineering. People are talking about productivity of different sorts of shovels facing problems requiring earth-moving machinery. Agile shoveling, well, well... (:-)) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de
From: Laurent Deniau on 28 Mar 2006 06:48 Dmitry A. Kazakov wrote: > On Tue, 28 Mar 2006 10:43:10 +0200, Laurent Deniau wrote: > But the language should leave some space for the choice. By-reference vs. > by-value should remain open. Very hard since it influence strongly the efficiency and the design of the language itself. >>On the other hand, passing objects by-address is >>the only way to deal with their polymorphism nature, whatever the >>semantic is by-value or by-reference. > > > No, this is wrong. Whatever identity is required for polymorphism, it need > not to be bound to memory address. Don't you send polymorphic objects over > the network? Don't you store them in files? No, I send and store value of object (states) of (maybe) polymorphic type. The value is not polymorphic. Pratically, (subtyping) polymorphism means reinterpret differently what is stored at this address, the only way to say to a computer to change its interpretation of a value without changing the value. Values cannot be reinterpreted directly, they must be converted (coercion) but this is ad-hoc polymorphism. >>>>>You don't do that if you indeed can go on with >>>>>the old object. Though, then, you have also answer a design question, what >>>>>a hell have all these different representation seek here, if you can use >>>>>them interchangeable? >>>> >>>>Because it simplifies a lot the design and the extendability of a projet >>>>like in the example shown in the Cecil documentation previously mentionned. >>> >>> >>>Hmm, it sounds illogical. If A has the same interface as B >>>(=interchangeable), then I cannot distinguish implementations of A from >>>ones of B in my client code. >> >>This is one possibility. But A and B could be interchangeable also if >>they do not have the same interface. > > > This would mean an untyped language. No, it can be achieved in a statically typed language, see gbeta and the work of Ernst or Cecil and the work of C. Chambers. > See above. I want wall sockets designed in a way that phase (InStream) > could not be plugged into the ground (OutStream). Ok, so no IOStream then. I am not in favor of IOStream since I prefer to separate the two ;-) >>>>Therefore you consider Dolphins to be stupid and not able to call >>>>friends to fight together some Sharks? >>> >>>Dolphins are mammals! Even if my biology is quite rusted, you hadn't caught >>>me on that! (:-)) >> >>Yes (it was where I wanted to go, that is having a new type close to the >>Fish behavior, but more cleaver), so create a class mammals, derive >>dolphin, and create another class boat and let see what Encounter becomes... > > > No problem, because Fishes and Mammals should have a common ancestor > (Vertebrata?) Encounter was in Fish'Class it wasn't in Vertebrata'Class. > That was perhaps a design fault, which has to be analyzed and corrected > rather than just patched. Just do it and see how your hierarchy becomes an unmaintainable mammut. > The compiler in a properly typed language will > immediately dismiss your attempt to call Encounter on Dolphins. This is > *good*! Sure, as well as the first run of the tests. > If you put Encounter into Vertebrata'Class, I wouldn't propose a > class-wide interface for it, because MD would be a better choice there. > Note also that your solution will not work for Vertebrata either, because > their behavior isn't covered by simple Eat/Flee dichotomy. Between eating > and fleeing they also read and write to USENET news groups... (:-)) You maybe forgot that methods are attached to generics and not to classes in MD, no "interface" needed then and no base class update. Then I can add what I need only when I need it, and most of the times, the tests tell me what is missing. It does not influence the hierarchy itself, only the behavior and that is nice since it is the observable part. What is concerned by the hierarchy is sharing behavior, that is where to put such default or specific behavior in the method specialization graph. This is fully behavior oriented, not type oriented. You sould read again the slides, assuming that type checking is there (and it can be in Cecil or in CLisp) and you will see that predicate dispatch is simply the "next step". a+, ld.
From: Dmitry A. Kazakov on 29 Mar 2006 03:31 On Tue, 28 Mar 2006 13:48:05 +0200, Laurent Deniau wrote: > Dmitry A. Kazakov wrote: >> On Tue, 28 Mar 2006 10:43:10 +0200, Laurent Deniau wrote: >> But the language should leave some space for the choice. By-reference vs. >> by-value should remain open. > > Very hard since it influence strongly the efficiency and the design of > the language itself. I don't see why. Ada successfully does this. Small objects are passed by-copy even if they are records or arrays. The compiler decides it at will. Note that the language explicitly states that a program exploiting differences in by-copy and by-reference (aliasing) is erroneous. >>>On the other hand, passing objects by-address is >>>the only way to deal with their polymorphism nature, whatever the >>>semantic is by-value or by-reference. >> >> No, this is wrong. Whatever identity is required for polymorphism, it need >> not to be bound to memory address. Don't you send polymorphic objects over >> the network? Don't you store them in files? > > No, I send and store value of object (states) of (maybe) polymorphic > type. The value is not polymorphic. Of course it is. The value of a polymorphic type is itself polymorphic. There is nothing magical behind values and types. > Pratically, (subtyping) polymorphism > means reinterpret differently what is stored at this address, the only > way to say to a computer to change its interpretation of a value without > changing the value. This is inaccurate. You don't reinterpret anything. It is just so that the same memory location may contain objects of different types at different stages of program execution. If you don't know at compile time some type, this does not imply that you reinterpret anything anew. As for polymorphic objects, the type of a polymorphic object is *statically* known. It is the class of. When (and if) dispatch converts a polymorphic object to a specific one, that does not reinterpret, it simply extracts the specific object from the polymorphic one. It is no different from accessing a record member. No magic, no type mutators. >>>This is one possibility. But A and B could be interchangeable also if >>>they do not have the same interface. >> >> This would mean an untyped language. > > No, it can be achieved in a statically typed language, see gbeta and the > work of Ernst or Cecil and the work of C. Chambers. Yes, there could be statically untyped languages. (:-)) [ I don't know what definition of "typed" you are using, but I use one that tells: if A and B are unrelated types, then there is no legal way to mix them. The reverse is also true. Once you managed to mix A and B, in a typed language, that would automatically mean existence of a type relation. The only carrier of this interchangeability (others would say substitutability) is types conversion. It might be an identity operation, address shift, copy-in/copy-out pair, that is an irrelevant implementation detail. There exists nothing beyond that. It is a quite simple model. I cannot understand why people are keep on hanging on muddy: one object - many types. ] >>>>>Therefore you consider Dolphins to be stupid and not able to call >>>>>friends to fight together some Sharks? >>>> >>>>Dolphins are mammals! Even if my biology is quite rusted, you hadn't caught >>>>me on that! (:-)) >>> >>>Yes (it was where I wanted to go, that is having a new type close to the >>>Fish behavior, but more cleaver), so create a class mammals, derive >>>dolphin, and create another class boat and let see what Encounter becomes... >> >> No problem, because Fishes and Mammals should have a common ancestor >> (Vertebrata?) Encounter was in Fish'Class it wasn't in Vertebrata'Class. >> That was perhaps a design fault, which has to be analyzed and corrected >> rather than just patched. > > Just do it and see how your hierarchy becomes an unmaintainable mammut. As I said, it is a bad example, for many reasons. So it probably [dis]proves nothing, but bad design. Species aren't types. Their relations are poorly modeled by types systems. Species aren't crisp. Even if they were, the behavioral pattern of a living organism is not determined by its place in taxonomy. There are carnivores, omnivores, herbivores, etc. They have different sizes. They posses different organs. They have different social behavior. They live in various habitats. That imprints the behavioral pattern of an individual organism much stronger than descent. Biological polymorphism and convergent evolution are obvious examples of that it does not work that way. You should find a better example to illustrate your point. Take a hierarchy of numerals. Start from ordinals, proceed to fields, then go to matrices. Add slices and submatrices. Add physical dimensions to each leaf of the hierarchy. Along the way ensure optimal memory and performance. Add the axis of intervals (interval arithmetic.) Continue to the fuzzy case based on intervals. [ I know no language capable to do this. ] >> If you put Encounter into Vertebrata'Class, I wouldn't propose a >> class-wide interface for it, because MD would be a better choice there. >> Note also that your solution will not work for Vertebrata either, because >> their behavior isn't covered by simple Eat/Flee dichotomy. Between eating >> and fleeing they also read and write to USENET news groups... (:-)) > > You maybe forgot that methods are attached to generics and not to > classes in MD, no "interface" needed then and no base class update. OK, it was sloppiness on my side. Let me reformulate it more accurately: Encounter is attached to a tuple of types. There were four design decisions we considered: 1. Vertebrata x Vertebrata (polymorphic on Vertebrata) 2. Vertebrata'Class x Vertebrata'Class (class-wide on Vertebrata) 3. Fish x Fish (polymorphic on Fish) 4. Fish'Class x Fish'Class (class-wide on Fish) You took 3. I said that 4 were better if Fish were considered. Then you switched to 1 (or 2), by adding Dolphins. I answered that 1 would be *then* better than 2. > Then > I can add what I need only when I need it, and most of the times, the > tests tell me what is missing. In presence of an exponentially growing number of variants? Come on. Tests are especially useless here. This is an obvious case for statical correctness checks. The key requirement for MD is in my view: dispatch shall never fail in a legal program. > It does not influence the hierarchy > itself, only the behavior and that is nice since it is the observable > part. What is concerned by the hierarchy is sharing behavior, that is > where to put such default or specific behavior in the method > specialization graph. This is fully behavior oriented, not type oriented. You didn't say how and where behavior is shared. Reuse is nice but it must be correct and controllable. Which rules determine propagation of sharing along the types hierarchy? By the way that was your question initiated the thread. So it seems that your model does not answer it. In my view the answer is - there is no answer. There is no right way to define all variants. One can diminish the number of variants using substitutability constraint, but it will remain exponential. So it is no matter how dispatching works. It might ask the Delphic Sibyl, I don't care. What matters is how to instruct the compiler to make dispatching working according to the application domain. I don't want to do it on the basis of concrete types. I want to do it for the whole class. That was the point about the symmetry. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de
From: Laurent Deniau on 3 Apr 2006 09:10 Dmitry A. Kazakov wrote: > On Tue, 28 Mar 2006 13:48:05 +0200, Laurent Deniau wrote: >>Dmitry A. Kazakov wrote: >>>But the language should leave some space for the choice. By-reference vs. >>>by-value should remain open. >>Very hard since it influence strongly the efficiency and the design of >>the language itself. > I don't see why. Ada successfully does this. Small objects are passed > by-copy even if they are records or arrays. The compiler decides it at > will. Note that the language explicitly states that a program exploiting > differences in by-copy and by-reference (aliasing) is erroneous. You seems to not understand what I am talking about (or don't want to?). > Of course it is. The value of a polymorphic type is itself polymorphic. Could you give me an example of polymorphic value? > As for polymorphic objects, the type of a polymorphic object is > *statically* known. Not always (e.g. downcast). May Ada raise type exception like Java or C++? If yes then you should think why, since you think that types are always statically known... > Take a hierarchy of numerals. Start from ordinals, proceed to fields, then > go to matrices. Add slices and submatrices. Add physical dimensions to each > leaf of the hierarchy. Along the way ensure optimal memory and performance. > Add the axis of intervals (interval arithmetic.) Continue to the fuzzy case > based on intervals. [ I know no language capable to do this. ] C++ is able to do that with no needs for a hierachy. What you are descibing here requires parametric polymorphism (C++ template) and some metaprogramming. Not my concern here. >>Then >>I can add what I need only when I need it, and most of the times, the >>tests tell me what is missing. > > In presence of an exponentially growing number of variants? Come on. Tests > are especially useless here. This is an obvious case for statical > correctness checks. The key requirement for MD is in my view: dispatch > shall never fail in a legal program. What about very large scale programs (extremly complex) written in dynamically typed languages (see article "Haskell vs. Erlang, Reloaded")? Static typing is one tool amongst others to help the programmer to detect errors at compile time and to help the compiler to make optimizations. But it also often requires to write larger and more complex code (to statisfy the type system constraints). It is far from being the answer to all problems. >>It does not influence the hierarchy >>itself, only the behavior and that is nice since it is the observable >>part. What is concerned by the hierarchy is sharing behavior, that is >>where to put such default or specific behavior in the method >>specialization graph. This is fully behavior oriented, not type oriented. > > You didn't say how and where behavior is shared. Reuse is nice but it must > be correct and controllable. Which rules determine propagation of sharing > along the types hierarchy? By the way that was your question initiated the > thread. So it seems that your model does not answer it. No, it is just that there is some place for choices like in any language design. I wanted to share experiences on this topic, not to discuss about pro and cons of MD and dynamic typing which is not my concern and also because such discussions usually continue for ever. For my projects, I am using C, C++ and (starting to use) Haskell as main stream languages. C++ has a type system stronger than C and Haskell is strongly typed. But my best productivity is in C where most of the errors are detected during runtime tests. a+, ld.
From: Dmitry A. Kazakov on 3 Apr 2006 11:45
On Mon, 03 Apr 2006 15:10:57 +0200, Laurent Deniau wrote: > Dmitry A. Kazakov wrote: >> Of course it is. The value of a polymorphic type is itself polymorphic. > > Could you give me an example of polymorphic value? class A {...}; class B : public A {...}; A * X = new B (); X points to a polymorphic object (of which value is also polymorphic.) It is very simply to check - X->Foo () will dispatch. >> As for polymorphic objects, the type of a polymorphic object is >> *statically* known. > > Not always (e.g. downcast). No. See the example above. The type is "a pointer to a descendant of A". It is statically known. [ You don't know which descendant, this makes the object polymorphic.] When you downcast to say "a pointer to a descendant of B" that type is also statically known, it is specified as T in dynamic_cast<T*>. So the type of a polymorphic object is always known, otherwise (remember, it is a behavioral notion of ADT) you wouldn't be able to do anything with it. No type - no methods, no methods - no access. > May Ada raise type exception like Java or C++? (Exceptions are values in Ada) > If yes then you should think why, since you think that types are > always statically known... The type of a *polymorphic* object, which does not imply that the specific type is known. When you write: catch (Exception& Error) {...} you *statically* know the class. I denote it as Exception'Class. This type is known statically. Unknown is the specific type which might be IOFault derived from Exception. >> Take a hierarchy of numerals. Start from ordinals, proceed to fields, then >> go to matrices. Add slices and submatrices. Add physical dimensions to each >> leaf of the hierarchy. Along the way ensure optimal memory and performance. >> Add the axis of intervals (interval arithmetic.) Continue to the fuzzy case >> based on intervals. [ I know no language capable to do this. ] > > C++ is able to do that with no needs for a hierachy. What you are > descibing here requires parametric polymorphism (C++ template) and some > metaprogramming. Not my concern here. But mine. Dynamic (true) polymorphism is to replace parametric (fake) one. As for hierarchy, it is always there, though might be implicit. >> The key requirement for MD is in my view: dispatch >> shall never fail in a legal program. > > What about very large scale programs (extremly complex) written in > dynamically typed languages (see article "Haskell vs. Erlang, > Reloaded")? Static typing is one tool amongst others to help the > programmer to detect errors at compile time and to help the compiler to > make optimizations. But it also often requires to write larger and more > complex code (to statisfy the type system constraints). It is far from > being the answer to all problems. Sorry, but I don't see how a run-time failure to dispatch might help here. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de |