|
From: Laurent Deniau on 27 Mar 2006 05:41 Dmitry A. Kazakov wrote: > Depends on what you mean under reference. When reference is an object (like > pointer), then it is not a view, but a distinct object of different type. > When reference means "by-reference", then it is an implementation detail. My experience has shown the opposite. I can even say that it is a key point of all designs whatever they aim to, including the language itself. For example, a language has either a by-value semantic (e.g. C, C++, Pascal, Fortran, Ada?, ...) or by-reference semantic (Java, SmallTalk, Python, all FPs, ...). I am not talking about simple things like pointers or C++ references. I am talking about the semantic of the link represented by the reference: lifetime, ownership, polymorphism and memory management. If I compare C++ (value) to Java (reference), one can observe that: - In Java, sharing objects is easy but dealing with small objects is under optimized and needs care (and good VM). - In C++, dealing with small objects is efficient but for big objects you need either optimized copy constructors (and assignment) or use shared object. But sharing objects is complex and under optimized and needs care. They are orthogonal and unfortunately, both are useful in real life. Now imagine that you want to organize your data into an hypertree with top-bottom *and* bottom-top optimized access while the user acts freely on the stored objects though another container (e.g. an Array). It is easy to implement with by-reference semantic and a nightmare with by-value semantic. This can be extended to any ordered container with multi-search criteria. These containers are what I call a "view". It is only a set of references to some objects that the user may not even be aware of. Doing a copy is a strong issue in such (and common) design. Replace the array above by database proxy, a network proxy, a process proxy and the mecanism is even more complex with the by-value semantic. >>It not that simple. What you describe is the visitor pattern known for >>at least a decade, nothing new then. Everybody knows the problems solved >>and inherent to this pattern. Multimethods are often seen as the >>solution to the problem of this pattern. And this pattern is often used >>to simulate multimethods when not supported. > > > So we could agree upon. That multimethod would solve this particular case > without mutating types? Yes. But only because the object Complex is small and transformation are somehow efficient. For example, in my project (magnetic field analysis) I am dealing with objects (e.g. FieldHarmonics) which are about 5K each and have two different major reprentations: Normalized (i.e. Polar) and UnNormalized (i.e. Rect). The user does not keep track of a state related to this representation since he even does not know when the representation switch from one to the other. This is driven by the actions and uses of the object while the behavior does not change. These objects are "viewed" (in the sens decribed above) by fews containers (including a persistant DB), where all and none of them are the owner of the data (asynchronous ownership). Doing this with a by-value semantic and creation-destruction of objects each time the representation changes would be a nightmare. Dynamic SI is very elegant here, because Harmonics is an ADT and PolarHarmonics and RectHarmonics derive from it. >>In dynamic typing languages, all instances are ADTs. The user only sees >>methods and knows nothing about the representation or even the type of >>the objects including classes (except if he queries for knowing it using >>the appropriate method). This is fully *behavior* oriented, the topic of >>my question... > > > Then I don't see why you insist on changing behavior of something that > consists only of the behavior. You just cannot do that, because there is no > way to mark the rest. Yes, there is references. Most of my object are non-transient and I think that is often the case in real life project: config, setting, remote link, etc... >>May I suggest a simple thing? Write a formal implementation of the >>algorithm described in the slides I cited and let see if it is closer to >>what is shown p5, p8 or p15. Then we will see what needs to be modified >>to add a new type of Shark, say a BigShark with its states Healthy and >>Injured. > > > Ah, it is difficult, because I am not convinced that MD is needed for > Encounter. The behavior of primitive animals does not vary that much. One > estimates another and then tries to attack, ignore or flee. Depending on > the intent of both, the result is an attack, hunt, ignoring, fight, > dispersing or fleeing. So this is class-wide, i.e. all descendants of Fish > share the behavior in Encounter: > > type Estimation is (Weaker, Same, Stronger); > > type Fish is ...; > function Estimate (Self : X; Other : Y) return Estimation; > procedure Attack (Predator, Prey : in out Fish); > procedure Hunt (Predator, Prey : in out Fish); > procedure Ignore (One, Another : in out Fish); > procedure Fight (One, Another : in out Fish); > procedure Disperse (One, Another : in out Fish); > procedure Flee (Standing, Running : in out Fish); > > procedure Encounter (X, Y : in out Fish'Class) is > begin > case Estimate (X, Y) is > when Weaker => > case Estimate (Y, X) is > when Weaker => Disperse (Y, X); > when Same => Flee (Y, X); > when Stronger => Hunt (Y, X); > end case; > when Same => > case Estimate (Y, X) is > when Weaker => Flee (X, Y); > when Same => Ignore (X, Y); > when Stronger => Attack (Y, X); > end case; > when Stronger => > case Estimate (Y, X) is > when Weaker => Hunt (X, Y); > when Same => Attack (X, Y); > when Stronger => Fight (X, Y); > end case; > end case; > end Encounter; This is exactly was I wanted to show and to avoid. Your implementation follow the design of the slide p5, a single method with 9 tests (3^3). Even while you changed the policy to something smalled and better classified (only 3 states!) because you know in advance all the cases. What about extending Encounter to Fish vs Boat and introduce ambiguous or overlapping estimation? Your design will start to explode, a very common problem in large projects... It has been shown that predicate dispatch is a generalization of multimethod which itself is a generalization of single-method (see the paper of Ernst, Kaplan and Chambers "Predicate Dispatching: A unified theory of dispatch", ECOOP'98). And dynamic inheritence and predicate dispatch are equivalent concepts (and sometimes the latter is implemented in term of the former). a+, ld.
From: Dmitry A. Kazakov on 27 Mar 2006 09:51 On Mon, 27 Mar 2006 12:41:44 +0200, Laurent Deniau wrote: > Dmitry A. Kazakov wrote: >> Depends on what you mean under reference. When reference is an object (like >> pointer), then it is not a view, but a distinct object of different type. >> When reference means "by-reference", then it is an implementation detail. > > My experience has shown the opposite. I can even say that it is a key > point of all designs whatever they aim to, including the language > itself. For example, a language has either a by-value semantic (e.g. C, > C++, Pascal, Fortran, Ada?, ...) or by-reference semantic (Java, > SmallTalk, Python, all FPs, ...). I am not talking about simple things > like pointers or C++ references. I am talking about the semantic of the > link represented by the reference: lifetime, ownership, polymorphism and > memory management. > > If I compare C++ (value) to Java (reference), one can observe that: > - In Java, sharing objects is easy but dealing with small objects is > under optimized and needs care (and good VM). > - In C++, dealing with small objects is efficient but for big objects > you need either optimized copy constructors (and assignment) or use > shared object. But sharing objects is complex and under optimized and > needs care. > > They are orthogonal and unfortunately, both are useful in real life. Yes, but you are mixing here semantics and implementation. Semantics is identity. Identity can be implemented using reference semantics. The reverse is untrue. I can have things without identity passed by-reference. Technically I can also have things with identity passed by-value. This happens when you marshal objects across the network or use DB replicas synchronized later on. So it is independent. Nothing prevents a language to get advantages of both. Polymorphic objects that hold types identity need not to be always passed by reference. It is a misconception. > Now imagine that you want to organize your data into an hypertree with > top-bottom *and* bottom-top optimized access while the user acts freely > on the stored objects though another container (e.g. an Array). It is > easy to implement with by-reference semantic and a nightmare with > by-value semantic. This can be extended to any ordered container with > multi-search criteria. > > These containers are what I call a "view". You cannot have containers of objects having value identity. You can have ones of handles to the objects with identity. These handles will have a type different from what they point to. The type might look as a subtype and be fully transparent to the operations of the target, with exception of identity, of course. One can make one step further and say that there were no original objects at all. This causes a lot of misconceptions about identity and references. So it is better to call things by their names. >>>It not that simple. What you describe is the visitor pattern known for >>>at least a decade, nothing new then. Everybody knows the problems solved >>>and inherent to this pattern. Multimethods are often seen as the >>>solution to the problem of this pattern. And this pattern is often used >>>to simulate multimethods when not supported. >> >> So we could agree upon. That multimethod would solve this particular case >> without mutating types? > > Yes. But only because the object Complex is small and transformation are > somehow efficient. For example, in my project (magnetic field analysis) > I am dealing with objects (e.g. FieldHarmonics) which are about 5K each > and have two different major reprentations: Normalized (i.e. Polar) and > UnNormalized (i.e. Rect). The user does not keep track of a state > related to this representation since he even does not know when the > representation switch from one to the other. This is driven by the > actions and uses of the object while the behavior does not change. These > objects are "viewed" (in the sens decribed above) by fews containers > (including a persistant DB), where all and none of them are the owner of > the data (asynchronous ownership). Doing this with a by-value semantic > and creation-destruction of objects each time the representation changes > would be a nightmare. No, you just create a proxy object to the existing one. This does not change the type. Basically you either change the object's internal representation or not. If change it, you have to construct a new object keeping its old semantics. 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? But in most cases this just does not work. You cannot switch between, say a binary tree and a hash table. They are sufficiently different. You can provide a common interface though. >>>May I suggest a simple thing? Write a formal implementation of the >>>algorithm described in the slides I cited and let see if it is closer to >>>what is shown p5, p8 or p15. Then we will see what needs to be modified >>>to add a new type of Shark, say a BigShark with its states Healthy and >>>Injured. >> >> Ah, it is difficult, because I am not convinced that MD is needed for >> Encounter. The behavior of primitive animals does not vary that much. One >> estimates another and then tries to attack, ignore or flee. Depending on >> the intent of both, the result is an attack, hunt, ignoring, fight, >> dispersing or fleeing. So this is class-wide, i.e. all descendants of Fish >> share the behavior in Encounter: >> >> type Estimation is (Weaker, Same, Stronger); >> >> type Fish is ...; >> function Estimate (Self : X; Other : Y) return Estimation; >> procedure Attack (Predator, Prey : in out Fish); >> procedure Hunt (Predator, Prey : in out Fish); >> procedure Ignore (One, Another : in out Fish); >> procedure Fight (One, Another : in out Fish); >> procedure Disperse (One, Another : in out Fish); >> procedure Flee (Standing, Running : in out Fish); >> >> procedure Encounter (X, Y : in out Fish'Class) is >> begin >> case Estimate (X, Y) is >> when Weaker => >> case Estimate (Y, X) is >> when Weaker => Disperse (Y, X); >> when Same => Flee (Y, X); >> when Stronger => Hunt (Y, X); >> end case; >> when Same => >> case Estimate (Y, X) is >> when Weaker => Flee (X, Y); >> when Same => Ignore (X, Y); >> when Stronger => Attack (Y, X); >> end case; >> when Stronger => >> case Estimate (Y, X) is >> when Weaker => Hunt (X, Y); >> when Same => Attack (X, Y); >> when Stronger => Fight (X, Y); >> end case; >> end case; >> end Encounter; > > This is exactly was I wanted to show and to avoid. Your implementation > follow the design of the slide p5, a single method with 9 tests (3^3). > Even while you changed the policy to something smalled and better > classified (only 3 states!) because you know in advance all the cases. > What about extending Encounter to Fish vs Boat and introduce ambiguous > or overlapping estimation? Your design will start to explode, a very > common problem in large projects... But the problem is not in the number of variants. The problem is in the mapping State -> Reaction. The point is that it has very little to do with types, because the pattern does not change across different species. Fishes are too primitive to do something else. It will not explode. Further, ambiguities are resolved not in the way you describe. Maybe, your example is just unfortunate, but if you ever have caught a fish, you should know how it swings between "snap the bait" and "flee." This is modeled using fuzzy relationships, linguistic variables and defuzzification in the decision making point. Such models deal with inherently ambiguous and incomplete data, because this is how all higher living organisms exist. This cannot be reduced by types. Consider it purely formal: there are that many number of variants, as a matter of fact. To reduce them you have to find a symmetry (if that exists.) Now tell me, why the symmetry induced by your types system should be the one pursued by fishes? Can you verify, prove it? Then if you can, why, that symmetry cannot be implemented in the code above? Note also that at the level of values (rather than types), there is far more powerful apparatus in our possession. I can make Estimation a real number and that will technically exclude any types system to match. You just cannot emulate algebra of real numbers with types, as long as the number of targets for dispatch stays discrete. (:-)) Going up for types we sufficiently loose in power. There must a very good reason for doing that. Don't get me wrong, I am not against dispatch. But you have to apply it (and types) only if the pattern sufficiently changes. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de
From: Laurent Deniau on 27 Mar 2006 11:21 Dmitry A. Kazakov wrote: > On Mon, 27 Mar 2006 12:41:44 +0200, Laurent Deniau wrote: > > >>Dmitry A. Kazakov wrote: >> >>If I compare C++ (value) to Java (reference), one can observe that: >>- In Java, sharing objects is easy but dealing with small objects is >>under optimized and needs care (and good VM). >>- In C++, dealing with small objects is efficient but for big objects >>you need either optimized copy constructors (and assignment) or use >>shared object. But sharing objects is complex and under optimized and >>needs care. >> >>They are orthogonal and unfortunately, both are useful in real life. > > > Yes, but you are mixing here semantics and implementation. 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. > Semantics is identity. What do you mean by identity? > Nothing prevents a language to get advantages of both. > Polymorphic objects > that hold types identity Polymorphic objects always hold types identity (except in some local context where optimization allows the object to never exists as such). > need not to be always passed by reference. It is a > misconception. True. But why is it not applied to real languages? It is not a misconception, it is an observation. By-value can easily "emulate" by-reference semantic (e.g. relationnal database) and by-reference can easily "emulate" by-value (e.g. haskell) up to some efficiency. No doubt about that. >>Now imagine that you want to organize your data into an hypertree with >>top-bottom *and* bottom-top optimized access while the user acts freely >>on the stored objects though another container (e.g. an Array). It is >>easy to implement with by-reference semantic and a nightmare with >>by-value semantic. This can be extended to any ordered container with >>multi-search criteria. >> >>These containers are what I call a "view". > > > You cannot have containers of objects having value identity. The C++ Standard Library has only such containers... >> Doing this with a by-value semantic >>and creation-destruction of objects each time the representation changes >>would be a nightmare. > > > No, you just create a proxy object to the existing one. This does not > change the type. Then you have one more object (and class) to write. Most of my comments goes into the direction of less code, less complexity and better flexibility. I don't care that I could do most of the work using design patterns (one more again here) if another language let me do it in a single line. Otherwise I could also replace overriding by switch cases and call it a design pattern... > Basically you either change the object's internal > representation or not. If change it, you have to construct a new object > keeping its old semantics. > 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. > But in most cases this just does not work. You cannot > switch between, say a binary tree and a hash table. Because the difference is not related to a change of state. Containers are objects where polymorphism matters less. It matters essentially because it allows to use them as objects in other containers (e.g. List of List). But by nature, containers represent structural organisation of data and polymorphic structures are not very used. Some such container exists and are useful but they are called differently and considered as another type of container with a richer interface. Nothing prevent to have an ordered hash table switching between a tree and a hash representation. Imagine that you could do it in O(1), why would you not do it internally? It is not done because it is not efficient and efficiency is the Graal that (unexperienced) programmers never stop to seek after. >>>>May I suggest a simple thing? Write a formal implementation of the >>>>algorithm described in the slides I cited and let see if it is closer to >>>>what is shown p5, p8 or p15. Then we will see what needs to be modified >>>>to add a new type of Shark, say a BigShark with its states Healthy and >>>>Injured. >>> >>>Ah, it is difficult, because I am not convinced that MD is needed for >>>Encounter. The behavior of primitive animals does not vary that much. One >>>estimates another and then tries to attack, ignore or flee. Depending on >>>the intent of both, the result is an attack, hunt, ignoring, fight, >>>dispersing or fleeing. So this is class-wide, i.e. all descendants of Fish >>>share the behavior in Encounter: >>> >>>type Estimation is (Weaker, Same, Stronger); >>> >>>type Fish is ...; >>>function Estimate (Self : X; Other : Y) return Estimation; >>>procedure Attack (Predator, Prey : in out Fish); >>>procedure Hunt (Predator, Prey : in out Fish); >>>procedure Ignore (One, Another : in out Fish); >>>procedure Fight (One, Another : in out Fish); >>>procedure Disperse (One, Another : in out Fish); >>>procedure Flee (Standing, Running : in out Fish); >>> >>>procedure Encounter (X, Y : in out Fish'Class) is >>>begin >>> case Estimate (X, Y) is >>> when Weaker => >>> case Estimate (Y, X) is >>> when Weaker => Disperse (Y, X); >>> when Same => Flee (Y, X); >>> when Stronger => Hunt (Y, X); >>> end case; >>> when Same => >>> case Estimate (Y, X) is >>> when Weaker => Flee (X, Y); >>> when Same => Ignore (X, Y); >>> when Stronger => Attack (Y, X); >>> end case; >>> when Stronger => >>> case Estimate (Y, X) is >>> when Weaker => Hunt (X, Y); >>> when Same => Attack (X, Y); >>> when Stronger => Fight (X, Y); >>> end case; >>> end case; >>>end Encounter; >> >>This is exactly was I wanted to show and to avoid. Your implementation >>follow the design of the slide p5, a single method with 9 tests (3^3). >>Even while you changed the policy to something smalled and better >>classified (only 3 states!) because you know in advance all the cases. >>What about extending Encounter to Fish vs Boat and introduce ambiguous >>or overlapping estimation? Your design will start to explode, a very >>common problem in large projects... > > > But the problem is not in the number of variants. The problem is in the > mapping State -> Reaction. The point is that it has very little to do with > types, because the pattern does not change across different species. Fishes > are too primitive to do something else. Therefore you consider Dolphins to be stupid and not able to call friends to fight together some Sharks? If your model remains primitive, the result will be primitive too... This is why software must evolve, to make us more cleaver ;-) > It will not explode. Further, > ambiguities are resolved not in the way you describe. Haskell has in its standard a *type* called Maybe to handle ambiguities... > This cannot be reduced by types. Consider it purely formal: there are that > many number of variants, as a matter of fact. To reduce them you have to > find a symmetry (if that exists.) Why to you want a symmetry? Encounter(A,B) is different from Encounter(B,A). And in some cases you can use a type projection (through inheritance) to get default behavior mapped on many types. > Note also that at the level of values (rather than types), there is far > more powerful apparatus in our possession. I can make Estimation a real > number and that will technically exclude any types system to match. You > just cannot emulate algebra of real numbers with types I do not say this for the same reason that I do not say to put some dynamic inheritance in all classes. But when one wants to change a state which induces a change of behavior then DI is useful. Most programs use a lot of states (behaviors) and entity (objects), both are discretes and countable and DI is a valuable tools to help their organization and interaction. I do not say more. If I also have static typing and other nice paradigms, I will not complain for sure. > , as long as the > number of targets for dispatch stays discrete. (:-)) Predicate dispatch handles logical combination of values to select the dispatched multimethod (e.g. Encounter(A,B) when A.age > B.age or A.age > 10). This is where DI and predicate dispatch differ (yet a good compiler should be able to go from one representation to the other as I said). > Going up for types we > sufficiently loose in power. There must a very good reason for doing that. > > Don't get me wrong, I am not against dispatch. But you have to apply it > (and types) only if the pattern sufficiently changes. I agree on this but for the moment I have to make a choice and from my point of view, the flexibility of multimethod dispatch (and the next step predicate dispatch) are more valuable than static typing in my projects and I noted that because it offers new possibilities, the way I program is also changing (like when I started to look at FLs). a+, ld.
From: Dmitry A. Kazakov on 27 Mar 2006 14:34 On Mon, 27 Mar 2006 18:21:14 +0200, Laurent Deniau wrote: > Dmitry A. Kazakov wrote: >> On Mon, 27 Mar 2006 12:41:44 +0200, Laurent Deniau wrote: >> >>>Dmitry A. Kazakov wrote: >>> >>>If I compare C++ (value) to Java (reference), one can observe that: >>>- In Java, sharing objects is easy but dealing with small objects is >>>under optimized and needs care (and good VM). >>>- In C++, dealing with small objects is efficient but for big objects >>>you need either optimized copy constructors (and assignment) or use >>>shared object. But sharing objects is complex and under optimized and >>>needs care. >>> >>>They are orthogonal and unfortunately, both are useful in real life. >> >> Yes, but you are mixing here semantics and implementation. > > 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. It is free if I don't restrict it, for example by declaring that the thing is not copyable. >> Semantics is identity. > > What do you mean by identity? Something that identifies the object, if we are talking about value identity. Usually it is the memory address, but it could be something else. For example, an incomputable thing, like true random generator, or an end of a pipe etc. >> Nothing prevents a language to get advantages of both. > > > Polymorphic objects >> that hold types identity > > Polymorphic objects always hold types identity (except in some local > context where optimization allows the object to never exists as such). .... without any exception. Whatever optimization are made, they are indivisible for the observer. I must admit my wording was sloppy. >> need not to be always passed by reference. It is a >> misconception. > > True. But why is it not applied to real languages? It is not a > misconception, it is an observation. OK, let's say, it is an observation of a common misconception. (:-)) Nothing in polymorphic objects imply by-reference semantics. > By-value can easily "emulate" > by-reference semantic (e.g. relationnal database) and by-reference can > easily "emulate" by-value (e.g. haskell) up to some efficiency. No doubt > about that. So we are in agreement. >> No, you just create a proxy object to the existing one. This does not >> change the type. > > Then you have one more object (and class) to write. In a good language it must happen almost automatically similarly as it is for pointers. That's a question of what operations the types algebra have to support. Creating safe referential types with user-definable semantics must be there. > Most of my comments > goes into the direction of less code, less complexity and better > flexibility. I don't care that I could do most of the work using design > patterns (one more again here) if another language let me do it in a > single line. Otherwise I could also replace overriding by switch cases > and call it a design pattern... Agree. >> Basically you either change the object's internal >> representation or not. If change it, you have to construct a new object >> keeping its old semantics. > >> 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. How could different or same implementation simplify or not the design of client code? It is already decoupled! >> But in most cases this just does not work. You cannot >> switch between, say a binary tree and a hash table. > > Because the difference is not related to a change of state. Containers > are objects where polymorphism matters less. It matters essentially > because it allows to use them as objects in other containers (e.g. List > of List). But by nature, containers represent structural organisation of > data and polymorphic structures are not very used. Some such container > exists and are useful but they are called differently and considered as > another type of container with a richer interface. Nothing prevent to > have an ordered hash table switching between a tree and a hash > representation. Imagine that you could do it in O(1), why would you not > do it internally? I would, but that would be a new object. That's the whole point. You say there is no new object, I object that there is always one. But note, I don't object, rather insist that the language should provide easy-to-use mechanisms to make such conversions transparent. I much enjoy C++ concept of types conversions. It is a pity that it wasn't driven to its logical end, allowing different representations of subtypes and so ad-hoc supertypes. Rather than pursuing restrictive and *wrong* idea that a subtype should always contain its base. (BTW, this aberration stems from the same misconception of universal applicability of referential semantics!) >> But the problem is not in the number of variants. The problem is in the >> mapping State -> Reaction. The point is that it has very little to do with >> types, because the pattern does not change across different species. Fishes >> are too primitive to do something else. > > 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! (:-)) > If your model remains primitive, > the result will be primitive too... This is why software must evolve, to > make us more cleaver ;-) BTW, biological organisms don't override as they evolve. It seems that they rather extend, lay over and over new levels above old behavioral patterns. In each of us lives an old Silurian fish... >> This cannot be reduced by types. Consider it purely formal: there are that >> many number of variants, as a matter of fact. To reduce them you have to >> find a symmetry (if that exists.) > > Why to you want a symmetry? Encounter(A,B) is different from > Encounter(B,A). And in some cases you can use a type projection (through > inheritance) to get default behavior mapped on many types. I meant symmetry in a wider sense. If we consider the graph of types building the class of parameters in Encounter, it will give us an immense (infinite) number of variants. But this graph has most likely rich symmetries along and across its branches and subgraphs. These symmetries are determined solely by the application domain, so the language should adapt here and allow to factor out common things. >> Note also that at the level of values (rather than types), there is far >> more powerful apparatus in our possession. I can make Estimation a real >> number and that will technically exclude any types system to match. You >> just cannot emulate algebra of real numbers with types > > I do not say this for the same reason that I do not say to put some > dynamic inheritance in all classes. But when one wants to change a state > which induces a change of behavior then DI is useful. Most programs use > a lot of states (behaviors) and entity (objects), both are discretes and > countable and DI is a valuable tools to help their organization and > interaction. I do not say more. If I also have static typing and other > nice paradigms, I will not complain for sure. I see. But my hope is that the same flexibility could become achievable in the model I've described. >> , as long as the >> number of targets for dispatch stays discrete. (:-)) > > Predicate dispatch handles logical combination of values to select the > dispatched multimethod (e.g. Encounter(A,B) when A.age > B.age or A.age > > 10). This is where DI and predicate dispatch differ (yet a good > compiler should be able to go from one representation to the other as I > said). I see. In my terms, what you want is ad-hoc subtypes created from constraints like A.age > x, or even A.age > B.age. I'm not sure that it is a good idea. I think that the margin between types should be more visible. The arguments against are: 1. Static checkability. In particular, It is paramount IMO to be statically sure that dispatch never fails. It is vital to detect as much as possible substitutability problems. (We are talking about large scale design.) 2. Efficiency. I wish to be able to bring anything under one roof of type tag before I start to dispatch. 3. I feel that constraints don't belong to dispatch. I cannot elaborate it more without further thinking about it, so it is just a feeling, so far. > I agree on this but for the moment I have to make a choice and from my > point of view, the flexibility of multimethod dispatch (and the next > step predicate dispatch) are more valuable than static typing in my > projects and I noted that because it offers new possibilities, the way I > program is also changing (like when I started to look at FLs). In my view the real challenge is to have both MD and static types. I strongly believe that it should be possible. You (and other proponents of dynamic typing) give up to easy! (:-)) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de
From: Laurent Deniau on 28 Mar 2006 03:43
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. > 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. 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. >>>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. 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). > How could different or same implementation > simplify or not the design of client code? It is already decoupled! True for this case, therefore here, only references to the object matter. >>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... > 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. > 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! > You (and other proponents of > dynamic typing) give up to easy! (:-)) Well, we were waiting for you ;-) 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. a+, ld. |