* Operation can be dispatching in only one type @ 2009-11-13 20:12 xorque 2009-11-13 20:34 ` Dmitry A. Kazakov 2009-11-16 17:43 ` Adam Beneschan 0 siblings, 2 replies; 132+ messages in thread From: xorque @ 2009-11-13 20:12 UTC (permalink / raw) Hello. I'm trying to write an archive/directory abstraction in a similar vein to PhysicsFS but am at a bit of a loss as to how to design the archiver interface: with Path; package Archiver is type Archiver_t is abstract tagged limited private; type Archiver_Class_Access_t is access Archiver_t'Class; procedure Init (Archiver : out Archiver_t) is abstract; function Can_Mount (Archiver : in Archiver_t; Path : in Path.Real_t) return Boolean is abstract; type File_t is abstract tagged limited private; type File_Class_Access_t is access File_t'Class; procedure Open (Archiver : in Archiver_t; Path : in Path.Virtual_t; File : out File_t) is abstract; procedure Close (File : in out File_t) is abstract; private type Archiver_t is abstract tagged limited null record; type File_t is abstract tagged limited null record; end Archiver; The idea of the above is that the main part of the library only deals with archivers and "files" (which might only really be pointers to entries in Zip files, for example) by 'Class. The problem with the above is that: archiver.ads:18:13: operation can be dispatching in only one type Hopefully someone here knows a better way to handle this. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-13 20:12 Operation can be dispatching in only one type xorque @ 2009-11-13 20:34 ` Dmitry A. Kazakov 2009-11-13 20:43 ` xorque 2009-11-13 20:44 ` xorque 2009-11-16 17:43 ` Adam Beneschan 1 sibling, 2 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-13 20:34 UTC (permalink / raw) On Fri, 13 Nov 2009 12:12:18 -0800 (PST), xorque wrote: > I'm trying to write an archive/directory abstraction in a similar vein > to PhysicsFS > but am at a bit of a loss as to how to design the archiver interface: > > with Path; > > package Archiver is > > type Archiver_t is abstract tagged limited private; > type Archiver_Class_Access_t is access Archiver_t'Class; > > procedure Init > (Archiver : out Archiver_t) is abstract; > > function Can_Mount > (Archiver : in Archiver_t; > Path : in Path.Real_t) return Boolean is abstract; > > type File_t is abstract tagged limited private; > type File_Class_Access_t is access File_t'Class; > > procedure Open > (Archiver : in Archiver_t; > Path : in Path.Virtual_t; > File : out File_t) is abstract; > > procedure Close > (File : in out File_t) is abstract; > > private > > type Archiver_t is abstract tagged limited null record; > > type File_t is abstract tagged limited null record; > > end Archiver; > > The idea of the above is that the main part of the library only deals with > archivers and "files" (which might only really be pointers to entries in Zip > files, for example) by 'Class. > > The problem with the above is that: > > archiver.ads:18:13: operation can be dispatching in only one type > > Hopefully someone here knows a better way to handle this. Ada does not support multiple dispatch of this form. For simplicity consider it does not support MD at all. According to your code Open is doubly dispatching in Archiver_t and File_t arguments. That does not work (unfortunately). You have to choose one and make another class-wide. E.g. procedure Open (Archiver : Archiver_t'Class; -- Class-wide Path : Virtual_t; File : in out File_t) is abstract; This would be a primitive operation of File_t only. P.S. If you indeed have to do MD, you should emulate it manually, by dispatching within Open to some primitive operation of Archiver_t. P.P.S. Do not use access types unless you need them. P.P.P.S. Replace tagged limited with Ada.Finalization.Limited_Controlled, sooner or later you almost certainly will need finalization and initialization. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-13 20:34 ` Dmitry A. Kazakov @ 2009-11-13 20:43 ` xorque 2009-11-13 21:14 ` Dmitry A. Kazakov 2009-11-13 20:44 ` xorque 1 sibling, 1 reply; 132+ messages in thread From: xorque @ 2009-11-13 20:43 UTC (permalink / raw) Thanks for the response. It may be that I don't want to design the interface like the above at all. The only real requirement is that: Archivers use a common protocol: Code just sees operations on Archiver_t'Class and doesn't know if it's reading from a Zip, RAR, directory, etc. Presumably, then, when I open a file using an operation from an archiver, I can't know about the implementation of the actual file as it's private to the archiver. Hence, I've ended up with two private tagged types. Is there a better way (I've not ignored your other suggestions, I'm just exploring other possibilities first)? ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-13 20:43 ` xorque @ 2009-11-13 21:14 ` Dmitry A. Kazakov 0 siblings, 0 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-13 21:14 UTC (permalink / raw) On Fri, 13 Nov 2009 12:43:42 -0800 (PST), xorque wrote: > The only real requirement is that: > > Archivers use a common protocol: Code just sees operations > on Archiver_t'Class and doesn't know if it's reading from a Zip, RAR, > directory, etc. > > Presumably, then, when I open a file using an operation from an > archiver, I can't know about the implementation of the actual file > as it's private to the archiver. > > Hence, I've ended up with two private tagged types. Is there a better > way (I've not ignored your other suggestions, I'm just exploring other > possibilities first)? MD is customary replaced my mix-in: 1. Standard mix-in: type File_Type is abstract new Ada.Finalization.Limited_Controlled with null record; procedure Open ( File : in out File_Type; Path : String ) is abstract; procedure Close (File : in out File_Type) is abstract; procedure Read ...; procedure Write ...; Files know nothing about archives. Archive has a file as a mixin: type Archiver_Type (Transport : not null access File_Type'Class) is new Ada.Finalization.Limited_Controlled with null record; procedure Open (Archiver : in out Archiver_Type; Path : String); Open of Archive_Type calls to Open of Archive.Transport. Same with other operations. They all are defined in terms of the operations of File_Type. Class through Archive.Transport are dispatching, because it is class-wide. 2. Generic mix-in. You can put Archive in the hierarchy of File_Type. The implementation of Archive_Type can be generic taking a descendant of File_Type and then extending it in the generic body. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-13 20:34 ` Dmitry A. Kazakov 2009-11-13 20:43 ` xorque @ 2009-11-13 20:44 ` xorque 1 sibling, 0 replies; 132+ messages in thread From: xorque @ 2009-11-13 20:44 UTC (permalink / raw) On Nov 13, 8:34 pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: .> > P.P.P.S. Replace tagged limited with Ada.Finalization.Limited_Controlled, > sooner or later you almost certainly will need finalization and > initialization. Agreed. It was always intended, just not implemented there for simplicity's sake. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-13 20:12 Operation can be dispatching in only one type xorque 2009-11-13 20:34 ` Dmitry A. Kazakov @ 2009-11-16 17:43 ` Adam Beneschan 2009-11-16 20:28 ` Dmitry A. Kazakov 1 sibling, 1 reply; 132+ messages in thread From: Adam Beneschan @ 2009-11-16 17:43 UTC (permalink / raw) On Nov 13, 12:12 pm, xorque <xorquew...@googlemail.com> wrote: > Hello. > > I'm trying to write an archive/directory abstraction in a similar vein > to PhysicsFS > but am at a bit of a loss as to how to design the archiver interface: > > with Path; > > package Archiver is > > type Archiver_t is abstract tagged limited private; > type Archiver_Class_Access_t is access Archiver_t'Class; > > procedure Init > (Archiver : out Archiver_t) is abstract; > > function Can_Mount > (Archiver : in Archiver_t; > Path : in Path.Real_t) return Boolean is abstract; > > type File_t is abstract tagged limited private; > type File_Class_Access_t is access File_t'Class; > > procedure Open > (Archiver : in Archiver_t; > Path : in Path.Virtual_t; > File : out File_t) is abstract; > > procedure Close > (File : in out File_t) is abstract; > > private > > type Archiver_t is abstract tagged limited null record; > > type File_t is abstract tagged limited null record; > > end Archiver; > > The idea of the above is that the main part of the library only deals > with > archivers and "files" (which might only really be pointers to entries > in Zip > files, for example) by 'Class. > > The problem with the above is that: > > archiver.ads:18:13: operation can be dispatching in only one type > > Hopefully someone here knows a better way to handle this. I don't know what PhysicsFS is, so I'm not clear on what you are trying to accomplish. Maybe your plan is to define concrete types derived from Archive_T and File_T that go together, in pairs. For example, a programmer might define one package Archive_1 that defines Archive_1_T and File_1_T, and another user might write a package Archive_2 that defines Archive_2_T and File_2_T, but the intent is that the two would be dependent---i.e. you would always use Open with two objects that go together, such as an object of type Archive_1_T with a File_1_T, or an Archive_2_T with a File_2_T, but never with an Archive_1_T and a File_2_T. In that case, you would make one parameter classwide, as Dmitry suggested: procedure Open (Archiver : Archiver_t'Class; -- Class-wide Path : Virtual_t; File : in out File_t) is abstract; Then an implementation would need to check manually to make sure the type is correct: procedure Open (Archive : Archiver_T'Class; Path : Virtual_T; File : in out File_1_T) is begin if Archive not in Archive_1_T then raise <some exception> with "Open on File_1_T used with wrong archiver class"; end if; declare The_Arch : Archive_1_T renames Archive_1_T(Archive); begin ... ... and now you have an Archive_1_T and a File_1_T to work with. (I haven't checked this to make sure it's correct. But it would be something like this.) By the way, for a while I was considering requesting a language change to allow this sort of restricted multiple dispatch, in cases where a primitive operation of two types could be declared and inherited for two derived types that are derived in the same package, and when dispatching, the program would check to make sure the two actual types really were declared in the same package. This would avoid having to deal with M x N combinations of types---something that I think is an issue with MI---since it only deals with pairs (or triplets, etc.) of types that are defined to "go together". I could try to write a proposal if there's enough interest, but I think it's too late to get into the next language revision. If your plan is that Archiver and File types be more independent, though, what you may need to do is to pick one of the parameters to Open, say Archive (as above) to be a class-wide type, and then provide enough operations on Archiver_T so that an implementation of Open could dispatch on Archive to do whatever needs to be done with the archive. If you have some other idea about the relationship between Archiver and File, then you might need to give me more specifics about how you intend your package to be used. -- Adam ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-16 17:43 ` Adam Beneschan @ 2009-11-16 20:28 ` Dmitry A. Kazakov 2009-11-16 20:32 ` Dmitry A. Kazakov 2009-11-16 21:35 ` Adam Beneschan 0 siblings, 2 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-16 20:28 UTC (permalink / raw) On Mon, 16 Nov 2009 09:43:06 -0800 (PST), Adam Beneschan wrote: > By the way, for a while I was considering requesting a language change > to allow this sort of restricted multiple dispatch, in cases where a > primitive operation of two types could be declared and inherited for > two derived types that are derived in the same package, and when > dispatching, the program would check to make sure the two actual types > really were declared in the same package. This would avoid having to > deal with M x N combinations of types---something that I think is an > issue with MI---since it only deals with pairs (or triplets, etc.) of > types that are defined to "go together". But you still have these combinations independently on where you derive. You allow the "diagonal" of the full dispatching table. I.e. the combinations: A1, B1, A2, B2, A3, B3 But other combinations are still there, because they can be spelt. It is just so that they raise Constraint_Error. This "diagonal" behavior is what we already have for multi-methods (a form of MD with only one hierarchy A=B). I suppose this is one of the motivations of your proposal. But the problem with "diagonal" dispatch is that it is inconsistent with the idea of static typing. I would insist on the design rule that dispatch shall never fail in a legal program. I.e. the compiler shall enforce all possible combinations of types no later than at compile time. Further, it shall not "invent" broken implementations. In "diagonal" dispatch it would do (and does) exactly this, it would override A1, B2 with an implementation raising Constraint_Error. At the same time the case represented by "diagonal" dispatch is very important in other situations, like parallel hierarchies of types. E.g. when we wanted to force a new instance from B'Class for each new instance from A'Class. But again that enforcement shall be static. BTW, it is not multiple dispatch. Technically this is rather single dispatch where the tuple of types (A, B) is considered as root of some class of tuples, in which (A1, B1), (A2, B2) are instances. Presently we have nothing in the language to handle this (except the jack-of-all-trades, generics). It would be interesting to speculate how tuples can be supported in Ada, especially their flattening in the arguments lists, and using tuples as multiple results of a function. > I could try to write a > proposal if there's enough interest, but I think it's too late to get > into the next language revision. I am incredibly interested in MD (and in tuples as well), but I think we should not touch it until we knew how to do it right. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-16 20:28 ` Dmitry A. Kazakov @ 2009-11-16 20:32 ` Dmitry A. Kazakov 2009-11-16 21:35 ` Adam Beneschan 1 sibling, 0 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-16 20:32 UTC (permalink / raw) On Mon, 16 Nov 2009 21:28:00 +0100, Dmitry A. Kazakov wrote: > At the same time the case represented by "diagonal" dispatch is very > important in other situations, like parallel hierarchies of types. E.g. > when we wanted to force a new instance from B'Class for each new instance > from A'Class. But again that enforcement shall be static. BTW, it is not > multiple dispatch. Technically this is rather single dispatch where the > tuple of types (A, B) is considered as root of some class of tuples, in > which (A1, B1), (A2, B2) are instances. Presently we have nothing in the > language to handle this (except the jack-of-all-trades, generics). It would > be interesting to speculate how tuples can be supported in Ada, especially > their flattening in the arguments lists, and using tuples as multiple > results of a function. I forgot to say, that in this case illegal combinations like A1, B2 are statically illegal, because there would be no tuple (A1, B2) declared. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-16 20:28 ` Dmitry A. Kazakov 2009-11-16 20:32 ` Dmitry A. Kazakov @ 2009-11-16 21:35 ` Adam Beneschan 2009-11-16 22:28 ` Dmitry A. Kazakov 1 sibling, 1 reply; 132+ messages in thread From: Adam Beneschan @ 2009-11-16 21:35 UTC (permalink / raw) On Nov 16, 12:28 pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > On Mon, 16 Nov 2009 09:43:06 -0800 (PST), Adam Beneschan wrote: > > By the way, for a while I was considering requesting a language change > > to allow this sort of restricted multiple dispatch, in cases where a > > primitive operation of two types could be declared and inherited for > > two derived types that are derived in the same package, and when > > dispatching, the program would check to make sure the two actual types > > really were declared in the same package. This would avoid having to > > deal with M x N combinations of types---something that I think is an > > issue with MI---since it only deals with pairs (or triplets, etc.) of > > types that are defined to "go together". > > But you still have these combinations independently on where you derive. > You allow the "diagonal" of the full dispatching table. I.e. the > combinations: > > A1, B1, > A2, B2, > A3, B3 > > But other combinations are still there, because they can be spelt. It is > just so that they raise Constraint_Error. This "diagonal" behavior is what > we already have for multi-methods (a form of MD with only one hierarchy > A=B). I suppose this is one of the motivations of your proposal. > > But the problem with "diagonal" dispatch is that it is inconsistent with > the idea of static typing. I would insist on the design rule that dispatch > shall never fail in a legal program. I.e. the compiler shall enforce all > possible combinations of types no later than at compile time. Since a call to a dispatching operation may have parameters that are themselves of class-wide types, I don't think this is possible. In Ada as it exists currently, you can declare a primitive operation with two parameters of the same tagged type (maybe that's what you meant by "multi-method"---sorry, I don't know the jargon). E.g. type T is abstract tagged null record; procedure Some_Operation (Param_1 : T; Param_2 : T); If you later call Some_Operation (X, Y); and X is declared to be of type T1 (a descendant of T) and Y is declared to be of type T2 (also a descendant of T), then the compiler can statically determine that this will fail. But if either X or Y (or both) is declared to be of type T'Class, then the compiler can't statically tell whether the dispatch will fail or not. So a runtime check is needed. Similarly for the proposal I was thinking of making. > Further, it > shall not "invent" broken implementations. In "diagonal" dispatch it would > do (and does) exactly this, it would override A1, B2 with an implementation > raising Constraint_Error. > > At the same time the case represented by "diagonal" dispatch is very > important in other situations, like parallel hierarchies of types. E.g. > when we wanted to force a new instance from B'Class for each new instance > from A'Class. But again that enforcement shall be static. BTW, it is not > multiple dispatch. Technically this is rather single dispatch where the > tuple of types (A, B) is considered as root of some class of tuples, in > which (A1, B1), (A2, B2) are instances. Presently we have nothing in the > language to handle this (except the jack-of-all-trades, generics). It would > be interesting to speculate how tuples can be supported in Ada, especially > their flattening in the arguments lists, and using tuples as multiple > results of a function. > > > I could try to write a > > proposal if there's enough interest, but I think it's too late to get > > into the next language revision. > > I am incredibly interested in MD (and in tuples as well), but I think we > should not touch it until we knew how to do it right. I don't think the proposal I was considering was a solution to MD, but rather to a smallish subset of MD-related problems---although it was a subset I thought would be useful, and I may have run into a case in my own code where I would have wanted to use it. -- Adam ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-16 21:35 ` Adam Beneschan @ 2009-11-16 22:28 ` Dmitry A. Kazakov 2009-11-17 22:10 ` Adam Beneschan 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-16 22:28 UTC (permalink / raw) On Mon, 16 Nov 2009 13:35:00 -0800 (PST), Adam Beneschan wrote: > On Nov 16, 12:28�pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: >> On Mon, 16 Nov 2009 09:43:06 -0800 (PST), Adam Beneschan wrote: >>> By the way, for a while I was considering requesting a language change >>> to allow this sort of restricted multiple dispatch, in cases where a >>> primitive operation of two types could be declared and inherited for >>> two derived types that are derived in the same package, and when >>> dispatching, the program would check to make sure the two actual types >>> really were declared in the same package. �This would avoid having to >>> deal with M x N combinations of types---something that I think is an >>> issue with MI---since it only deals with pairs (or triplets, etc.) of >>> types that are defined to "go together". >> >> But you still have these combinations independently on where you derive. >> You allow the "diagonal" of the full dispatching table. I.e. the >> combinations: >> >> A1, B1, >> A2, B2, >> A3, B3 >> >> But other combinations are still there, because they can be spelt. It is >> just so that they raise Constraint_Error. This "diagonal" behavior is what >> we already have for multi-methods (a form of MD with only one hierarchy >> A=B). I suppose this is one of the motivations of your proposal. >> >> But the problem with "diagonal" dispatch is that it is inconsistent with >> the idea of static typing. I would insist on the design rule that dispatch >> shall never fail in a legal program. I.e. the compiler shall enforce all >> possible combinations of types no later than at compile time. > > Since a call to a dispatching operation may have parameters that are > themselves of class-wide types, I don't think this is possible. No, what I have in mind is that each time a new type is derived, we could somehow ensure that the whole row M+1 (from N x M+1) is filled either by a overriding or per a reasonable inheritance. The major problem is that we cannot see all Ns when we do M+1. The idea of deriving both types in one package looks like an attempt to control the places where we can expand the table N x M. That is not enough to enforce completeness of the table in the above sense. There should be something more strong, but also more liberal in terms of modularization. Obviously to derive from all types related via a shared primitive operation in one package is awful (and sometimes just impossible within the given parent-child hierarchy of packages). > In Ada as it exists currently, you can declare a primitive operation > with two parameters of the same tagged type (maybe that's what you > meant by "multi-method"---sorry, I don't know the jargon). E.g. > > type T is abstract tagged null record; > procedure Some_Operation (Param_1 : T; Param_2 : T); > > If you later call > > Some_Operation (X, Y); > > and X is declared to be of type T1 (a descendant of T) and Y is > declared to be of type T2 (also a descendant of T), then the compiler > can statically determine that this will fail. But if either X or Y > (or both) is declared to be of type T'Class, then the compiler can't > statically tell whether the dispatch will fail or not. So a runtime > check is needed. Similarly for the proposal I was thinking of making. Yep, this is what I meant. The compiler should require to override *all* combinations: type T2 is new T1 with ...; overriding procedure Some_Operation (X : T2; Y : T2); -- Error! overriding procedure Some_Operation (X : T2; Y : T2); overriding procedure Some_Operation (X : T1; Y : T2); overriding procedure Some_Operation (X : T2; Y : T1); -- OK Yes, this is tedious, but the programmer should know what we does when he declares a multi-method. This is the semantics of - there are n**2 combinations which do potentially different things. If there are only n combinations, that is another case, which should *not* look MD, because it is not. I think it should somehow use tuples instead. The error should happen upon tuple construction, not in the call. For the reader it should be clear that there is only one tag. I have no idea how to spell that syntactically or by other means. Al for "true" multi-methods I think we should support delegation in order to control combinatorial explosion. For example: 1. declaration Some_Operation commutative; 2. explicit delegation of T2 x T1 to T1 x T2 overriding procedure Some_Operation (X : T2; Y : T1) renames Some_Operation with (X => Y, Y => X); 3. delegation of T1 x T2 to T2 x T2 supplying a downcast conversion from T1 to T2 overriding procedure Some_Operation (X : T1; Y : T2) renames Some_Operation with (X => (X with ...), Y => Y); 4. delegation of T2 x T2 to T1 x T2 (upcast) overriding procedure Some_Operation (X : T2; Y : T2) renames Some_Operation with (X => T1 (X), Y => Y); etc. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-16 22:28 ` Dmitry A. Kazakov @ 2009-11-17 22:10 ` Adam Beneschan 2009-11-18 9:46 ` Dmitry A. Kazakov 2009-11-19 2:23 ` tmoran 0 siblings, 2 replies; 132+ messages in thread From: Adam Beneschan @ 2009-11-17 22:10 UTC (permalink / raw) On Nov 16, 2:28 pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > No, what I have in mind is that each time a new type is derived, we could > somehow ensure that the whole row M+1 (from N x M+1) is filled either by a > overriding or per a reasonable inheritance. The major problem is that we > cannot see all Ns when we do M+1. > > The idea of deriving both types in one package looks like an attempt to > control the places where we can expand the table N x M. That is not enough > to enforce completeness of the table in the above sense. > > There should be something more strong, but also more liberal in terms of > modularization. Obviously to derive from all types related via a shared > primitive operation in one package is awful (and sometimes just impossible > within the given parent-child hierarchy of packages). .... > Yep, this is what I meant. The compiler should require to override *all* > combinations: > > type T2 is new T1 with ...; > overriding > procedure Some_Operation (X : T2; Y : T2); > -- Error! > > overriding > procedure Some_Operation (X : T2; Y : T2); > overriding > procedure Some_Operation (X : T1; Y : T2); > overriding > procedure Some_Operation (X : T2; Y : T1); > -- OK > > Yes, this is tedious, but the programmer should know what we does when he > declares a multi-method. This is the semantics of - there are n**2 > combinations which do potentially different things. If there are only n > combinations, that is another case, which should *not* look MD, because it > is not. I think it should somehow use tuples instead. The error should > happen upon tuple construction, not in the call. For the reader it should > be clear that there is only one tag. I have no idea how to spell that > syntactically or by other means. It appears to me that we're talking at cross-purposes. I was trying to solve one particular class of problems; you seem to be trying to solve the entire Multiple Dispatch problem. I believe that when I first thought about making this proposal, I was assuming that a fully general multiple dispatch wasn't ever going to be part of the language, so I was thinking about an improvement for one particular case I had run into. Here's an example of the sort of problem I'm thinking about: Suppose you want to define an abstract type that represents a document, which could be a plain ASCII text file, a Word document, a PDF file, etc., that all have in common that they consist of sequences of "characters" and "other elements of some sort". Some of the documents may also have formatting information or other good stuff, but these won't be common to all documents. You want to write a package that deals with documents and particularly with the text (characters) in the documents. The type of document (plain text, Word, etc.) won't be known until run time. Some of the operations you want to provide are: search for given text in the document, and return "markers" that point to the beginning and end of the text that is found; and move text from one point to another in the document. A natural way to do this, it seems, would be to define a Document as an abstract type, and let other packages (Plaintext_Documents, Word_Documents, PDF_Documents, etc.) define concrete types as extensions of that type for plain text, Word documents, PDF documents, and so on. You also want to define, say, a Text_Region type that represents a region of text in a document, and a Marker type that represents a particular point in the document text, between two characters. The Text_Region and Marker types should be abstract also, since we don't know how they'd be implemented for different document types; for a plain text document, a Marker is probably just a string index, in essence, but the Word_Documents and PDF_Documents might unpack a file into some complex internal data structure when it reads it, so a Marker for those document types might contain pointers into the internal data structure, or something. So it seems natural to write the package like this (along with other operations): package Documents is type Document is abstract tagged private; type Text_Region is abstract tagged private; type Marker is abstract tagged private; function Search_For_Text (Doc : Document; Text : String) return Text_Region is abstract; -- I'm assuming here that it would return some sort of -- "no region" value if the text is not found function Beginning_Of (Reg : Text_Region) return Marker is abstract; function End_Of (Reg : Text_Region) return Marker is abstract; procedure Move_Text (Doc : in out Document; From : in Text_Region; To : in Marker) is abstract; end Documents; The last would cut the text represented by the From region and move it to the point specified by "To". (It would be up to the packages to figure out how to move formatting information or other information in an appropriate way.) Of course, Move_Text can't be declared because it is a primitive operation of more than one tagged type. Actually, none of the functions can be declared, either, but I want to focus on the Move_Text procedure. You might want to call Move_Text with a Word_Documents.Document, Word_Documents.Text_Region, and Word_Documents.Marker, or similarly with three objects all with types from the PDF_Documents or Plaintext_Documents package, but you would never want to mix objects from two different packages. And that was the purpose of the proposal I was thinking of making: to allow for restricted use of multiple dispatch for cases like this (and this is a situation I believe I've run into more than once), in which Move_Text could be declared but could only be called if the parameters' specific types all come from the same package, checked at runtime if necessary. The Move_Text procedure would be inherited in each of the other packages (Plaintext_Documents, etc.), with types from that package, but there would not be any inherited Move_Text that would involve types from different packages. (This was an idea I first thought about a long time ago, but eventually abandoned; I really hadn't thought about how to deal with the functions.) There are workarounds, of course. As I responded to the original poster in this thread, you can make some of the parameters to Move_Text class-wide, and the concrete procedures would check to make sure the actual tags for the class-wide parameters are correct---or just doing a type conversion will do the check: package body Word_Documents is overriding procedure Move_Text (Doc : in out Document; From : in Documents.Text_Region'Class; To : in Documents.Marker'Class) is X_From : Text_Region renames Text_Region(From); X_To : Marker renames Marker(To); ... I think that's correct. Anyway, those will raise Constraint_Error if the parameters are from the wrong package. However, I consider that to be a workaround (maybe others don't think it's that bad), which is why I was thinking about making this proposal---to make things work more "naturally", without a workaround, in this sort of situation that I've run into multiple times. I'm not sure what your idea is trying to solve (other than "making multiple dispatch perfect"); but even if it does make general multiple dispatch available, your way of doing things would not solve anything in this sort of case, because you would require Move_Text to be overridden for every possible combination of children of the types, which makes it unusable and means that even with your Grand Solution To Everything, those of us who want to implement something like the above would have to go back to using the workaround. That's why I think we're not on the same page---we're trying to solve two different and unrelated problems that seem only superficially related. Maybe we can't solve them both. -- Adam ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-17 22:10 ` Adam Beneschan @ 2009-11-18 9:46 ` Dmitry A. Kazakov 2009-11-18 16:39 ` Adam Beneschan 2009-11-19 2:23 ` tmoran 1 sibling, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-18 9:46 UTC (permalink / raw) On Tue, 17 Nov 2009 14:10:54 -0800 (PST), Adam Beneschan wrote: > On Nov 16, 2:28�pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >> No, what I have in mind is that each time a new type is derived, we could >> somehow ensure that the whole row M+1 (from N x M+1) is filled either by a >> overriding or per a reasonable inheritance. The major problem is that we >> cannot see all Ns when we do M+1. >> >> The idea of deriving both types in one package looks like an attempt to >> control the places where we can expand the table N x M. That is not enough >> to enforce completeness of the table in the above sense. >> >> There should be something more strong, but also more liberal in terms of >> modularization. Obviously to derive from all types related via a shared >> primitive operation in one package is awful (and sometimes just impossible >> within the given parent-child hierarchy of packages). > > .... > >> Yep, this is what I meant. The compiler should require to override *all* >> combinations: >> >> � �type T2 is new T1 with ...; >> � �overriding >> � � � procedure Some_Operation (X : T2; Y : T2); >> � � � � -- Error! >> >> � �overriding >> � � � procedure Some_Operation (X : T2; Y : T2); >> � �overriding >> � � � procedure Some_Operation (X : T1; Y : T2); >> � �overriding >> � � � procedure Some_Operation (X : T2; Y : T1); >> � � � � �-- OK >> >> Yes, this is tedious, but the programmer should know what we does when he >> declares a multi-method. This is the semantics of - there are n**2 >> combinations which do potentially different things. If there are only n >> combinations, that is another case, which should *not* look MD, because it >> is not. I think it should somehow use tuples instead. The error should >> happen upon tuple construction, not in the call. For the reader it should >> be clear that there is only one tag. I have no idea how to spell that >> syntactically or by other means. > > It appears to me that we're talking at cross-purposes. No, it is the same problem of MD. It has some special cases like multi-methods, which are simpler, but it is still MD. The example you provided is full MD. You have three hierarchies: Document'Class, Text_Region'Class, Marker'Class An operation can be primitive in any combinations of types from these hierarchies. > I was trying to solve one particular class of problems; > you seem to be trying to solve the entire Multiple > Dispatch problem. I believe that when I first thought > about making this proposal, I was assuming that a fully > general multiple dispatch wasn't ever going to be part > of the language, so I was thinking about an improvement > for one particular case I had run into. > > Here's an example of the sort of problem I'm thinking > about: Suppose you want to define an abstract type that > represents a document, which could be a plain ASCII > text file, a Word document, a PDF file, etc., that all > have in common that they consist of sequences of > "characters" and "other elements of some sort". Some > of the documents may also have formatting information > or other good stuff, but these won't be common to all > documents. > > You want to write a package that deals with documents > and particularly with the text (characters) in the > documents. The type of document (plain text, Word, > etc.) won't be known until run time. Some of the > operations you want to provide are: search for given > text in the document, and return "markers" that point > to the beginning and end of the text that is found; and > move text from one point to another in the document. > > A natural way to do this, it seems, would be to define > a Document as an abstract type, and let other packages > (Plaintext_Documents, Word_Documents, PDF_Documents, > etc.) define concrete types as extensions of that type > for plain text, Word documents, PDF documents, and so > on. You also want to define, say, a Text_Region type > that represents a region of text in a document, and a > Marker type that represents a particular point in the > document text, between two characters. The Text_Region > and Marker types should be abstract also, since we > don't know how they'd be implemented for different > document types; for a plain text document, a Marker is > probably just a string index, in essence, but the > Word_Documents and PDF_Documents might unpack a file > into some complex internal data structure when it reads > it, so a Marker for those document types might contain > pointers into the internal data structure, or > something. > > So it seems natural to write the package like this > (along with other operations): > > package Documents is > > type Document is abstract tagged private; > type Text_Region is abstract tagged private; > type Marker is abstract tagged private; > > function Search_For_Text (Doc : Document; Text : String) > return Text_Region is abstract; > -- I'm assuming here that it would return some sort of > -- "no region" value if the text is not found > function Beginning_Of (Reg : Text_Region) return Marker > is abstract; > function End_Of (Reg : Text_Region) return Marker > is abstract; > > procedure Move_Text (Doc : in out Document; > From : in Text_Region; > To : in Marker) > is abstract; > > end Documents; > > The last would cut the text represented by the From > region and move it to the point specified by "To". (It > would be up to the packages to figure out how to move > formatting information or other information in an > appropriate way.) > > Of course, Move_Text can't be declared because it is a > primitive operation of more than one tagged type. > Actually, none of the functions can be declared, > either, but I want to focus on the Move_Text procedure. > You might want to call Move_Text with a > Word_Documents.Document, Word_Documents.Text_Region, > and Word_Documents.Marker, or similarly with three > objects all with types from the PDF_Documents or > Plaintext_Documents package, but you would never want > to mix objects from two different packages. As an example consider these operations: procedure Cut ( Doc : in out Document; From : in Text_Region; Clipboard : in out Document ); procedure Paste ( Clipboard : in Document; Doc : in out Document; To : in Marker ); > And that > was the purpose of the proposal I was thinking of > making: to allow for restricted use of multiple > dispatch for cases like this (and this is a situation I > believe I've run into more than once), in which > Move_Text could be declared but could only be called if > the parameters' specific types all come from the same > package, checked at runtime if necessary. The > Move_Text procedure would be inherited in each of the > other packages (Plaintext_Documents, etc.), with types > from that package, but there would not be any inherited > Move_Text that would involve types from different > packages. (This was an idea I first thought about a > long time ago, but eventually abandoned; I really > hadn't thought about how to deal with the functions.) So if you derived ASCII_Text from Plain_Text you would lose Move_Text if you forgot to derive from Text_Region and Marker? > There are workarounds, of course. As I responded to > the original poster in this thread, you can make some > of the parameters to Move_Text class-wide, and the > concrete procedures would check to make sure the actual > tags for the class-wide parameters are correct---or > just doing a type conversion will do the check: > > package body Word_Documents is > > overriding > procedure Move_Text (Doc : in out Document; > From : in Documents.Text_Region'Class; > To : in Documents.Marker'Class) is > X_From : Text_Region renames Text_Region(From); > X_To : Marker renames Marker(To); > ... > > I think that's correct. Anyway, those will raise > Constraint_Error if the parameters are from the wrong > package. (BTW, the jargon word is "covariance". Document, Text_Region and Marker are said "covariant" in Move_Text.) > However, I consider that to be a workaround (maybe > others don't think it's that bad), which is why I was > thinking about making this proposal---to make things > work more "naturally", without a workaround, in this > sort of situation that I've run into multiple times. > I'm not sure what your idea is trying to solve (other > than "making multiple dispatch perfect"); but even if > it does make general multiple dispatch available, your > way of doing things would not solve anything in this > sort of case, because you would require Move_Text to be > overridden for every possible combination of children > of the types, which makes it unusable and means that > even with your Grand Solution To Everything, those of > us who want to implement something like the above would > have to go back to using the workaround. Consider again ASCII_Text derived from Plain_Text. Let we also derived from Plain_Text_Region and Plain_Marker: Plain_Text <-- ASCII_Text Plain_Text_Region <-- ASCII_Region Plain_Marker <-- ASCII_Marker Now, my concern is what happens with Move_To called on ASCII_Text, Text_Region, Plain_Marker You say, it is Constraint_Error. The question what is the reason for this? I don't see any. It is for the programmer to decide. I you say the language forbids that, I say OK, but then that MUST be done static. Anyway your proposal would impose some draconian limitations on the packages structure. E.g. it would be impossible to provide implementations and different types of Marker in separate packages. Or consider a generic implementing a Marker for the given document type. Would that work? > That's why I think we're not on the same page---we're > trying to solve two different and unrelated problems > that seem only superficially related. Maybe we can't > solve them both. Actually I considered both cases with the requirement of the constraint being static. To summarize: 1. Full MD = no constraint => all combinations checked and required 2. SD on tuples = static constraint on the combinations The case 2 has consequences, of course. What I find totally inappropriate for a language like Ada is: 3. Dynamic constraint with run-time checks dropping arbitrary exceptions. I think we should have learnt something from the "accessibility checks disaster". -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-18 9:46 ` Dmitry A. Kazakov @ 2009-11-18 16:39 ` Adam Beneschan 2009-11-18 19:21 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Adam Beneschan @ 2009-11-18 16:39 UTC (permalink / raw) On Nov 18, 1:46 am, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > It appears to me that we're talking at cross-purposes. > > No, it is the same problem of MD. It has some special cases like > multi-methods, which are simpler, but it is still MD. The example you > provided is full MD. You have three hierarchies: > > Document'Class, Text_Region'Class, Marker'Class > > An operation can be primitive in any combinations of types from these > hierarchies. > > Here's an example of the sort of problem I'm thinking > > about: Suppose you want to define an abstract type that > > represents a document, which could be a plain ASCII > > text file, a Word document, a PDF file, etc., that all > > have in common that they consist of sequences of > > "characters" and "other elements of some sort".... > > Of course, Move_Text can't be declared because it is a > > primitive operation of more than one tagged type. > > Actually, none of the functions can be declared, > > either, but I want to focus on the Move_Text procedure. > > You might want to call Move_Text with a > > Word_Documents.Document, Word_Documents.Text_Region, > > and Word_Documents.Marker, or similarly with three > > objects all with types from the PDF_Documents or > > Plaintext_Documents package, but you would never want > > to mix objects from two different packages. > > As an example consider these operations: > > procedure Cut > ( Doc : in out Document; > From : in Text_Region; > Clipboard : in out Document > ); > procedure Paste > ( Clipboard : in Document; > Doc : in out Document; > To : in Marker > ); Right, I wasn't envisioning this sort of operation that involved two *different* documents of (potentially) two different types. When I started thinking about this idea, I had had some issues with programs I was working on, but it was so long ago that I don't remember what the exact problem was and I can't find it. Whatever it was, it was *not* a case where there would have been any operations involving two different objects of two different child types. > So if you derived ASCII_Text from Plain_Text you would lose Move_Text if > you forgot to derive from Text_Region and Marker? Most likely, I would have written the rules so that if you *do* declare an operation like this of two (or more) different tagged types, and if in some other package you don't derive from all those types, it would be an error at that point. Again, although I can see that someone might want to derive ASCII_Text from Plain_Text, it wasn't something I considered at the time since it wasn't germane to the type of problem I was running into. > Consider again ASCII_Text derived from Plain_Text. Let we also derived from > Plain_Text_Region and Plain_Marker: > > Plain_Text <-- ASCII_Text > Plain_Text_Region <-- ASCII_Region > Plain_Marker <-- ASCII_Marker > > Now, my concern is what happens with Move_To called on > > ASCII_Text, Text_Region, Plain_Marker > > You say, it is Constraint_Error. The question what is the reason for this? > I don't see any. It is for the programmer to decide. > > I you say the language forbids that, I say OK, but then that MUST be done > static. I don't see a practical reason why that would be necessary. And I have a major objection, at least to what I think you're proposing. Suppose someone at some organization declares a package with some abstract types. Another organization is developing a large application. Since this is a large application, it's divided into several parts with one part being given to one team, another part to another team, and so on, working independently. The idea is that as long as each team can develop its part of the application that works according to some "contract", then you should be able to put the pieces together and get the whole thing to work. Suppose that two or three of those teams decide to use this library package and derives child types from those abstract types. This shouldn't be an issue for teams working independently, but your idea would make it an issue, because in order for the program to compile, you would require that somebody write useless versions of the inherited operations for every possible combination of derived types, even when the derived types come from two different teams working independently. Sounds like a needless burden to me. > Anyway your proposal would impose some draconian limitations on the > packages structure. E.g. it would be impossible to provide implementations > and different types of Marker in separate packages. Or consider a generic > implementing a Marker for the given document type. Would that work? 1) Keep in mind that I wasn't trying to solve the whole world, just one subset of the problem. The way I envisioned it would, I think, have been fairly simple to generate code for and wouldn't be a huge implementation burden, nor would it have imposed a huge burden on programmers. (I could be wrong, since there are some cases I didn't think about, particularly involving tag-indeterminate function calls.) I realize that my proposal wasn't going to solve everything, but my feeling is that if you can solve a subset of a problem simply, and solving the entire problem would be orders of magnitude more complex, then the fact that there will still be unsolved issues isn't a valid reason not to solve the narrower problem. (That would be called "letting the perfect be the enemy of the good enough". And even with the limitations you think are draconian, I think it would have been good enough for the types of problems I was encountering.) Unless we envision that the larger problem *will* be addressed at some point and the solution to the smaller problem might interfere with the larger solution. But at the time, I didn't see any reason to believe that the general "multiple dispatch" case will ever be addressed. Maybe it still won't. 2) I didn't finish thinking everything through before I abandoned the idea, so I didn't consider things like generics. Also, it's possible that there might have been a way to address some of your objections without significantly adding to the implementation burden. > What I find totally inappropriate for a language like Ada is: > > 3. Dynamic constraint with run-time checks dropping arbitrary exceptions. > > I think we should have learnt something from the "accessibility checks > disaster". OK, I don't understand this. First, I don't understand what about accessibility checks was a disaster; and second, I don't see what's wrong with runtime checks in cases where it's too hard for a compiler to figure out at compile time whether something will go wrong. Is this another case of letting the perfect be the enemy of the good enough? -- Adam ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-18 16:39 ` Adam Beneschan @ 2009-11-18 19:21 ` Dmitry A. Kazakov 2009-11-19 0:27 ` Randy Brukardt 2009-11-19 16:04 ` Adam Beneschan 0 siblings, 2 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-18 19:21 UTC (permalink / raw) On Wed, 18 Nov 2009 08:39:14 -0800 (PST), Adam Beneschan wrote: > On Nov 18, 1:46�am, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > > Again, although I can see that someone might want to derive ASCII_Text > from Plain_Text, it wasn't something I considered at the time since it > wasn't germane to the type of problem I was running into. So, in effect you want a depth-1 hierarchy, like we have for the protected interfaces? >> Consider again ASCII_Text derived from Plain_Text. Let we also derived from >> Plain_Text_Region and Plain_Marker: >> >> � �Plain_Text <-- ASCII_Text >> � �Plain_Text_Region <-- ASCII_Region >> � �Plain_Marker <-- ASCII_Marker >> >> Now, my concern is what happens with Move_To called on >> >> � �ASCII_Text, Text_Region, Plain_Marker >> >> You say, it is Constraint_Error. The question what is the reason for this? >> I don't see any. It is for the programmer to decide. >> >> I you say the language forbids that, I say OK, but then that MUST be done >> static. > > I don't see a practical reason why that would be necessary. And I > have a major objection, at least to what I think you're proposing. > Suppose someone at some organization declares a package with some > abstract types. Another organization is developing a large > application. Since this is a large application, it's divided into > several parts with one part being given to one team, another part to > another team, and so on, working independently. The idea is that as > long as each team can develop its part of the application that works > according to some "contract", then you should be able to put the > pieces together and get the whole thing to work. Suppose that two or > three of those teams decide to use this library package and derives > child types from those abstract types. This shouldn't be an issue for > teams working independently, but your idea would make it an issue, > because in order for the program to compile, you would require that > somebody write useless versions of the inherited operations for every > possible combination of derived types, even when the derived types > come from two different teams working independently. Sounds like a > needless burden to me. No, it is not a burden it is a mere consequence of a decision made earlier. If the abstract types are Printer and Contents with the joint operation Print (the classic example of MD), you *have* to implement all combinations. It is not a question. The question is how to make the language helping us in doing this work safely and efficiently. I have no idea how to do this while keeping it modular. But that does not change the fact that all combinations have to be defined. To silently define some of them as Constraint_Error is a very bad idea for the language like Ada. >> What I find totally inappropriate for a language like Ada is: >> >> 3. Dynamic constraint with run-time checks dropping arbitrary exceptions. >> >> I think we should have learnt something from the "accessibility checks >> disaster". > > OK, I don't understand this. First, I don't understand what about > accessibility checks was a disaster; Because they are the major contributor to hours spent on debugging unhandled exceptions. > and second, I don't see what's > wrong with runtime checks in cases where it's too hard for a compiler > to figure out at compile time whether something will go wrong. Accessibility checks are known for being false positive. Same with MD, it is not a semantic error to cross types in an operation. It is an *artificial* error introduced by the language designer, because it would be too difficult to do it otherwise (e.g. right (:-)). Then the very same designer finds his own error too difficult to detect at compile time! I have no problem with the former, but do not accept the latter. > Is > this another case of letting the perfect be the enemy of the good > enough? There exist lots of imperfect languages already. But Ada must be perfect! (:-)) Returning to the case where we do not want cross combinations. I thought about it and came to the conclusion that this is single dispatch (tags are synchronous). I think we should approach this differently. Let us consider an example were dispatching table is semantically "diagonal": type Object is limited interface ...; type Handle is interface ...; Each time we derive from Object, we want a new Handle. This is IMO a better example than Document x Region x Marker. Note that here the issue of packages is especially important. Most likely we wanted to place Objects into private packages, making only Handles public. Most operations on Object and Handle are same and have only one controlled argument. I am tempted to say that Handle should also implement Object. type Handle is interface and Object ...; But there are two cross operations: function Ref (X : Object) return Handle is abstract; function Target (X : Handle) return access Object is abstract; The same problem more complicated. How to add type Readonly_Handle is interface ...; -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-18 19:21 ` Dmitry A. Kazakov @ 2009-11-19 0:27 ` Randy Brukardt 2009-11-19 2:11 ` Robert A Duff 2009-11-19 8:50 ` Dmitry A. Kazakov 2009-11-19 16:04 ` Adam Beneschan 1 sibling, 2 replies; 132+ messages in thread From: Randy Brukardt @ 2009-11-19 0:27 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:1wtsriaxu0s4s$.ikwnnz5teukp$.dlg@40tude.net... ... >> OK, I don't understand this. First, I don't understand what about >> accessibility checks was a disaster; > > Because they are the major contributor to hours spent on debugging > unhandled exceptions. That seems odd to me. I rather *like* getting unhandled exceptions, because it is almost always easy to see what the problem is from the exception name and the traceback. (And all of my long-running programs have systems to record this in the case of an unexpected exception.) It's the incorrect results, compiler bugs (where the code is right, but the wrong thing happens), and crashes that cause "hours of debugging". OTOH, doing something that even generates a runtime accessibility check is a bug IMHO (it's what Bob Duff calls a "tripping hazard" - a latent problem that will bite you when someone does something unconventional -- in that sense it is just like the bug of assigning a string parameter has lower bound 1); it would be useful if the compiler could optionally point out such places so you could fix them. Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-19 0:27 ` Randy Brukardt @ 2009-11-19 2:11 ` Robert A Duff 2009-11-19 15:57 ` Adam Beneschan 2009-11-19 8:50 ` Dmitry A. Kazakov 1 sibling, 1 reply; 132+ messages in thread From: Robert A Duff @ 2009-11-19 2:11 UTC (permalink / raw) "Randy Brukardt" <randy@rrsoftware.com> writes: > OTOH, doing something that even generates a runtime accessibility check is a > bug IMHO (it's what Bob Duff calls a "tripping hazard"... I didn't invent the term. ;-) I think I first heard it from OSHA regulations, when I was working in a factory making "real stuff" instead of software. For example, I just found this by googling "OSHA tripping hazard": Extension cords that could cause a tripping hazard. This is an easy area for OSHA to find violations while auditing most organizations. Extension cords are a major cause of tripping and fall hazards and therefore are under a microscope. Extension cords shall not be found passing through hallways, windows and other areas which could cause a tripping hazard. ... and train employees how to use the cords without creating a tripping hazard. 29 CFR 1910.310 & 1910.305(G)(2)(iii) >... - a latent problem > that will bite you when someone does something unconventional -- in that > sense it is just like the bug of assigning a string parameter has lower > bound 1); it would be useful if the compiler could optionally point out such > places so you could fix them. My objection to run-time accessibility checks is that you can't pin the blame on a particular line of code: procedure P (X : access T); P either stores the access value in a long-lived global data structure (in which case P(Local'Access) is a bad idea in clients) or P does not do that (in which case P(Local'Access) is just fine). The author of P knows which one is the case, but has no way (other than comments) to communicate this to the author of the client. I've never seen a case where P decides at run time which it is. So we have a run-time check on something that is known at compile time (by the person who wrote P), and should be known at compile time (by the person who wrote the client). Yes, it's a tripping hazard. And when the exception is raised, we don't know whether P or client-of-P is at fault. - Bob ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-19 2:11 ` Robert A Duff @ 2009-11-19 15:57 ` Adam Beneschan 2009-11-19 19:39 ` Robert A Duff 0 siblings, 1 reply; 132+ messages in thread From: Adam Beneschan @ 2009-11-19 15:57 UTC (permalink / raw) On Nov 18, 6:11 pm, Robert A Duff <bobd...@shell01.TheWorld.com> wrote: > My objection to run-time accessibility checks is that you can't pin the > blame on a particular line of code: > > procedure P (X : access T); > > P either stores the access value in a long-lived global data structure > (in which case P(Local'Access) is a bad idea in clients) > or P does not do that > (in which case P(Local'Access) is just fine). > The author of P knows which one is the case, > but has no way (other than comments) to communicate this to > the author of the client. > I've never seen a case where P decides at run time which it is. Since I'm too lazy to look, I'm hoping you know this off the top of your head: is there anything in the AI's about preconditions that would allow something to be caught when P is called rather than later in the body of P? I thought there were also some proposed attributes that could test accessibility levels. -- Adam ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-19 15:57 ` Adam Beneschan @ 2009-11-19 19:39 ` Robert A Duff 2009-11-19 23:43 ` Randy Brukardt 0 siblings, 1 reply; 132+ messages in thread From: Robert A Duff @ 2009-11-19 19:39 UTC (permalink / raw) Adam Beneschan <adam@irvine.com> writes: > On Nov 18, 6:11�pm, Robert A Duff <bobd...@shell01.TheWorld.com> > wrote: > >> My objection to run-time accessibility checks is that you can't pin the >> blame on a particular line of code: >> >> � � procedure P (X : access T); >> >> P either stores the access value in a long-lived global data structure >> (in which case P(Local'Access) is a bad idea in clients) >> or P does not do that >> (in which case P(Local'Access) is just fine). >> The author of P knows which one is the case, >> but has no way (other than comments) to communicate this to >> the author of the client. >> I've never seen a case where P decides at run time which it is. > > Since I'm too lazy to look, I'm hoping you know this off the top of > your head: is there anything in the AI's about preconditions that > would allow something to be caught when P is called rather than later > in the body of P? I thought there were also some proposed attributes > that could test accessibility levels. Such things have been discussed. We're probably going to have preconditions, and I seem to recall discussing some way to query the necessary accessibility-level info. But I think perhaps the best solution is to use a named access type, or an 'in out' parameter, in most cases. Functions are going to allow 'in out' parameters (probably), so the need for access parameters is much diminished. - Bob ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-19 19:39 ` Robert A Duff @ 2009-11-19 23:43 ` Randy Brukardt 0 siblings, 0 replies; 132+ messages in thread From: Randy Brukardt @ 2009-11-19 23:43 UTC (permalink / raw) "Robert A Duff" <bobduff@shell01.TheWorld.com> wrote in message news:wcctywq1ezh.fsf@shell01.TheWorld.com... > Adam Beneschan <adam@irvine.com> writes: ... >> Since I'm too lazy to look, I'm hoping you know this off the top of >> your head: is there anything in the AI's about preconditions that >> would allow something to be caught when P is called rather than later >> in the body of P? I thought there were also some proposed attributes >> that could test accessibility levels. > > Such things have been discussed. We're probably going to have > preconditions, and I seem to recall discussing some way to query the > necessary accessibility-level info. Memberships most likely will be extended to include accessibility checks. But I can't off-hand figure out how that could be used to do a library-level accessibility check short of declaring a named library-level access type to use in the membership. In that case, you probably should have just used the named type in the parameter in the first place and need no precondition (at least for that). Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-19 0:27 ` Randy Brukardt 2009-11-19 2:11 ` Robert A Duff @ 2009-11-19 8:50 ` Dmitry A. Kazakov 2009-11-19 23:54 ` Randy Brukardt 1 sibling, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-19 8:50 UTC (permalink / raw) On Wed, 18 Nov 2009 18:27:42 -0600, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:1wtsriaxu0s4s$.ikwnnz5teukp$.dlg@40tude.net... > ... >>> OK, I don't understand this. First, I don't understand what about >>> accessibility checks was a disaster; >> >> Because they are the major contributor to hours spent on debugging >> unhandled exceptions. > > That seems odd to me. I rather *like* getting unhandled exceptions, because > it is almost always easy to see what the problem is from the exception name > and the traceback. Only theoretically. Practically, the existing handlers of "legal" exceptions get perplexed, the stack gets wound up causing a cascade of exceptions in Finalize's. > OTOH, doing something that even generates a runtime accessibility check is a > bug Isn't it an advise to use only 'Unchecked_Access? (:-)) I think it is a problem that the code containing 'Access or upcast pointer type conversions is "suspicious", a subject of careful inspection for false positives upon accessibility check: Upcast_Ptr (P) -- Unsafe P.all'Access -- Unsafe P.all'Unchecked_Access -- "Safe" > IMHO (it's what Bob Duff calls a "tripping hazard" - a latent problem > that will bite you when someone does something unconventional -- in that > sense it is just like the bug of assigning a string parameter has lower > bound 1); it would be useful if the compiler could optionally point out such > places so you could fix them. The difference is that for string bound there is a way to do it safe and for 'Access there is none (and I also agree with Robert's response.) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-19 8:50 ` Dmitry A. Kazakov @ 2009-11-19 23:54 ` Randy Brukardt 2009-11-20 8:34 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Randy Brukardt @ 2009-11-19 23:54 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:1iipp3bn16fe2.yqa1gz1ru17a$.dlg@40tude.net... > On Wed, 18 Nov 2009 18:27:42 -0600, Randy Brukardt wrote: > >> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message >> news:1wtsriaxu0s4s$.ikwnnz5teukp$.dlg@40tude.net... >> ... >>>> OK, I don't understand this. First, I don't understand what about >>>> accessibility checks was a disaster; >>> >>> Because they are the major contributor to hours spent on debugging >>> unhandled exceptions. >> >> That seems odd to me. I rather *like* getting unhandled exceptions, >> because >> it is almost always easy to see what the problem is from the exception >> name >> and the traceback. > > Only theoretically. Practically, the existing handlers of "legal" > exceptions get perplexed, the stack gets wound up causing a cascade of > exceptions in Finalize's. That might be true in general, but definitely not in this case: a handler for Program_Error is *always* wrong (unless it is a global handler for *any* exception), as Program_Error always represents a program bug. So I don't see how "existing" handlers can be confused. It is necessary to treat all Adjust and Finalize routines like task bodies in that they need a "when others" handler -- otherwise the exceptions are sucked up and you get completely lost. Our programming standard requires such handlers (generally, they output Exception_Information to the logging facility - and almost every significant Ada system needs some sort of logging facility). ... > The difference is that for string bound there is a way to do it safe and > for 'Access there is none (and I also agree with Robert's response.) Well, Ada 2012 (or whatever it will be called) should help that out by giving you a way to compare accessibilites (via memberships). So at least you will be able to make checks to avoid the problem. Better than nothing, but still not ideal. The better way to avoid the problem is to never, ever use anonymous access types. (Named access types have checks that are always made at compile-time for most compilers -- but not Janus/Ada, because of generic sharing.) Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-19 23:54 ` Randy Brukardt @ 2009-11-20 8:34 ` Dmitry A. Kazakov 2009-11-20 10:58 ` Jean-Pierre Rosen ` (2 more replies) 0 siblings, 3 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-20 8:34 UTC (permalink / raw) On Thu, 19 Nov 2009 17:54:40 -0600, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:1iipp3bn16fe2.yqa1gz1ru17a$.dlg@40tude.net... >> On Wed, 18 Nov 2009 18:27:42 -0600, Randy Brukardt wrote: >> >>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message >>> news:1wtsriaxu0s4s$.ikwnnz5teukp$.dlg@40tude.net... >>> ... >>>>> OK, I don't understand this. First, I don't understand what about >>>>> accessibility checks was a disaster; >>>> >>>> Because they are the major contributor to hours spent on debugging >>>> unhandled exceptions. >>> >>> That seems odd to me. I rather *like* getting unhandled exceptions, because >>> it is almost always easy to see what the problem is from the exception name >>> and the traceback. >> >> Only theoretically. Practically, the existing handlers of "legal" >> exceptions get perplexed, the stack gets wound up causing a cascade of >> exceptions in Finalize's. > > That might be true in general, but definitely not in this case: a handler > for Program_Error is *always* wrong (unless it is a global handler for *any* > exception), as Program_Error always represents a program bug. So I don't see > how "existing" handlers can be confused. exception when Error : others => some cleanup -- This usually becomes a problem raise; > It is necessary to treat all Adjust and Finalize routines like task bodies > in that they need a "when others" handler -- otherwise the exceptions are > sucked up and you get completely lost. Our programming standard requires > such handlers (generally, they output Exception_Information to the logging > facility - and almost every significant Ada system needs some sort of > logging facility). Yes, but this does not help. Upon exception propagation (Constraint_Error), you get some objects finalized. This in turn causes a snowball of exceptions in finalized objects, because no design is robust to hold any error at any place. In the end you have a huge log of meaningless messages, which only complicate debugging. Sometimes I which Ada had "halt at once" statement which would stop all tasks and hold any I/O. But of course the proper solution would be contracted exceptions. >> The difference is that for string bound there is a way to do it safe and >> for 'Access there is none (and I also agree with Robert's response.) > > Well, Ada 2012 (or whatever it will be called) should help that out by > giving you a way to compare accessibilites (via memberships). So at least > you will be able to make checks to avoid the problem. Better than nothing, > but still not ideal. The better way to avoid the problem is to never, ever > use anonymous access types. (Named access types have checks that are always > made at compile-time for most compilers -- but not Janus/Ada, because of > generic sharing.) The problem is not bound to only anonymous types. It also appears when you convert access types. The source type (or both) might be a formal generic parameter, so you cannot statically ensure that a "local" pointer is converted no a "less local" one. Pool specific pointers need to be converted though one target object is derived from another. Here everything is broken: generic contract, meaningless conversion, meaningless check, meaningless exception. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-20 8:34 ` Dmitry A. Kazakov @ 2009-11-20 10:58 ` Jean-Pierre Rosen 2009-11-21 6:02 ` Randy Brukardt 2009-11-22 5:45 ` xorque 2 siblings, 0 replies; 132+ messages in thread From: Jean-Pierre Rosen @ 2009-11-20 10:58 UTC (permalink / raw) Dmitry A. Kazakov a �crit : > Sometimes I which Ada had "halt at once" > statement which would stop all tasks and hold any I/O. > Aborting the main task comes quite close to that wish, except that controlled objects are finalized. But OTOH, you have these people who want to be absolutely sure that objects get finalized under any circumstance... -- --------------------------------------------------------- J-P. Rosen (rosen@adalog.fr) Visit Adalog's web site at http://www.adalog.fr ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-20 8:34 ` Dmitry A. Kazakov 2009-11-20 10:58 ` Jean-Pierre Rosen @ 2009-11-21 6:02 ` Randy Brukardt 2009-11-21 13:07 ` Dmitry A. Kazakov 2009-11-22 5:45 ` xorque 2 siblings, 1 reply; 132+ messages in thread From: Randy Brukardt @ 2009-11-21 6:02 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:18wh86jvjvoe0.cofxcc8udm6q$.dlg@40tude.net... > On Thu, 19 Nov 2009 17:54:40 -0600, Randy Brukardt wrote: >> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message >> news:1iipp3bn16fe2.yqa1gz1ru17a$.dlg@40tude.net... ... >> That might be true in general, but definitely not in this case: a handler >> for Program_Error is *always* wrong (unless it is a global handler for >> *any* >> exception), as Program_Error always represents a program bug. So I don't >> see >> how "existing" handlers can be confused. > > exception > when Error : others => > some cleanup -- This usually becomes a problem > raise; If that's the case, "some cleanup" is seriously flawed. An "others" handler has to always work no matter what condition come into it, and in particular cannot depend on the code/data it surrounds to be in any particular state. The clean up code needs to be aware that data structures may be corrupt; if it isn't it of course will cascade errors and generally make a mess -- but in that case it is just junk code doing more harm than good. I believe this particular pattern ("others" handler containing explicit cleanup) usually represents a bad design. Clients (or even the implementers of ADTs) often forget to include the cleanup where it is needed. These days, I try to put all needed cleanup into the ADTs, so that they get cleaned up no matter what -- finalization is pretty much the only certainty in Ada. And in that case, you don't need these kind of exception handlers. >> It is necessary to treat all Adjust and Finalize routines like task >> bodies >> in that they need a "when others" handler -- otherwise the exceptions are >> sucked up and you get completely lost. Our programming standard requires >> such handlers (generally, they output Exception_Information to the >> logging >> facility - and almost every significant Ada system needs some sort of >> logging facility). > > Yes, but this does not help. Upon exception propagation > (Constraint_Error), > you get some objects finalized. This in turn causes a snowball of > exceptions in finalized objects, because no design is robust to hold any > error at any place. In the end you have a huge log of meaningless > messages, > which only complicate debugging. That can happen, but it usually indicates too much coupling between objects and/or too little concern about the implications of errors. Finalize routines should never, ever propagate an exception, and need to be written to avoid that - that means avoiding assumptions about other objects. If that's not done, of course you get messy cascades of errors. I realize that it isn't completely possible to avoid these things, but it can be minimized. And the huge log of messages doesn't matter, you only need the first one (presuming every "others" handler logs the data). That's the place the exception was raised, and the information tells you how it got to that point; just ignore the rest of the log (in that sense it is just like fixing syntax errors in most compilers -- the correction isn't good enough to find more than the first error if it is at all significant). > Sometimes I which Ada had "halt at once" > statement which would stop all tasks and hold any I/O. Janus/Ada does have such a thing (always did, it was the first thing implemented on CP/M back in 1980). It works because (to date) we don't use operating systems facilities for tasking, exception handling, or finalization. But it is pretty dangerous, and really can be used only to instantly shut down a misbehaving system -- but of course if there is any real resource management being handled by finalization, you could leak resources such that you don't get them back easily (the obvious example is temporary files, which won't be cleaned up in that scenario). > But of course the proper solution would be contracted exceptions. I don't see how that would help. The problem is used "others" when you really need to list the exceptions that you are expecting. If the compiler is smart enough to be able to prove that no bounded errors occur (and no constraint violations either), maybe that would do some good, but I doubt that you will see such compilers anytime soon. In the absence of that, you have to put Program_Error, Constraint_Error, and Storage_Error into every contract, so you don't gain much. >>> The difference is that for string bound there is a way to do it safe and >>> for 'Access there is none (and I also agree with Robert's response.) >> >> Well, Ada 2012 (or whatever it will be called) should help that out by >> giving you a way to compare accessibilites (via memberships). So at least >> you will be able to make checks to avoid the problem. Better than >> nothing, >> but still not ideal. The better way to avoid the problem is to never, >> ever >> use anonymous access types. (Named access types have checks that are >> always >> made at compile-time for most compilers -- but not Janus/Ada, because of >> generic sharing.) > > The problem is not bound to only anonymous types. It also appears when you > convert access types. The source type (or both) might be a formal generic > parameter, so you cannot statically ensure that a "local" pointer is > converted no a "less local" one. Pool specific pointers need to be > converted though one target object is derived from another. Here > everything > is broken: generic contract, meaningless conversion, meaningless check, > meaningless exception. In this case, whether a check will fail is known statically for any compiler that generates generics via macro substitution (that is all Ada compilers other than Janus/Ada). I can hardly imagine that there exists a compiler that will generate "raise Program_Error" unconditionally without generating a warning! So this is an academic concern at best (I don't think you're using Janus/Ada) -- yes, you can ignore the warnings and run such a program, but there isn't any reason to actually do so unless you're running an ACATS test. (And if you do the conversion in the generic specification, the compiler is required to reject the program.) Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-21 6:02 ` Randy Brukardt @ 2009-11-21 13:07 ` Dmitry A. Kazakov 0 siblings, 0 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-21 13:07 UTC (permalink / raw) On Sat, 21 Nov 2009 00:02:59 -0600, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:18wh86jvjvoe0.cofxcc8udm6q$.dlg@40tude.net... >> On Thu, 19 Nov 2009 17:54:40 -0600, Randy Brukardt wrote: >>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message >>> news:1iipp3bn16fe2.yqa1gz1ru17a$.dlg@40tude.net... > ... >>> That might be true in general, but definitely not in this case: a handler >>> for Program_Error is *always* wrong (unless it is a global handler for *any* >>> exception), as Program_Error always represents a program bug. So I don't >>> see how "existing" handlers can be confused. >> >> exception >> when Error : others => >> some cleanup -- This usually becomes a problem >> raise; > > If that's the case, "some cleanup" is seriously flawed. An "others" handler > has to always work no matter what condition come into it, and in particular > cannot depend on the code/data it surrounds to be in any particular state. > The clean up code needs to be aware that data structures may be corrupt; if > it isn't it of course will cascade errors and generally make a mess -- but > in that case it is just junk code doing more harm than good. Right, "others" stands for all "legal" exceptions propagating out of the body, which the programmer was unable to specify, because it is too much work to do. The problem is that "illegal" exceptions indicating broken program do not belong here, and because of the lack of contracts you cannot separate them. The pattern is wrong, but there is no other. > I believe this particular pattern ("others" handler containing explicit > cleanup) usually represents a bad design. Clients (or even the implementers > of ADTs) often forget to include the cleanup where it is needed. These days, > I try to put all needed cleanup into the ADTs, so that they get cleaned up > no matter what -- finalization is pretty much the only certainty in Ada. And > in that case, you don't need these kind of exception handlers. I agree, but that only moves cleanup from one place to another (Finalize). There is no way to get rid of it. Within Finalize cleanup has even more chances to get perplexed, because in Ada you cannot know whether Finalize was called upon normal leaving the scope due to an exception propagation. Further the Ada's construction model is not enough fine to fully accommodate this pattern. For accessibility checks it could also aggravate the problem because accessibility is not invariant to moving from one body to another. The same code might be OK or not OK within different bodies. > And the huge log of messages doesn't matter, you only need the first one > (presuming every "others" handler logs the data). No it will be the last one (it is a stack of errors which gets unwound), but most likely it will be consumed by something else, like Program_Error. That is a reason for the "wrong" pattern with "others". Yes it is bad, but it lets you to log Exception_Information *before* things explode. >> But of course the proper solution would be contracted exceptions. > > I don't see how that would help. The problem is used "others" when you > really need to list the exceptions that you are expecting. If the compiler > is smart enough to be able to prove that no bounded errors occur (and no > constraint violations either), maybe that would do some good, but I doubt > that you will see such compilers anytime soon. In the absence of that, you > have to put Program_Error, Constraint_Error, and Storage_Error into every > contract, so you don't gain much. It should be more intelligent than Java. For example, "I don't do Storage_Error if there is N (static value) free bytes of stack", "I don't raise Program_Error from Initialize/Finalize" etc. An important issue to me is exception contract bound to some precondition, which ensures absence of specified exceptions. When an exception happens, it is not range error, or accessibility check to blame, but the precondition violated. >>>> The difference is that for string bound there is a way to do it safe and >>>> for 'Access there is none (and I also agree with Robert's response.) >>> >>> Well, Ada 2012 (or whatever it will be called) should help that out by >>> giving you a way to compare accessibilites (via memberships). So at least >>> you will be able to make checks to avoid the problem. Better than nothing, >>> but still not ideal. The better way to avoid the problem is to never, ever >>> use anonymous access types. (Named access types have checks that are always >>> made at compile-time for most compilers -- but not Janus/Ada, because of >>> generic sharing.) >> >> The problem is not bound to only anonymous types. It also appears when you >> convert access types. The source type (or both) might be a formal generic >> parameter, so you cannot statically ensure that a "local" pointer is >> converted no a "less local" one. Pool specific pointers need to be >> converted though one target object is derived from another. Here everything >> is broken: generic contract, meaningless conversion, meaningless check, >> meaningless exception. > > In this case, whether a check will fail is known statically for any compiler > that generates generics via macro substitution (that is all Ada compilers > other than Janus/Ada). I can hardly imagine that there exists a compiler > that will generate "raise Program_Error" unconditionally without generating > a warning! Yes it generates that warning, and the warning is ignored. Any warning is ignored, that is one the most fundamental principle of modern software design. (:-)) The code was modified to X.all'Unchecked_Access. Everybody is happy? -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-20 8:34 ` Dmitry A. Kazakov 2009-11-20 10:58 ` Jean-Pierre Rosen 2009-11-21 6:02 ` Randy Brukardt @ 2009-11-22 5:45 ` xorque 2009-11-22 11:25 ` Georg Bauhaus 2 siblings, 1 reply; 132+ messages in thread From: xorque @ 2009-11-22 5:45 UTC (permalink / raw) I have to say I'm quite impressed that my question started so much discussion. For this (minor) project, I tried a multitude of different approaches and ended up with one that appeared safe, right until it ended up causing GNAT to have some sort of heart attack: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42140 After some five or six attempts with different methods, I think I'm at the point where I've given up on the idea of using more than one tagged type for this and will likely just expose opaque integer identifiers. The problem was really one of state rather than dispatching, for me. I wanted one 'archiver' type to dispatch operations that were stateless ("Can this archiver open this archive?"), one type to hold per-archive state (the state of an open archive) and one type to hold per-file state (a Stream_IO.File_Type handle or an offset into an open zip file). I believe I sketched an example of the kind of interface I wanted (in more or less completely invalid Ada-ish pseudocode): Archive : access Archiver.Archive_t'Class; File : access Archiver.File_t'Class; for Index in Archivers'Range loop if Archiver.Can_Mount (Archiver => Archivers (Index), Path => "file.zip") then Archive := Archiver.Open_Archive (Archiver => Archivers (Index), Path => "file.zip"); File := Archiver.Open (Archive => Archive, Path => "/directory/file.txt"); Archiver.Close (File); Archiver.Close_Archive (Archive); end if; end loop; This interface would have been entirely internal as somebody actually using the code would have used it through another layer (removing the requirement to test against different archivers and in effect making it impossible to tell that there are even multiple packages involved). To the poster that didn't know what PhysicsFS was: http://icculus.org/physfs/ A small example of how the interface is used (note that the fact that there are multiple archivers involved is completely hidden): http://icculus.org/physfs/physfstut.txt Regards, xw ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 5:45 ` xorque @ 2009-11-22 11:25 ` Georg Bauhaus 2009-11-22 11:30 ` xorque 2009-11-22 16:25 ` Dmitry A. Kazakov 0 siblings, 2 replies; 132+ messages in thread From: Georg Bauhaus @ 2009-11-22 11:25 UTC (permalink / raw) On 11/22/09 6:45 AM, xorque wrote: > > For this (minor) project, I tried a multitude of different approaches > and ended up > with one that appeared safe, right until it ended up causing GNAT to > have some > sort of heart attack: > > http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42140 Do you get segmentation fault with GNAT run in Ada mode? (Currently, this would mean options -gnato -fstack-check and no -gnatp, I think.) What happens if you name the access-to-String type used for a component of Archive_t, i.e. something like type String_a is access String; subtype File_Name_a is String_a; type Archive_t is new Ada.Finalization.Limited_Controlled with record Name : File_Name_a; File : Stream_IO.File_Type; end record; ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 11:25 ` Georg Bauhaus @ 2009-11-22 11:30 ` xorque 2009-11-22 16:25 ` Dmitry A. Kazakov 1 sibling, 0 replies; 132+ messages in thread From: xorque @ 2009-11-22 11:30 UTC (permalink / raw) On Nov 22, 11:25 am, Georg Bauhaus <rm- host.bauh...@maps.futureapps.de> wrote: > >http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42140 > > Do you get segmentation fault with GNAT run in Ada mode? > (Currently, this would mean options -gnato -fstack-check > and no -gnatp, I think.) Yes, I actually always compile code with the following: -O2 -g -fstack-check -gnatw.eHeFT -gnatVa -gnato -gnata -gnatW8 -gnatiw -gnaty2aAbdefhiklnprStu (Although without -O2 when actually writing code, though). > What happens if you name the access-to-String type used for > a component of Archive_t, i.e. something like No change, unfortunately. Regards, xw ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 11:25 ` Georg Bauhaus 2009-11-22 11:30 ` xorque @ 2009-11-22 16:25 ` Dmitry A. Kazakov 2009-11-22 16:27 ` xorque 1 sibling, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-22 16:25 UTC (permalink / raw) On Sun, 22 Nov 2009 12:25:40 +0100, Georg Bauhaus wrote: > On 11/22/09 6:45 AM, xorque wrote: >> >> For this (minor) project, I tried a multitude of different approaches >> and ended up >> with one that appeared safe, right until it ended up causing GNAT to >> have some >> sort of heart attack: >> >> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42140 > > Do you get segmentation fault with GNAT run in Ada mode? > (Currently, this would mean options -gnato -fstack-check > and no -gnatp, I think.) The code is broken. It creates a temporal object and takes an access to its component. The obtained pointer is dangling. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 16:25 ` Dmitry A. Kazakov @ 2009-11-22 16:27 ` xorque 2009-11-22 16:42 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: xorque @ 2009-11-22 16:27 UTC (permalink / raw) On Nov 22, 4:25 pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > The code is broken. It creates a temporal object and takes an access to its > component. The obtained pointer is dangling. > Not sure which part of the code you're referring to here. Regards, xw ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 16:27 ` xorque @ 2009-11-22 16:42 ` Dmitry A. Kazakov 2009-11-22 16:52 ` xorque 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-22 16:42 UTC (permalink / raw) On Sun, 22 Nov 2009 08:27:59 -0800 (PST), xorque wrote: > On Nov 22, 4:25�pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: >> The code is broken. It creates a temporal object and takes an access to its >> component. The obtained pointer is dangling. > > Not sure which part of the code you're referring to here. The function Open_Archive returns a new object. In Main you call it and then apply the function Stream to the result. Stream returns an access to the component File of the temporal object created by Open_Archive. Then this object is destroyed and a dangling pointer is assigned to S. When you call Integer'Inpit on S, it accesses a garbage. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 16:42 ` Dmitry A. Kazakov @ 2009-11-22 16:52 ` xorque 2009-11-22 17:41 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: xorque @ 2009-11-22 16:52 UTC (permalink / raw) On Nov 22, 4:42 pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > On Sun, 22 Nov 2009 08:27:59 -0800 (PST), xorque wrote: > > Not sure which part of the code you're referring to here. > > The function Open_Archive returns a new object. In Main you call it and > then apply the function Stream to the result. Stream returns an access to > the component File of the temporal object created by Open_Archive. Then > this object is destroyed and a dangling pointer is assigned to S. When you > call Integer'Inpit on S, it accesses a garbage. Ah, I see what you mean. In the process of trying to save the result of Open_Archive so that I can test if this problem still occurs, I've run into another problem: A : Archiver.Archiver_t; O : constant Archiver.Archive_t := Archiver.Archive_t (Archiver.Open_Archive (A, "file.zip")); S : constant Stream_IO.Stream_Access := Archiver.Stream (O); main.adb:9:46: illegal context for call to function with limited result I have to admit to not understanding that error. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 16:52 ` xorque @ 2009-11-22 17:41 ` Dmitry A. Kazakov 2009-11-22 18:03 ` xorque 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-22 17:41 UTC (permalink / raw) On Sun, 22 Nov 2009 08:52:05 -0800 (PST), xorque wrote: > On Nov 22, 4:42�pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: >> On Sun, 22 Nov 2009 08:27:59 -0800 (PST), xorque wrote: >>> Not sure which part of the code you're referring to here. >> >> The function Open_Archive returns a new object. In Main you call it and >> then apply the function Stream to the result. Stream returns an access to >> the component File of the temporal object created by Open_Archive. Then >> this object is destroyed and a dangling pointer is assigned to S. When you >> call Integer'Inpit on S, it accesses a garbage. > > Ah, I see what you mean. > > In the process of trying to save the result of Open_Archive so that I > can test > if this problem still occurs, I've run into another problem: > > A : Archiver.Archiver_t; > O : constant Archiver.Archive_t := Archiver.Archive_t > (Archiver.Open_Archive (A, "file.zip")); > S : constant Stream_IO.Stream_Access := Archiver.Stream (O); > > main.adb:9:46: illegal context for call to function with limited > result > > I have to admit to not understanding that error. And I don't understand your design. If Archive is a Stream derive then it from Root_Stream_Type. If it deals with a stream then pass Root_Stream_Type'Class to the operations that need it. But never ever return pointers to components without an urgent need. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 17:41 ` Dmitry A. Kazakov @ 2009-11-22 18:03 ` xorque 2009-11-22 18:08 ` xorque ` (2 more replies) 0 siblings, 3 replies; 132+ messages in thread From: xorque @ 2009-11-22 18:03 UTC (permalink / raw) On Nov 22, 5:41 pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > On Sun, 22 Nov 2009 08:52:05 -0800 (PST), xorque wrote: > > > I have to admit to not understanding that error. > > And I don't understand your design. If Archive is a Stream derive then it > from Root_Stream_Type. If it deals with a stream then pass > Root_Stream_Type'Class to the operations that need it. The design is both irrelevant and obsolete. I'm just trying to find out if the problem is *definitely* that code so that I can close a bug in the GCC tracker. > But never ever return pointers to components without an urgent need. Is it possible to save the return value of Open_Archive, or not? Regards, xw ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 18:03 ` xorque @ 2009-11-22 18:08 ` xorque 2009-11-22 18:28 ` Dmitry A. Kazakov 2009-11-23 7:48 ` Georg Bauhaus 2 siblings, 0 replies; 132+ messages in thread From: xorque @ 2009-11-22 18:08 UTC (permalink / raw) If it's not already obvious, I'm aware that there's a bug in the above code but am not currently convinced that it's the cause of *this* crash as the crash occurs before the archive is finalized. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 18:03 ` xorque 2009-11-22 18:08 ` xorque @ 2009-11-22 18:28 ` Dmitry A. Kazakov 2009-11-22 18:41 ` xorque 2009-11-22 21:47 ` Robert A Duff 2009-11-23 7:48 ` Georg Bauhaus 2 siblings, 2 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-22 18:28 UTC (permalink / raw) On Sun, 22 Nov 2009 10:03:22 -0800 (PST), xorque wrote: > On Nov 22, 5:41�pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: >> On Sun, 22 Nov 2009 08:52:05 -0800 (PST), xorque wrote: >> >>> I have to admit to not understanding that error. >> >> And I don't understand your design. If Archive is a Stream derive then it >> from Root_Stream_Type. If it deals with a stream then pass >> Root_Stream_Type'Class to the operations that need it. > > The design is both irrelevant and obsolete. I'm just trying to find out if > the problem is *definitely* that code so that I can close a bug in the > GCC tracker. The problem semantically is that you convert type, which would/should create a copy of a limited object. Within a declaration ":=" denotes initialization. You may not convert anything initializing a limited object. Proper ways to go: O : constant Archiver.Archive_t'Class := Archiver.Open_Archive (A, "file.zip"); or O :Archiver.Archive_t'Class renames Archiver.Open_Archive (A, "file.zip"); or (with type casing, which is a *view* conversion) O : Archiver.Archive_t renames Archiver.Archive_t (Archiver.Open_Archive (A, "file.zip")); >> But never ever return pointers to components without an urgent need. > > Is it possible to save the return value of Open_Archive, or not? No, the type is limited it does not have "values", which can be saved (copied). You can create an object initialized in a way that its state would correspond to the desired value. P.S. ":=" for initialization of limited objects might look misleading (and does look misleading to me), but that would be another discussion on Ada design, in which nobody would agree with me. So I prefer not to go into it. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 18:28 ` Dmitry A. Kazakov @ 2009-11-22 18:41 ` xorque 2009-11-22 21:47 ` Robert A Duff 1 sibling, 0 replies; 132+ messages in thread From: xorque @ 2009-11-22 18:41 UTC (permalink / raw) On Nov 22, 6:28 pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > On Sun, 22 Nov 2009 10:03:22 -0800 (PST), xorque wrote: > > The problem semantically is that you convert type, which would/should > create a copy of a limited object... > <snipped> > Ok, thanks for the explanation. I made the change you suggest to initialize ("save") the archive and the crash doesn't occur. I've closed the bug on the GCC due to the discussion of the bug becoming confused between two issues (one of them - this one - invalid and another that's definitely valid). Regards, xw ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 18:28 ` Dmitry A. Kazakov 2009-11-22 18:41 ` xorque @ 2009-11-22 21:47 ` Robert A Duff 2009-11-23 3:42 ` stefan-lucks 2009-11-23 8:52 ` Dmitry A. Kazakov 1 sibling, 2 replies; 132+ messages in thread From: Robert A Duff @ 2009-11-22 21:47 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > P.S. ":=" for initialization of limited objects might look misleading (and > does look misleading to me), but that would be another discussion on Ada > design, in which nobody would agree with me. So I prefer not to go into it. Well, I would agree. Ada uses the term "assignment" to refer to both "initial assignment / initialization" and "assignment_statement / overwriting". I'd prefer to use different symbols for the two. We're not going to change Ada in that regard, for compatibility reasons, but I'm thinking in my hobby language design to use the term "assignment" for the initial one, and "reassignment" for the subsequent overwriting one, and use different symbols for the two. So, for a limited type, "assignment" is legal, "reassignment" is not. What do you think? - Bob ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 21:47 ` Robert A Duff @ 2009-11-23 3:42 ` stefan-lucks 2009-11-30 20:36 ` Robert A Duff 2009-11-23 8:52 ` Dmitry A. Kazakov 1 sibling, 1 reply; 132+ messages in thread From: stefan-lucks @ 2009-11-23 3:42 UTC (permalink / raw) On Sun, 22 Nov 2009, Robert A Duff wrote: > Ada uses the term "assignment" to refer to both "initial assignment / > initialization" and "assignment_statement / overwriting". > I'd prefer to use different symbols for the two. > We're not going to change Ada in that regard, for compatibility reasons, > but I'm thinking in my hobby language design to use the term "assignment" > for the initial one, and "reassignment" for the subsequent overwriting > one, and use different symbols for the two. > > So, for a limited type, "assignment" is legal, "reassignment" is not. Distinguishing the two different operations which are written as ":=" in Ada by using different words and even different symbols would make a lot of sense. But it is quite a standard notion to write "assignment" for overwriting the current value of a variable by a new value. This notion is widely used, much beyond Ada. You would likely confuse people by calling that operation a "reassignment" and using "assignment" for anther kind of operation. Please don't do that! Better use "assignment" for the overwriting operation, and another word (such as "initial assignment", "constructive assignment", "inisignment", ... a native English speaker might find better words) for the type of assignment that does not overwrite an existing value, and which is allowed for limited types. So long -- ------ Stefan Lucks -- Bauhaus-University Weimar -- Germany ------ Stefan dot Lucks at uni minus weimar dot de ------ I love the taste of Cryptanalysis in the morning! ------ ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-23 3:42 ` stefan-lucks @ 2009-11-30 20:36 ` Robert A Duff 2009-11-30 23:54 ` (see below) 2009-12-01 12:13 ` Georg Bauhaus 0 siblings, 2 replies; 132+ messages in thread From: Robert A Duff @ 2009-11-30 20:36 UTC (permalink / raw) stefan-lucks@see-the.signature writes: > On Sun, 22 Nov 2009, Robert A Duff wrote: > >> Ada uses the term "assignment" to refer to both "initial assignment / >> initialization" and "assignment_statement / overwriting". >> I'd prefer to use different symbols for the two. >> We're not going to change Ada in that regard, for compatibility reasons, >> but I'm thinking in my hobby language design to use the term "assignment" >> for the initial one, and "reassignment" for the subsequent overwriting >> one, and use different symbols for the two. >> >> So, for a limited type, "assignment" is legal, "reassignment" is not. > > Distinguishing the two different operations which are written as ":=" in > Ada by using different words and even different symbols would make a lot of > sense. I definitely want to use different symbols as well as different words. But I'm not sure what symbols to choose. Maybe := and :== . I want to allow: X : Integer; if ... then X := 0; -- initialize (what I want to call "assign") else X := 1; -- initialize (what I want to call "assign") end if; while ... loop X :== X + 1; -- overwriting (what I want to call "reassign") ... end loop; > But it is quite a standard notion to write "assignment" for overwriting > the current value of a variable by a new value. Well, the term is used in many non-functional languages for both cases: overwriting junk-bits with a value, and overwriting a previously-assigned value with a value. This is presumably because in those languages, there's no important difference between the two. In functional languages, the term "single assignment" is sometimes used, which means you can initialize a variable, but you can never overwrite. >... This notion is widely > used, much beyond Ada. You would likely confuse people by calling that > operation a "reassignment" and using "assignment" for anther kind of > operation. Please don't do that! Well, the word "reassignment" contains the word "assignment" -- it means "to assign again". I don't see why that would be confusing. Ada (to keep this slightly on topic ;-)) of course uses "assignment" for both kinds: initialization and assignment_statement. I find that confusing, because you'd think "assignment_statement" is the (only) thing that does "assignment". > Better use "assignment" for the overwriting operation, and another word > (such as "initial assignment", "constructive assignment", "inisignment", > ... a native English speaker might find better words) for the type of > assignment that does not overwrite an existing value, and which is allowed > for limited types. Thanks for the advice, but I'm not sure I'm convinced to take it. If we agree that there are two different kinds of things called "assignment" in common languages, I don't see why one should deserve the term "assignment" in preference to the other... - Bob ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-30 20:36 ` Robert A Duff @ 2009-11-30 23:54 ` (see below) 2009-12-01 12:13 ` Georg Bauhaus 1 sibling, 0 replies; 132+ messages in thread From: (see below) @ 2009-11-30 23:54 UTC (permalink / raw) On 30/11/2009 20:36, in article wcc8wdn697e.fsf@shell01.TheWorld.com, "Robert A Duff" <bobduff@shell01.TheWorld.com> wrote: > stefan-lucks@see-the.signature writes: >> Distinguishing the two different operations which are written as ":=" in >> Ada by using different words and even different symbols would make a lot of >> sense. > I definitely want to use different symbols as well as different words. > But I'm not sure what symbols to choose. Maybe := and :== . > > I want to allow: > > X : Integer; > > if ... then > X := 0; -- initialize (what I want to call "assign") > else > X := 1; -- initialize (what I want to call "assign") > end if; > > while ... loop > X :== X + 1; -- overwriting (what I want to call "reassign") > ... > end loop; What is wrong with the terms "initialize" and "assign", respectively? X : Integer initially if ... then 0 else 1 end if; Y : Integer constant if ... then 1 else 2 end if; while ... loop X := X + Y; -- assign ... end loop; -- Bill Findlay <surname><forename> chez blueyonder.co.uk ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-30 20:36 ` Robert A Duff 2009-11-30 23:54 ` (see below) @ 2009-12-01 12:13 ` Georg Bauhaus 2009-12-01 12:23 ` Georg Bauhaus ` (2 more replies) 1 sibling, 3 replies; 132+ messages in thread From: Georg Bauhaus @ 2009-12-01 12:13 UTC (permalink / raw) Robert A Duff schrieb: > stefan-lucks@see-the.signature writes: > >> On Sun, 22 Nov 2009, Robert A Duff wrote: >> >>> Ada uses the term "assignment" to refer to both "initial assignment / >>> initialization" and "assignment_statement / overwriting". >>> I'd prefer to use different symbols for the two. >>> We're not going to change Ada in that regard, for compatibility reasons, >>> but I'm thinking in my hobby language design to use the term "assignment" >>> for the initial one, and "reassignment" for the subsequent overwriting >>> one, and use different symbols for the two. >>> >>> So, for a limited type, "assignment" is legal, "reassignment" is not. >> Distinguishing the two different operations which are written as ":=" in >> Ada by using different words and even different symbols would make a lot of >> sense. > > I definitely want to use different symbols as well as different words. Am I correct in assuming that one important part of this argument is about referring to the value stored for some variable before this store has a "reasonable" value? (Where "reasonable" does not currently have a meaning that can be inferred from the LRM, I guess.) If so, and presuming the programming language Ada is very much, and explicitly, about storing and manipulating bits in registers, memory words, ... of digital computers in a strongly typed fashion: In this case I would know the use of being carried away by functional, uhm, phantasm, pardon the expression. Rather, why not have Ada turn warnings about "uninitialized" variables into a rule like Java's? Then we could rely on the language: compilers will detect uninitialized variables provided these do not have a pragma/keyword/... to say that uninitialized is what the programmer wants. Some fancy means to tell the compiler that this variable does indeed have a good first value like pragma Import. X : [constant] Car; -- default init, -- undefined, -- junk bits. Doesn't matter -- *no* pragma Import (Ada, X); begin Spare := X.Tire (5); -- would become illegal, -- as no value has been assigned yet. -- Currently, we get a warning if Fast then X := Ferrari.Make (...); else X := Fiat.Make (...); end if; Spare := X.Tire (5); Does the phrase "first value" make sense? ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 12:13 ` Georg Bauhaus @ 2009-12-01 12:23 ` Georg Bauhaus 2009-12-01 12:44 ` Georg Bauhaus 2009-12-01 13:48 ` Dmitry A. Kazakov 2009-12-01 23:51 ` Randy Brukardt 2 siblings, 1 reply; 132+ messages in thread From: Georg Bauhaus @ 2009-12-01 12:23 UTC (permalink / raw) Georg Bauhaus schrieb: > In this case I would know the use of being carried away by functional, > uhm, phantasm, pardon the expression. There is a "not" after "would", sorry. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 12:23 ` Georg Bauhaus @ 2009-12-01 12:44 ` Georg Bauhaus 0 siblings, 0 replies; 132+ messages in thread From: Georg Bauhaus @ 2009-12-01 12:44 UTC (permalink / raw) Georg Bauhaus schrieb: > Georg Bauhaus schrieb: > >> In this case I would know the use of being carried away by functional, >> uhm, phantasm, pardon the expression. > > There is a "not" after "would", sorry. It's missing in fact. I'll get something to eat... ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 12:13 ` Georg Bauhaus 2009-12-01 12:23 ` Georg Bauhaus @ 2009-12-01 13:48 ` Dmitry A. Kazakov 2009-12-01 15:02 ` Georg Bauhaus 2009-12-01 23:51 ` Randy Brukardt 2 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-01 13:48 UTC (permalink / raw) On Tue, 01 Dec 2009 13:13:29 +0100, Georg Bauhaus wrote: > Then we could rely on the language: compilers will detect > uninitialized variables provided these do not have a pragma/keyword/... > to say that uninitialized is what the programmer wants. > Some fancy means to tell the compiler that this variable > does indeed have a good first value like pragma Import. > > X : [constant] Car; -- default init, The error is here! > -- undefined, > -- junk bits. Doesn't matter > -- *no* pragma Import (Ada, X); > > begin > > Spare := X.Tire (5); -- would become illegal, Not here! ------------------------- Anyway, you cannot do that because: if HALT (P) then X := Z; end if; Y := X; -- Is this legal? > Does the phrase "first value" make sense? An object shall not have invalid values. All values are valid if the language is typed. Enforcing user-defined construction including prohibition of certain kinds of construction (e.g. per default constructor) is a different story. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 13:48 ` Dmitry A. Kazakov @ 2009-12-01 15:02 ` Georg Bauhaus 2009-12-01 16:18 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Georg Bauhaus @ 2009-12-01 15:02 UTC (permalink / raw) Dmitry A. Kazakov schrieb: > On Tue, 01 Dec 2009 13:13:29 +0100, Georg Bauhaus wrote: > >> Then we could rely on the language: compilers will detect >> uninitialized variables provided these do not have a pragma/keyword/... >> to say that uninitialized is what the programmer wants. >> Some fancy means to tell the compiler that this variable >> does indeed have a good first value like pragma Import. >> >> X : [constant] Car; -- default init, > > The error is here! >> -- undefined, >> -- junk bits. Doesn't matter >> -- *no* pragma Import (Ada, X); >> >> begin >> >> Spare := X.Tire (5); -- would become illegal, > > Not here! Why? Nothing needs to have happened in between the X's declaration and the first reference made to it. > ------------------------- > Anyway, you cannot do that because: > > if HALT (P) then > X := Z; > end if; > Y := X; -- Is this legal? (HALT is a run-time issue that has no impact here.) While this snippet would not be legal as is (on purpose!), Ada's case coverage rules can make the programmer write a legal program easily: write an else branch! The compiler can then decide that a value will be assigned in either branch. If nothing is to be assigned this can only be for the reason that the variable is imported, or has a value already. In the former case an unchecked conversion involving only the variable will do; syntactic sugar might be nice to have. if HALT (P) then X := Z; else !X; end if; One might even omit the else branch without loss when Ada forces saying that the variable is imported. >> Does the phrase "first value" make sense? > > An object shall not have invalid values. All values are valid if the > language is typed. Enforcing user-defined construction including > prohibition of certain kinds of construction (e.g. per default constructor) > is a different story. > If you feed this to a Java compiler you will see how it is done. The Java compiler will not accept a reference to a variable's component when the variable may not have been initialized. import java.math.BigInteger; public class Dummy { enum TireColor { Black, White }; class Tire { TireColor rim_color; } public static void main(String[] args) { Tire spare; TireColor its_color; final BigInteger P; // some program's number P = BigInteger.valueOf(Long.parseLong(args[0])); if (HALT(P)) { spare = new Tire(); } // this line not accepted by a Java compiler: its_color = spare.rim_color; // <----- } static boolean HALT(BigInteger gn) { // dummy if (gn.equals(BigInteger.ZERO)) return true; return HALT(gn.add(BigInteger.ONE)); } } ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 15:02 ` Georg Bauhaus @ 2009-12-01 16:18 ` Dmitry A. Kazakov 2009-12-01 17:52 ` Georg Bauhaus 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-01 16:18 UTC (permalink / raw) On Tue, 01 Dec 2009 16:02:21 +0100, Georg Bauhaus wrote: > Dmitry A. Kazakov schrieb: >> On Tue, 01 Dec 2009 13:13:29 +0100, Georg Bauhaus wrote: >> >>> Then we could rely on the language: compilers will detect >>> uninitialized variables provided these do not have a pragma/keyword/... >>> to say that uninitialized is what the programmer wants. >>> Some fancy means to tell the compiler that this variable >>> does indeed have a good first value like pragma Import. >>> >>> X : [constant] Car; -- default init, >> >> The error is here! >>> -- undefined, >>> -- junk bits. Doesn't matter >>> -- *no* pragma Import (Ada, X); >>> >>> begin >>> >>> Spare := X.Tire (5); -- would become illegal, >> >> Not here! > > Why? Because X is illegal right after begin: IF accessing X is illegal THEN the corresponding operation does not belong to the type of X THEN the type of X is not Car. q.e.d. (Provided, we are talking about a typed language) >> ------------------------- >> Anyway, you cannot do that because: >> >> if HALT (P) then >> X := Z; >> end if; >> Y := X; -- Is this legal? > > (HALT is a run-time issue that has no impact here.) If you cannot decide if X is "initialized", then you cannot decode whether the program is legal. However you could define some set of pragmatic rules with either many false positives or many false negatives, or even mixed. These rules will be most likely observed as arbitrary by laymen. I don't think the issue deserves this. > While this snippet would not be legal as is (on purpose!), > Ada's case coverage rules can make the programmer write a > legal program easily: write an else branch! And this one: procedure Foo (X : in out Car); ... begin Foo (X); Y := X; -- Is this legal? Probably, already the call to Foo is illegal? And if Foo were declared as procedure Foo (X : out Car); >>> Does the phrase "first value" make sense? >> >> An object shall not have invalid values. All values are valid if the >> language is typed. Enforcing user-defined construction including >> prohibition of certain kinds of construction (e.g. per default constructor) >> is a different story. > > If you feed this to a Java compiler you will see how it is done. > The Java compiler will not accept a reference to a variable's > component when the variable may not have been initialized. I consider this model wrong. It is better not to introduce inappropriate values rather than trying to catch them later. Java does not have constrained types, so I can understand why they go this way. I think it is better to ensure that a declared value is initialized at the declaration point. I also think that forward uninitialized declarations represent bad style, e.g.: function Foo (...) return Bar is Result : Bar; begin ... if ... then raise Baz; end if; ... Result := ...; ... return Result; end Foo; I understand the motivation to declare Result uninitialized (because we could leave Foo via an exception), but I don't like this. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 16:18 ` Dmitry A. Kazakov @ 2009-12-01 17:52 ` Georg Bauhaus 2009-12-01 18:47 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Georg Bauhaus @ 2009-12-01 17:52 UTC (permalink / raw) Dmitry A. Kazakov schrieb: > On Tue, 01 Dec 2009 16:02:21 +0100, Georg Bauhaus wrote: > >> Dmitry A. Kazakov schrieb: >>> On Tue, 01 Dec 2009 13:13:29 +0100, Georg Bauhaus wrote: >>> >>>> Then we could rely on the language: compilers will detect >>>> uninitialized variables provided these do not have a pragma/keyword/... >>>> to say that uninitialized is what the programmer wants. >>>> Some fancy means to tell the compiler that this variable >>>> does indeed have a good first value like pragma Import. >>>> >>>> X : [constant] Car; -- default init, >>> The error is here! >>>> -- undefined, >>>> -- junk bits. Doesn't matter >>>> -- *no* pragma Import (Ada, X); >>>> >>>> begin >>>> >>>> Spare := X.Tire (5); -- would become illegal, >>> Not here! >> Why? > > Because X is illegal right after begin: > > IF accessing X is illegal THEN the corresponding operation does not belong > to the type of X THEN the type of X is not Car. q.e.d. But the implications of this proof are purely formal, and not relevant before X is used. There is no way to perform an operation involving X in its own declaration. The difference in views would be that your laws say Don't create objects that could be used illegally if there were uses that can't be there, though, but for formal reasons. Whereas Java's ruling says (at compile time) Your program cannot be accepted because this object cannot be in a legal state here. The variable may not have been assigned a value. > (Provided, we are talking about a typed language) I think there is more in what you say than what is covered by the words "typed language"? >>> ------------------------- >>> Anyway, you cannot do that because: >>> >>> if HALT (P) then >>> X := Z; >>> end if; >>> Y := X; -- Is this legal? >> (HALT is a run-time issue that has no impact here.) > > If you cannot decide if X is "initialized", But I can decide whether (and when!) X is "initialized"! The compiler does not need to run HALT in order to see that X may not have been initialized when the condition is not true. The program is not accepted because it may lack an assignment to X before Y := X for much simpler reasons. In fact, SPARK marks it as error. I made a simple example, a procedure that does or does not assign depending on an unknown Boolean Condition parameter: 1 package body Asgn is 2 3 procedure Exmpl (Condition: Boolean; Result : out Integer) is 4 X : Integer; 5 begin 6 if Condition then 7 X := 42; 8 end if; 9 10 Result := X / 2; ^1 ??? ( 1) Flow Error :501: Expression contains reference(s) to variable X, which may have an undefined value. 11 end Exmpl; ??? ( 2) Flow Error :602: The undefined initial value of X may be used in the derivation of Result. 12 13 end Asgn; What the new rule would do is merge the two messages into one message about X that may not have a value yet: An "undefined initial value" MUST not be used like it is used on line 10. The (different) Ada language rule will forbid. >> While this snippet would not be legal as is (on purpose!), >> Ada's case coverage rules can make the programmer write a >> legal program easily: write an else branch! > > And this one: > > procedure Foo (X : in out Car); > ... > begin > Foo (X); > Y := X; -- Is this legal? Yes, this is legal, because Foo is called with X having been assigned a value. It cannot be otherwise, the recursion has to start somewhere. It can only start with an actual parameter that has a value (again, the compiler can check this). > And if Foo were declared as > > procedure Foo (X : out Car); We'd have roughly the same as this: X : Car; begin X := Foo_as_function; -- now X can be used I see no operational problem. Is there one? >>>> Does the phrase "first value" make sense? >>> An object shall not have invalid values. All values are valid if the >>> language is typed. Enforcing user-defined construction including >>> prohibition of certain kinds of construction (e.g. per default constructor) >>> is a different story. >> If you feed this to a Java compiler you will see how it is done. >> The Java compiler will not accept a reference to a variable's >> component when the variable may not have been initialized. > > I consider this model wrong. It is better not to introduce inappropriate > values rather than trying to catch them later. The Java rule works at compile time. No value is introduced at any time during compilation. Nothing to catch. A reference to a variable in source text is permitted if and only if the compiler can show, by following simple compile time rules, that a value has been assigned to the variable or constant prior to referencing. It need not evaluate calls (i.e. run the program) to arrive at a decision. > Java does not have > constrained types, so I can understand why they go this way. Ehm, I don't see the connection here. Which one is it? When I declare X : Some_Type(Some_Constraint); begin -- X may need further "initilization", and assigments, since -- Some_Type is an "open minded" type of a varying nature, -- not a fixed value. Its objects accumulate values. > I also think that forward uninitialized declarations represent bad > style, e.g.: > > function Foo (...) return Bar is > Result : Bar; > begin > ... > if ... then > raise Baz; > end if; > ... > Result := ...; > ... > return Result; > end Foo; > > I understand the motivation to declare Result uninitialized (because we > could leave Foo via an exception), but I don't like this. > But assigning the first value when declaring X won't help when the initialization can raise exceptions. How could this change? When the body of Foo gradually operates on Result to produce its final state (in a safe way because no reference can be made to Result or its parts without prior assignments), why should I pretend that any valid initial value for Result, provided by an initialization expression, is better than its default value? (Assuming that I cannot refer to "invalid" components of objects because the compiler will simply reject a program that might try.) ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 17:52 ` Georg Bauhaus @ 2009-12-01 18:47 ` Dmitry A. Kazakov 2009-12-01 21:53 ` John B. Matthews 2009-12-02 1:13 ` Georg Bauhaus 0 siblings, 2 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-01 18:47 UTC (permalink / raw) On Tue, 01 Dec 2009 18:52:15 +0100, Georg Bauhaus wrote: > Dmitry A. Kazakov schrieb: >> On Tue, 01 Dec 2009 16:02:21 +0100, Georg Bauhaus wrote: >> >>> Dmitry A. Kazakov schrieb: >>>> On Tue, 01 Dec 2009 13:13:29 +0100, Georg Bauhaus wrote: >>>> >>>>> Then we could rely on the language: compilers will detect >>>>> uninitialized variables provided these do not have a pragma/keyword/... >>>>> to say that uninitialized is what the programmer wants. >>>>> Some fancy means to tell the compiler that this variable >>>>> does indeed have a good first value like pragma Import. >>>>> >>>>> X : [constant] Car; -- default init, >>>> The error is here! >>>>> -- undefined, >>>>> -- junk bits. Doesn't matter >>>>> -- *no* pragma Import (Ada, X); >>>>> >>>>> begin >>>>> >>>>> Spare := X.Tire (5); -- would become illegal, >>>> Not here! >>> Why? >> >> Because X is illegal right after begin: >> >> IF accessing X is illegal THEN the corresponding operation does not belong >> to the type of X THEN the type of X is not Car. q.e.d. > > But the implications of this proof are purely formal, > and not relevant before X is used. They are relevant to the declaration of X. It cannot be declared of Car, if it is not. > There is no way to perform an operation involving X in > its own declaration. But it can be used right after the declaration. > The difference in views would be that your laws say Don't > create objects that could be used illegally if there > were uses that can't be there, though, but for formal reasons. > Whereas Java's ruling says (at compile time) Your program > cannot be accepted because this object cannot be in > a legal state here. No, Java says, that it self failed to prove that this object is in a state the programmer might want. This is an absolutely informal statement, because Java cannot have any idea about what the programmer actually wanted. The only basis for reasoning might be the object type. But that tells nothing. So Java speculates that the default constructor is somewhat worse than copy constructor. Why does it so? Did programmer told this the compiler? No he didn't. Yes, it might be the case, but then why not to allow the programmer to say exactly this: do not allow default constructors for this type? I would even make this a default. E.g. if the programmer does not explicitly allow default constructors they are forbidden. So X : T; -- Is always illegal unless I do some actions >> (Provided, we are talking about a typed language) > > I think there is more in what you say than what is covered > by the words "typed language"? properly typed language! (:-)) >> And this one: >> >> procedure Foo (X : in out Car); >> ... >> begin >> Foo (X); >> Y := X; -- Is this legal? > > Yes, this is legal, because Foo is called with X having been > assigned a value. But Foo might read X in its body before updating it. It can leave it untouched etc. >> And if Foo were declared as >> >> procedure Foo (X : out Car); > > We'd have roughly the same as this: > > X : Car; > begin > X := Foo_as_function; -- now X can be used > > I see no operational problem. Is there one? There is one, Foo might leave X unchanged, unless you introduce further special rules for out parameters. It will be interesting: begin begin Foo (X); exception when Baz => null; end; Y := X; -- Is this legal? Ada does not specify what happens with out parameters updated before an exception gets raised in the body of Foo: procedure Foo (X : out Car) is begin if HALT (p) then raise Baz; -- Is this legal? else X := Merzedes; end if; end Foo; >>>>> Does the phrase "first value" make sense? >>>> An object shall not have invalid values. All values are valid if the >>>> language is typed. Enforcing user-defined construction including >>>> prohibition of certain kinds of construction (e.g. per default constructor) >>>> is a different story. >>> If you feed this to a Java compiler you will see how it is done. >>> The Java compiler will not accept a reference to a variable's >>> component when the variable may not have been initialized. >> >> I consider this model wrong. It is better not to introduce inappropriate >> values rather than trying to catch them later. > > The Java rule works at compile time. No value is introduced at any > time during compilation. Nothing to catch. Of course there is something to catch. The compiler has to do this. So the question is at which cost, how many false positives and negatives it will find? How scalable is this feature for more elaborated types? >> Java does not have >> constrained types, so I can understand why they go this way. > > Ehm, I don't see the connection here. Which one is it? > > When I declare > > X : Some_Type(Some_Constraint); > begin > -- X may need further "initilization", and assigments, since > -- Some_Type is an "open minded" type of a varying nature, > -- not a fixed value. Its objects accumulate values. I mean constraints in a wider sense. For example: Some_Time (<>) e.g. a subtype that would require explicit initialization. >> I also think that forward uninitialized declarations represent bad >> style, e.g.: >> >> function Foo (...) return Bar is >> Result : Bar; >> begin >> ... >> if ... then >> raise Baz; >> end if; >> ... >> Result := ...; >> ... >> return Result; >> end Foo; >> >> I understand the motivation to declare Result uninitialized (because we >> could leave Foo via an exception), but I don't like this. > > But assigning the first value when declaring X won't help > when the initialization can raise exceptions. How could this change? I don't follow you. My example illustrated a situation where an uninitialized value might be an advantage, because one possible outcome of Foo is exception propagation, in which case leaving Result raw could save some vital nanoseconds of execution time. I don't buy this. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 18:47 ` Dmitry A. Kazakov @ 2009-12-01 21:53 ` John B. Matthews 2009-12-02 0:32 ` Georg Bauhaus 2009-12-02 1:13 ` Georg Bauhaus 1 sibling, 1 reply; 132+ messages in thread From: John B. Matthews @ 2009-12-01 21:53 UTC (permalink / raw) In article <wof0ewwkyzy0$.13398rnn8cmje$.dlg@40tude.net>, "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote: > On Tue, 01 Dec 2009 18:52:15 +0100, Georg Bauhaus wrote: > > > Dmitry A. Kazakov schrieb: > >> On Tue, 01 Dec 2009 16:02:21 +0100, Georg Bauhaus wrote: [...] > >>> If you feed this to a Java compiler you will see how it is done. > >>> The Java compiler will not accept a reference to a variable's > >>> component when the variable may not have been initialized. > >> > >> I consider this model wrong. It is better not to introduce > >> inappropriate values rather than trying to catch them later. > > > > The Java rule works at compile time. No value is introduced at any > > time during compilation. Nothing to catch. > > Of course there is something to catch. The compiler has to do this. > So the question is at which cost, how many false positives and > negatives it will find? How scalable is this feature for more > elaborated types? If I may amplify on this, the Java compiler rejects the assignment to its_color because it's a local variable, which must have an explicit value before use [1]. Once Tire's constructor completes, the value of spare.rim_color has the default value null [1]. The compiler need only check that its_color has been assigned in the current scope [2]. In Georg Bauhaus' example, the constructor is invoked in a nested scope. In Ada, I get a warning that '"Spare" is read but never assigned.' type Tire_Color is (Black, White); type Tire is record Rim_Color : Tire_Color; end record; Spare : Tire; If I throw in "for Tire_Color use (Black => 1, White => 2)," the implicit initial value [3] of Rim_Color is not valid for Tire_Color. It's "a bounded error to evaluate the value of such an object [4]," and I get CONSTRAINT_ERROR at run-time. The Java compiler doesn't warn that spare.rim_color is null by default; the Ada compiler doesn't warn that Spare.Rim_Color is invalid by default. In either language, I have to either accept the default initial values or specify them. I sense I'm missing something. [1]<http://java.sun.com/docs/books/jls/third_edition/html/typesValues.htm l#4.12.5> [2]<http://java.sun.com/docs/books/jls/third_edition/html/defAssign.html# 25979> [2]<http://www.adaic.com/standards/05rm/html/RM-3-3-1.html> [3]<http://www.adaic.com/standards/05rm/html/RM-13-9-1.html> -- John B. Matthews trashgod at gmail dot com <http://sites.google.com/site/drjohnbmatthews> ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 21:53 ` John B. Matthews @ 2009-12-02 0:32 ` Georg Bauhaus 2009-12-02 11:18 ` John B. Matthews 0 siblings, 1 reply; 132+ messages in thread From: Georg Bauhaus @ 2009-12-02 0:32 UTC (permalink / raw) On 12/1/09 10:53 PM, John B. Matthews wrote: > If I may amplify on this, the Java compiler rejects the assignment to > its_color because it's a local variable, which must have an explicit > value before use [1]. Once Tire's constructor completes, the value of > spare.rim_color has the default value null [1]. The compiler need only > check that its_color has been assigned in the current scope [2]. In > Georg Bauhaus' example, the constructor is invoked in a nested scope. > > In Ada, I get a warning that '"Spare" is read but never assigned.' > > type Tire_Color is (Black, White); > type Tire is record > Rim_Color : Tire_Color; > end record; > Spare : Tire; > > If I throw in "for Tire_Color use (Black => 1, White => 2)," the > implicit initial value [3] of Rim_Color is not valid for Tire_Color. > It's "a bounded error to evaluate the value of such an object [4]," and > I get CONSTRAINT_ERROR at run-time. > > The Java compiler doesn't warn that spare.rim_color is null by default; > the Ada compiler doesn't warn that Spare.Rim_Color is invalid by > default. In either language, I have to either accept the default initial > values or specify them. I sense I'm missing something. The Java rule I had been thinking of starts from less emphasis on what the default initial (Ada) value of a (local) variable might be, given current Ada rules. Rather, in the sequence of statements below the compiler would just not accept the reference to the .Rim_Color component of Spare. The meaning of the declaration Spare : Tire needs to be understood as slightly changed, to exclude (or ignore) default initialization. Spare : Tire; begin -- Here, whatever Spare is, or whichever side effects its -- declaration may have, it is not used between -- its declaration and the line following the if statement. -- Therefore, we are free to think of it as something -- or as nothing, or as something to become a Tire when -- necessary. A virtual object, perhaps. (Otherwise, use -- syntax to indicate that there is something important -- going on in default init; or, for compatibility, when -- nothing important is going on.) if Some_Condition then Spare := Make_a_Tire; end if; Its_Color := Spare.Rim_Color; -- illegal A simple rule would now be a copy of the Java rule which is quoted below. Just assume that Spare has no value. Just like the last line is not accepted by SPARK or by Java (the corresponding Java source line). The warning which some Ada compilers will issue (that Spare may not have been assigned a value) is then turned into an error. As might be expected in Ada, some syntax might be in order to say that default initialization does provide an initial value that warrants the safe use of the variable after the if statement (or is needed for its side effects, but this is another story, I guess). Another case is when a declared variable is used in a declaration of another variable following it, Spare : Tire; Another : Tire := Spare; -- might become illegal begin ... Illegal unless it is specified that Spare does have a valid Tire value. Or, oddly, that Another "inherits" the unknown state of Spare WRT being initialized or not. This is the Java rule I had in mind. I found it thanks to the link you have supplied: "A Java compiler must carry out a specific conservative flow analysis to make sure that, for every access of a local variable or blank final field f, f is definitely assigned before the access; otherwise a compile-time error must occur." > [2]<http://java.sun.com/docs/books/jls/third_edition/html/defAssign.html# > 25979> ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-02 0:32 ` Georg Bauhaus @ 2009-12-02 11:18 ` John B. Matthews 2009-12-02 14:29 ` Jean-Pierre Rosen 0 siblings, 1 reply; 132+ messages in thread From: John B. Matthews @ 2009-12-02 11:18 UTC (permalink / raw) In article <4b15b59b$0$7632$9b4e6d93@newsspool1.arcor-online.net>, Georg Bauhaus <rm-host.bauhaus@maps.futureapps.de> wrote: [...] > The Java rule I had been thinking of starts from less emphasis on > what the default initial (Ada) value of a (local) variable might be, > given current Ada rules. Rather, in the sequence of statements below > the compiler would just not accept the reference to the .Rim_Color > component of Spare. The meaning of the declaration Spare : Tire > needs to be understood as slightly changed, to exclude (or ignore) > default initialization. > > Spare : Tire; > begin > -- Here, whatever Spare is, or whichever side effects its > -- declaration may have, it is not used between > -- its declaration and the line following the if statement. > -- Therefore, we are free to think of it as something > -- or as nothing, or as something to become a Tire when > -- necessary. A virtual object, perhaps. (Otherwise, use > -- syntax to indicate that there is something important > -- going on in default init; or, for compatibility, when > -- nothing important is going on.) > > if Some_Condition then > Spare := Make_a_Tire; > end if; > Its_Color := Spare.Rim_Color; -- illegal > > > A simple rule would now be a copy of the Java rule which is > quoted below. Just assume that Spare has no value. > Just like the last line is not accepted by SPARK or > by Java (the corresponding Java source line). The warning > which some Ada compilers will issue (that Spare may not have > been assigned a value) is then turned into an error. Ah, I see. Nothing in the present Ada specification _requires_ such a warning. > As might be expected in Ada, some syntax might be in > order to say that default initialization does > provide an initial value that warrants the safe use of the > variable after the if statement (or is needed for its > side effects, but this is another story, I guess). > > Another case is when a declared variable is used in > a declaration of another variable following it, > > Spare : Tire; > Another : Tire := Spare; -- might become illegal > begin > ... > > Illegal unless it is specified that Spare does have > a valid Tire value. Or, oddly, that Another "inherits" the > unknown state of Spare WRT being initialized or not. > > This is the Java rule I had in mind. I found it thanks to the > link you have supplied: > "A Java compiler must carry out a specific conservative flow > analysis to make sure that, for every access of a local > variable or blank final field f, f is definitely assigned > before the access; otherwise a compile-time error must occur." I can see the appeal of adding such an analysis to Ada. <http://java.sun.com/docs/books/jls/third_edition/html/defAssign.html> -- John B. Matthews trashgod at gmail dot com <http://sites.google.com/site/drjohnbmatthews> ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-02 11:18 ` John B. Matthews @ 2009-12-02 14:29 ` Jean-Pierre Rosen 2009-12-02 15:35 ` Georg Bauhaus 0 siblings, 1 reply; 132+ messages in thread From: Jean-Pierre Rosen @ 2009-12-02 14:29 UTC (permalink / raw) John B. Matthews a écrit : >> This is the Java rule I had in mind. I found it thanks to the >> link you have supplied: >> "A Java compiler must carry out a specific conservative flow >> analysis to make sure that, for every access of a local >> variable or blank final field f, f is definitely assigned >> before the access; otherwise a compile-time error must occur." > > I can see the appeal of adding such an analysis to Ada. > > <http://java.sun.com/docs/books/jls/third_edition/html/defAssign.html> > Unless it has changed recently (it is sometimes since I updated my Java education), the Java solution is not bullet-proof: you can still access variables before initialization from constructors. Note that if the Java people were confident in their rule, there would be no need to require that every variable be initialized to zero! -- --------------------------------------------------------- J-P. Rosen (rosen@adalog.fr) Visit Adalog's web site at http://www.adalog.fr ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-02 14:29 ` Jean-Pierre Rosen @ 2009-12-02 15:35 ` Georg Bauhaus 0 siblings, 0 replies; 132+ messages in thread From: Georg Bauhaus @ 2009-12-02 15:35 UTC (permalink / raw) Jean-Pierre Rosen schrieb: > Note that if the Java > people were confident in their rule, there would be no need to require > that every variable be initialized to zero! A local variable, unlike the others, is not initialized to zero. But the "assigned before use" rule applies to local variables. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 18:47 ` Dmitry A. Kazakov 2009-12-01 21:53 ` John B. Matthews @ 2009-12-02 1:13 ` Georg Bauhaus 2009-12-02 9:07 ` Dmitry A. Kazakov 1 sibling, 1 reply; 132+ messages in thread From: Georg Bauhaus @ 2009-12-02 1:13 UTC (permalink / raw) On 12/1/09 7:47 PM, Dmitry A. Kazakov wrote: > It cannot be declared of Car, if it is not. Which brings us (or me, at least) to the problem of whether or not the meaning of an Ada program (or a sequential subprogram) will change if object X really "is" after "begin" or whether its physical existence is allowed start just before actual use. :-) X might show its existence through side effects of its default initialization. Rather implicit if the effect is important. >> There is no way to perform an operation involving X in >> its own declaration. > > But it can be used right after the declaration. Sure, but it isn't used, and the compiler will make sure it isn't unless it has a value. > why not to > allow the programmer to say exactly this: do not allow default constructors > for this type? I would even make this a default. E.g. if the programmer > does not explicitly allow default constructors they are forbidden. So > > X : T; -- Is always illegal unless I do some actions Or maybe this could mean that if there is no explict specification of actions associated with the declaration, X must be considered uninitialized in the following text. Some syntax might be nice to have. > Ada does not specify what happens with out parameters updated before an > exception gets raised in the body of Foo: > > procedure Foo (X : out Car) is > begin > if HALT (p) then > raise Baz; -- Is this legal? > else > X := Merzedes; > end if; > end Foo; If we were to integrate exceptions into normal control flow like Java does? Here is what Java does when the constructor may raise an exception (taking the role of Foo above): if (some_condition) { try { spare = new Dummy.Tire(); } catch (Exception e) { ; } // this line not accepted by a Java compiler: its_color = spare.rim_color; // <----- } // this line not accepted by a Java compiler: its_color = spare.rim_color; // <----- > Of course there is something to catch. The compiler has to do this. So the > question is at which cost, how many false positives and negatives it will > find? How scalable is this feature for more elaborated types? From the Java point of view, there are no false positives or negatives. The rule is pretty clear, I think. Assuming that Ada programmers will be able to say "is initialized" for just a variable declaration, there should not be any false positives or negatives. There could be more reliance on the programmer, though, not sure. > My example illustrated a situation where an > uninitialized value might be an advantage, because one possible outcome of > Foo is exception propagation, in which case leaving Result raw could save > some vital nanoseconds of execution time. I don't buy this. Whether the out mode variable had been initialized in the body or not, the exceptional situation might have destroyed the value of the out mode parameter. A variable would not be considered initialized, I'd think, in an exception handler, unless the programmer says so (or assigns a value). ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-02 1:13 ` Georg Bauhaus @ 2009-12-02 9:07 ` Dmitry A. Kazakov 2009-12-02 12:35 ` John B. Matthews 2009-12-03 5:29 ` Randy Brukardt 0 siblings, 2 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-02 9:07 UTC (permalink / raw) On Wed, 02 Dec 2009 02:13:14 +0100, Georg Bauhaus wrote: > On 12/1/09 7:47 PM, Dmitry A. Kazakov wrote: > >> It cannot be declared of Car, if it is not. > > Which brings us (or me, at least) to the problem of whether or > not the meaning of an Ada program (or a sequential subprogram) > will change if object X really "is" after "begin" or whether > its physical existence is allowed start just before actual > use. :-) X might show its existence through side effects > of its default initialization. > Rather implicit if the effect is important. Ada rules are rather complex, the object exists right after its declaration, but its existence is somewhat sleepy, a larva, not yet the butterfly: Consider the following: task type A is entry Hey; end A; type T (Greeting : access A) is new Ada.Finalization.Limited_Controlled with null record; overriding procedure Initialize (X : in out T); task body A is begin accept Hey; end A; procedure Initialize (X : in out T) is begin X.Greeting.Hey; end Initialize; With the above: declare X : aliased A; Y : T (X'Access); -- This is a deadlock! begin The object X (a task) is not completely initialized when Y's Initialize is called. This is one of nasty problems with tasks as objects. And in general I think that many feel uncomfortable with Ada declarations being executable. I have no clear idea how to approach this problem, though. >>> There is no way to perform an operation involving X in >>> its own declaration. >> >> But it can be used right after the declaration. > > Sure, but it isn't used, and the compiler will make sure > it isn't unless it has a value. That erodes the concept of scope, which idea is that the object is usable anywhere within its scope. >> Ada does not specify what happens with out parameters updated before an >> exception gets raised in the body of Foo: >> >> procedure Foo (X : out Car) is >> begin >> if HALT (p) then >> raise Baz; -- Is this legal? >> else >> X := Merzedes; >> end if; >> end Foo; > > If we were to integrate exceptions into normal control > flow like Java does? Contracted exceptions is a way to move the check down the code. Consider an invalid initial value, which is legal. Its validness is rather determined by outcome of some operations, e.g. reading it causes Constraint_Error. Then, with exception contracts, if you declared the program as not raising Constraint_Error, then the program with an invalid initial value could become illegal if Constraint_Error is not caught: declare raise no Constraint_Error; -- Imaginary syntax X : Car; begin Print (X.Tire); end; -- Illegal, this block might rise Constraint_Error declare raise no Constraint_Error; X : Valid_Car; -- Illegal require initialization by a valid value begin Print (X.Tire); end; -- Legal now, but fails at the declaration point declare raise no Constraint_Error; X : Valid_Car := My_Car; -- A valid value begin Print (X.Tire); end; -- No Constraint_Error >> Of course there is something to catch. The compiler has to do this. So the >> question is at which cost, how many false positives and negatives it will >> find? How scalable is this feature for more elaborated types? > > From the Java point of view, there are no false positives > or negatives. Of course there are, because it is a halting problem. So you need some optimistic or pessimistic estimation of: if False then Print (X.Tire); -- Is this illegal? If yes, that is a false positive end if; [Also see Randy's points] >> My example illustrated a situation where an >> uninitialized value might be an advantage, because one possible outcome of >> Foo is exception propagation, in which case leaving Result raw could save >> some vital nanoseconds of execution time. I don't buy this. > > Whether the out mode variable had been initialized in the > body or not, the exceptional situation might have destroyed > the value of the out mode parameter. A variable would not > be considered initialized, I'd think, in an exception handler, > unless the programmer says so (or assigns a value). No the problem is whether to treat a call to the body as legal. The problem is that you could not rely on its contract (out Car), in order to be able to decide whether this call would "initialize" the object. BTW, you consider ":=" as an initialization, but why? Where is a guaranty that it initializes the object by a "valid" value? I feel that very concept is somewhat inconsistent with ADT. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-02 9:07 ` Dmitry A. Kazakov @ 2009-12-02 12:35 ` John B. Matthews 2009-12-02 13:35 ` Dmitry A. Kazakov 2009-12-03 5:23 ` Randy Brukardt 2009-12-03 5:29 ` Randy Brukardt 1 sibling, 2 replies; 132+ messages in thread From: John B. Matthews @ 2009-12-02 12:35 UTC (permalink / raw) In article <1jcbtmi5rztyp$.norvlhez9i9$.dlg@40tude.net>, "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote: Georg Bauhaus wrote: > > From the Java point of view, there are no false positives > > or negatives. > > Of course there are, because it is a halting problem. So you need > some optimistic or pessimistic estimation of: > > if False then > Print (X.Tire); -- Is this illegal? If yes, that is a false positive > end if; It would be indeterminate but for doing "a specific conservative flow analysis." In the specific cases of boolean constant expressions and if statements, if (true) { spare = new Tire(); } // spare assigned if (1 == 1) { spare = new Tire(); } // spare assigned if (false) { spare = new Tire(); } // spare unassigned if (1 == 2) { spare = new Tire(); } // spare unassigned if (always()) { spare = new Tire(); } // spare unassigned static boolean always() { return true; } In particular, spare remains unassigned even though the function always() is always true. In Ada, a certain compiler may warn 'variable "N" is assigned but never read' for the following: N : Integer; if False then N := 1; end if; I'm not especially advocating applying the Java rules to Ada, but I like the warning. <http://java.sun.com/docs/books/jls/third_edition/html/defAssign.html> -- John B. Matthews trashgod at gmail dot com <http://sites.google.com/site/drjohnbmatthews> ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-02 12:35 ` John B. Matthews @ 2009-12-02 13:35 ` Dmitry A. Kazakov 2009-12-03 5:23 ` Randy Brukardt 1 sibling, 0 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-02 13:35 UTC (permalink / raw) On Wed, 02 Dec 2009 07:35:39 -0500, John B. Matthews wrote: > In article <1jcbtmi5rztyp$.norvlhez9i9$.dlg@40tude.net>, > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote: > > Georg Bauhaus wrote: > >>> From the Java point of view, there are no false positives >>> or negatives. >> >> Of course there are, because it is a halting problem. So you need >> some optimistic or pessimistic estimation of: >> >> if False then >> Print (X.Tire); -- Is this illegal? If yes, that is a false positive >> end if; > > It would be indeterminate but for doing "a specific conservative flow > analysis." In the specific cases of boolean constant expressions and if > statements, > > if (true) { spare = new Tire(); } // spare assigned > if (1 == 1) { spare = new Tire(); } // spare assigned > if (false) { spare = new Tire(); } // spare unassigned > if (1 == 2) { spare = new Tire(); } // spare unassigned > if (always()) { spare = new Tire(); } // spare unassigned > > static boolean always() { return true; } > > In particular, spare remains unassigned even though the function > always() is always true. > > In Ada, a certain compiler may warn 'variable "N" is assigned but never > read' for the following: > > N : Integer; > if False then N := 1; end if; > > I'm not especially advocating applying the Java rules to Ada, but I like > the warning. Yes, warning is nice. The problem is with making it an error as Georg suggests (and Java does). An error (and its complement "no error") require much more careful definition. "Initialized" is too shaky for that, but for all I find "unassigned" inconsistent with strong typing. A typed object is always assigned because it cannot have any values other than of its type. If we wanted an object to have *certain* values, e.g. "any value different from the default one", then a naive analysis a-la Java is too little to me. I would prefer full blown pre-, postconditions and invariants for such a case. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-02 12:35 ` John B. Matthews 2009-12-02 13:35 ` Dmitry A. Kazakov @ 2009-12-03 5:23 ` Randy Brukardt 2009-12-03 20:21 ` John B. Matthews 1 sibling, 1 reply; 132+ messages in thread From: Randy Brukardt @ 2009-12-03 5:23 UTC (permalink / raw) "John B. Matthews" <nospam@nospam.invalid> wrote in message news:nospam-9C9974.07353802122009@news.aioe.org... ... > I'm not especially advocating applying the Java rules to Ada, but I like > the warning. Warnings have no semantic impact in Ada. There are a few places in the standard that suggest that warnings be generated, but there is no ideas whatsoever what that might mean. It is purely a quality-of-implementation issue -- and that is between you and your Ada vendor. Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-03 5:23 ` Randy Brukardt @ 2009-12-03 20:21 ` John B. Matthews 0 siblings, 0 replies; 132+ messages in thread From: John B. Matthews @ 2009-12-03 20:21 UTC (permalink / raw) In article <hf7i10$enn$1@munin.nbi.dk>, "Randy Brukardt" <randy@rrsoftware.com> wrote: > "John B. Matthews" <nospam@nospam.invalid> wrote in message > news:nospam-9C9974.07353802122009@news.aioe.org... > ... > > I'm not especially advocating applying the Java rules to Ada, but I > > like the warning. > > Warnings have no semantic impact in Ada. There are a few places in > the standard that suggest that warnings be generated, but there is no > ideas whatsoever what that might mean. It is purely a quality-of- > implementation issue -- and that is between you and your Ada vendor. Few indeed: ARM 1.1.1, 1.1.5, 13.5.2, 2.8, M.3! This is a virtue in Ada. When reviewing code, my own or others', I'll often use -gnatwa or -Xlint or -Wall or similar. Even if the warning does not represent a genuine oversight, it may suggest the need for a clarifying comment. -- John B. Matthews trashgod at gmail dot com <http://sites.google.com/site/drjohnbmatthews> ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-02 9:07 ` Dmitry A. Kazakov 2009-12-02 12:35 ` John B. Matthews @ 2009-12-03 5:29 ` Randy Brukardt 2009-12-03 11:24 ` Georg Bauhaus 1 sibling, 1 reply; 132+ messages in thread From: Randy Brukardt @ 2009-12-03 5:29 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:1jcbtmi5rztyp$.norvlhez9i9$.dlg@40tude.net... ... > No the problem is whether to treat a call to the body as legal. The > problem > is that you could not rely on its contract (out Car), in order to be able > to decide whether this call would "initialize" the object. > > BTW, you consider ":=" as an initialization, but why? Where is a guaranty > that it initializes the object by a "valid" value? I feel that very > concept > is somewhat inconsistent with ADT. To expand on this point, there are a number of places in Ada where objects can become "deinitialized". (Compilation strategies can prevent some of these, but at a cost of more checks with the associated loss of performance. For instance, Janus/Ada prevents Unchecked_Conversion from deinitializing scalar objects -- but to do so requires that range/validity checks be done on the results of some conversions -- hardly "unchecked"). Other places (such as the result of Stream'Read and the results of Sequential_IO.Read for composite types, and especially abort) can't be eliminated in any reasonable way. If you really care about this topic, I suggest you read and reread 13.9.1 until you understand it. (Then join the ARG and explain it to the rest of us. ;-)! This is not at all a simple topic, and the sort of simple rule used by Java does little other than give a false sense of security (and a lot of annoying errors in code that has no problems). Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-03 5:29 ` Randy Brukardt @ 2009-12-03 11:24 ` Georg Bauhaus 2009-12-03 23:08 ` Randy Brukardt 0 siblings, 1 reply; 132+ messages in thread From: Georg Bauhaus @ 2009-12-03 11:24 UTC (permalink / raw) Randy Brukardt schrieb: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:1jcbtmi5rztyp$.norvlhez9i9$.dlg@40tude.net... > ... >> No the problem is whether to treat a call to the body as legal. The >> problem >> is that you could not rely on its contract (out Car), in order to be able >> to decide whether this call would "initialize" the object. > [...] > This is not at all a simple topic, and the sort of simple rule used > by Java does little other than give a false sense of security (and a lot of > annoying errors in code that has no problems). After reading your recent hints to compatibility and single pass compilation preventing flow analysis for legality decisions, I noticed the possibility of a restrictions pragma, too. Is it really annoying when programmers are forced to state, explicitly, what they seem to be knowing? That is, for example, "I know that no ELSE is missing in the following code, therefore I have omitted a no-op else branch" X : Some_Type; begin if I_Know_What_This_Does (P) then X := Assign_It; end if; Use_It := Involve (X); When I try to change a program, I find it annoying to *not* see an ELSE branch that does then become a missing ELSE branch. The desired change turns a one-way flow into a blind alley... The code typically looks like this: X : Some_Type; begin if Has_Worked_For_Me (P) then X := Assign_it; end if; I_Can_Use_It := Involve (X); -- (formally fine in Ada, though not in Java or SPARK) The incompleteness of information here can be avoided, to some extent I think, and that's the reason I find the Java rule to be helpful, or the Ada warning, or the pragma which you have suggested. Not really annoying. I can no longer like seeing only half of the cases of Boolean covered in an IF, explicitly. To me, SPARK is not an annoyance when it refuses to accept the above code, since flow analysis reveals the possibility of missing initilization. The Ada programmer may well have decided the halting question for I_Know_What_This_Does or Has_Worked_For_Me. However, I'd sure like to be hinted to his reasons and not just take inspiration from a missing ELSE branch. Could a compiler make some good use of code like the following as a minimal way to express the programmer's valuable knowledge? Or some less intrusive (annoying?) pragma or syntax or ...? X : Some_Type; begin if Whatever (P) then X := Assign_it; else -- Stupid Ada configuration requires this. -- Yeah, X is valid because ... -- ... Whatever (P) is always true, you know. pragma Assert (Some_Type'Valid (X)); null; end if; ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-03 11:24 ` Georg Bauhaus @ 2009-12-03 23:08 ` Randy Brukardt 2009-12-04 8:52 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Randy Brukardt @ 2009-12-03 23:08 UTC (permalink / raw) "Georg Bauhaus" <rm.dash-bauhaus@futureapps.de> wrote in message news:4b179ffb$0$6591$9b4e6d93@newsspool3.arcor-online.net... > Randy Brukardt schrieb: >> [...] >> This is not at all a simple topic, and the sort of simple rule used >> by Java does little other than give a false sense of security (and a lot >> of >> annoying errors in code that has no problems). > > After reading your recent hints to compatibility and single > pass compilation preventing flow analysis for legality decisions, > I noticed the possibility of a restrictions pragma, too. > > Is it really annoying when programmers are forced to state, > explicitly, what they seem to be knowing? That is, for example, > "I know that no ELSE is missing in the following code, > therefore I have omitted a no-op else branch" > > X : Some_Type; > begin > if I_Know_What_This_Does (P) then > X := Assign_It; > end if; > > Use_It := Involve (X); > > When I try to change a program, I find it annoying to > *not* see an ELSE branch that does then become a missing ELSE > branch. The desired change turns a one-way flow into a blind alley... Compatibility again would cause problems for a general rule; a restriction could be stronger. It should be noted that our local coding standard would consider the above incorrect; there always must be an else branch or at least a comment explaining why there is no else branch: if I_Know_What_This_Does (P) then X := Assign_It; -- else can't happen as P must be valid - previously checked. end if; Although I generally prefer: if I_Know_What_This_Does (P) then X := Assign_It; else raise Program_Error; -- Can't happen as P must be valid - previously checked. end if; Or in our compiler: if I_Know_What_This_Does (P) then X := Assign_It; else J2Trace.Internal_Error ("P must be valid"); -- Often including P in the message. end if; ... > The incompleteness of information here can be avoided, > to some extent I think, and that's the reason I find > the Java rule to be helpful, or the Ada warning, or the > pragma which you have suggested. > Not really annoying. I can no longer like seeing only half > of the cases of Boolean covered in an IF, explicitly. Ada has generally left this to coding standards. I'm sure Jean-Pierre will be happy to tell us that Ada-Control has options for enforcing the existence of ELSE branches. One could imagine providing such possibilities within the language, but it is fairly rare that we've done that. That's partially because Restrictions (almost always) apply to the entire partition, including the runtime libraries. But independently developed subsystems (like the runtime system or Claw or GTKAda or AWS) are likely to have used different coding standards. And rewriting them to *your* coding standard is busy work at best, and a great way to introduce bugs at worst. Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-03 23:08 ` Randy Brukardt @ 2009-12-04 8:52 ` Dmitry A. Kazakov 2009-12-05 2:45 ` Randy Brukardt 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-04 8:52 UTC (permalink / raw) On Thu, 3 Dec 2009 17:08:01 -0600, Randy Brukardt wrote: > It should be noted that our local coding standard would consider the above > incorrect; there always must be an else branch or at least a comment > explaining why there is no else branch: What about: if Read_Error (File) then raise Data_Error; end if; or if Estimated_Error <= Threshold then return Estimated_Result; end if; or "exit when", which is a hidden if without else. All these cases represent some lengthy action, maybe iterated or recursive. The action is left upon some condition. If not senseless "else null;", then semantically, under "else" there must be the rest of the action (recursive, infinite). Maybe "if" with two alternatives should better be "case". Though it would look strange most of us: case Condition is when True => X := This; when False => X := That; end case; -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-04 8:52 ` Dmitry A. Kazakov @ 2009-12-05 2:45 ` Randy Brukardt 2009-12-05 10:32 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Randy Brukardt @ 2009-12-05 2:45 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:qchmzfxola9n$.1owojhwd91cbc.dlg@40tude.net... > On Thu, 3 Dec 2009 17:08:01 -0600, Randy Brukardt wrote: > >> It should be noted that our local coding standard would consider the >> above >> incorrect; there always must be an else branch or at least a comment >> explaining why there is no else branch: > > What about: > > if Read_Error (File) then > raise Data_Error; > end if; I hardly ever write this. I looked at 15 units picked at random and found only a handful of raises, most of them in case statements. And some of the "if" statements have elses or elsifs anyway. But the couple of examples I did find, I did indeed violate the coding standard, because there is no need to describe the else case. The beginning of our coding standard says that it is guidelines, not something to be followed blindly, and adding noise comments surely would qualify. The one place where this is fairly common (the heading of Claw functions) would have been much better written as preconditions (which Ada 95 didn't have, of course): if not Is_Valid (Window) then raise Not_Valid_Error; end if; doesn't need any explanation, but it would be better to have it clearly in the spec rather than just in comments. Writing in Ada 2012 as proposed today (and that might change before it gets standardized): procedure Do_Something (Window : Claw.Basic_Window_Type; ...) with Pre => Is_Valid (Window); is surely better because it puts the check in the spec where the caller can see it, rather than in comments that can be ignored. (And it also opens up the possibility of tools/compilers warning when it is *not* true at the point of a call, both potentially eliminating generated code and making error detection earlier.) > or > > if Estimated_Error <= Threshold then > return Estimated_Result; > end if; This should always have an else comment, IMHO. In the example you have: if if Estimated_Error <= Threshold then return Estimated_Result; -- else continue iteration end if; because the fact that iteration is intentionally being continued is important. I always try to comment why and how loops are exited, because I find I can't figure it out easily when returning to the code in a year or five. > or "exit when", which is a hidden if without else. I also find that it is very rare that I can use "exit when". If I can, I would prefer to do so, as it doesn't need much commenting (continuation is obvious). But almost always I find that something needs to be done (usually to save the result of the iteration) before exiting for good, so I usually end up with an if statement: for I in Data'Range loop exit when Item = Buffer (I); end loop; -- Oops, we don't know where the item was found or *if* it was found, which was the point of the iteration. So instead: Found := Data'Last + 1; for I in Data'Range loop if Item = Buffer (I) then Found := I; exit; -- else keep looking end if; end loop; -- Found now tells us where the item was, or if it was absent. which is a pattern that seems to happen a lot in my programs. > Maybe "if" with two alternatives should better be "case". Though it would > look strange most of us: > > case Condition is > when True => > X := This; > when False => > X := That; > end case; That's not *that* weird; it's not unusual to find out that there are more than two possibilities and a case statement is often the best way to handle that. Anyway, this is my (and by extension, RR's) coding standard. YMMV. Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-05 2:45 ` Randy Brukardt @ 2009-12-05 10:32 ` Dmitry A. Kazakov 2009-12-08 0:19 ` Randy Brukardt 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-05 10:32 UTC (permalink / raw) On Fri, 4 Dec 2009 20:45:19 -0600, Randy Brukardt wrote: > Writing in Ada 2012 as proposed today > (and that might change before it gets standardized): > > procedure Do_Something (Window : Claw.Basic_Window_Type; ...) > with Pre => Is_Valid (Window); Why not to allow such constraints for subtypes? E.g. subtype Valid_Window_Type is Window_Type when Is_Valid; then simply: procedure Do_Something (Window : Valid_Window_Type; ...) > is surely better because it puts the check in the spec where the caller can > see it, rather than in comments that can be ignored. (And it also opens up > the possibility of tools/compilers warning when it is *not* true at the > point of a call, both potentially eliminating generated code and making > error detection earlier.) Yes, but I do prefer the subtypes as the vehicle, rather than arbitrary preconditions put here and there on the subprograms. The latter is kind of weak typing to me. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-05 10:32 ` Dmitry A. Kazakov @ 2009-12-08 0:19 ` Randy Brukardt 2009-12-08 4:30 ` stefan-lucks 2009-12-08 9:22 ` Dmitry A. Kazakov 0 siblings, 2 replies; 132+ messages in thread From: Randy Brukardt @ 2009-12-08 0:19 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:1gcigitaii0u0.1psu2vj52e66g$.dlg@40tude.net... > On Fri, 4 Dec 2009 20:45:19 -0600, Randy Brukardt wrote: > >> Writing in Ada 2012 as proposed today >> (and that might change before it gets standardized): >> >> procedure Do_Something (Window : Claw.Basic_Window_Type; ...) >> with Pre => Is_Valid (Window); > > Why not to allow such constraints for subtypes? E.g. > > subtype Valid_Window_Type is Window_Type when Is_Valid; > > then simply: > > procedure Do_Something (Window : Valid_Window_Type; ...) That's also under consideration, but there are some problems with it. One problem is that such types are very confusing in array indexes/slices (do the values all participate or just the ones that pass the predicate?) There are a couple of others as well. Another issue is that not all preconditions/postconditions can be written this way. For one thing, a precondition can depend on multiple parameters at once. Another issues is that the entry condition and exit conditions may be different for a parameter. For instance, from Claw: procedure Create (Window : Basic_Window_Type; ...) with Pre => not Is_Valid (Window), Post => Is_Valid (Window); So subtypes cannot completely replace pre and post conditions, but they can be a complement to them. Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-08 0:19 ` Randy Brukardt @ 2009-12-08 4:30 ` stefan-lucks 2009-12-08 9:12 ` Dmitry A. Kazakov 2009-12-11 0:10 ` Robert A Duff 2009-12-08 9:22 ` Dmitry A. Kazakov 1 sibling, 2 replies; 132+ messages in thread From: stefan-lucks @ 2009-12-08 4:30 UTC (permalink / raw) On Mon, 7 Dec 2009, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > > Why not to allow such constraints for subtypes? E.g. > > > > subtype Valid_Window_Type is Window_Type when Is_Valid; > > > > then simply: > > > > procedure Do_Something (Window : Valid_Window_Type; ...) > > That's also under consideration, Great! Isn't that essentially the same as the invariants in Eiffel? > but there are some problems with it. One > problem is that such types are very confusing in array indexes/slices (do > the values all participate or just the ones that pass the predicate?) One way to solve^H^H^H^H^H^H circumvent this problem would be to prohibit that kind of subtyping for discrete types. In Dmity's example above, Window_Type is likely to be a record (probably a tagged one). If it is not a discrete type, you can't use it as an array index. :-) In any case, you definitively would not want to allow that kind of type for array indexes, if only the values which the predicate being true is allowed. How would you efficiently implement something like subtype Primes is Positive when Is_Prime; A: array (Primes (10_000 .. 20_000)) of T; -- 10_001 primality tests B: array (Primes (Start .. Stop)) of T; -- Start-Stop+1 such tests -- possibly at runtime > Another issue is that not all preconditions/postconditions can be written > this way. For one thing, a precondition can depend on multiple parameters at > once. Another issues is that the entry condition and exit conditions may be > different for a parameter. Right. That is precisely why Eiffel distinguishes preconditions, postconditions and invariants -- and supports all the three. -- ------ Stefan Lucks -- Bauhaus-University Weimar -- Germany ------ Stefan dot Lucks at uni minus weimar dot de ------ I love the taste of Cryptanalysis in the morning! ------ ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-08 4:30 ` stefan-lucks @ 2009-12-08 9:12 ` Dmitry A. Kazakov 2009-12-10 4:09 ` Randy Brukardt 2009-12-11 0:10 ` Robert A Duff 1 sibling, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-08 9:12 UTC (permalink / raw) On Tue, 8 Dec 2009 05:30:30 +0100, stefan-lucks@see-the.signature wrote: > On Mon, 7 Dec 2009, Randy Brukardt wrote: > >> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > >>> Why not to allow such constraints for subtypes? E.g. >>> >>> subtype Valid_Window_Type is Window_Type when Is_Valid; >>> >>> then simply: >>> >>> procedure Do_Something (Window : Valid_Window_Type; ...) >> >> That's also under consideration, > > Great! > > Isn't that essentially the same as the invariants in Eiffel? I see differences. Both Window_Type and Valid_Window_Type are visible. Eiffel invariant deals with one type. Further the invariant is an implementation detail normally invisible for the clients. Subtype is a publicly declared constraint (contract). >> but there are some problems with it. One >> problem is that such types are very confusing in array indexes/slices (do >> the values all participate or just the ones that pass the predicate?) > > One way to solve^H^H^H^H^H^H circumvent this problem would be to prohibit > that kind of subtyping for discrete types. In Dmity's example above, > Window_Type is likely to be a record (probably a tagged one). If it is not > a discrete type, you can't use it as an array index. :-) Well, it would be nice not to limit this to only tagged types. > In any case, you definitively would not want to allow that kind of type > for array indexes, if only the values which the predicate being true is > allowed. How would you efficiently implement something like > > subtype Primes is Positive when Is_Prime; > A: array (Primes (10_000 .. 20_000)) of T; -- 10_001 primality tests I assume the above should be: A: array (Primes'Val (10_000) .. Primes'Val (20_000)) of T; > B: array (Primes (Start .. Stop)) of T; -- Start-Stop+1 such tests > -- possibly at runtime And this: B: array (Primes (Start) .. Primes (Stop)) of T; However the rules determining the subtype of the range are not clear. The main problem is that there is no range types and their subtype to clarify the issue: Positive'(1)..Positive'(2) is this positive range or Integer range? Same with above, if ranges fall back to the base type ranges, there will be no problem, but also the semantics of the declarations would change to: A: array (Integer (Primes'Val (10_000)) .. Integer (Primes'Val (20_000))) of T; This is the actual problem to me, not the performance issues. The programmer packing a DVD playback into the subtype constraint should know what he is doing. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-08 9:12 ` Dmitry A. Kazakov @ 2009-12-10 4:09 ` Randy Brukardt 0 siblings, 0 replies; 132+ messages in thread From: Randy Brukardt @ 2009-12-10 4:09 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:c7bquicl77wg$.1lpuev0db0z46$.dlg@40tude.net... > On Tue, 8 Dec 2009 05:30:30 +0100, stefan-lucks@see-the.signature wrote: ... >> Isn't that essentially the same as the invariants in Eiffel? > > I see differences. Both Window_Type and Valid_Window_Type are visible. > Eiffel invariant deals with one type. Further the invariant is an > implementation detail normally invisible for the clients. Subtype is a > publicly declared constraint (contract). Right. We're actually considering both. We're almost certainly going to have invariants, on private types only, which are required to be checked only at the boundary of the defining package. (Such invariants can be abused, but they still more predicable than those on visible types.) Such invariants apply to all values of a type. Subtype predicates (for the lack of a better name, they're definitely *not* constraints, read AI05-0153-1 to see why) apply only to a view of a type, and would mainly be checked when a conversion to the subtype is done. (Almost everything interesting in Ada is technically a conversion to a subtype, so that covers the vast majority of interesting cases.) The current thinking is that using a subtype with a predicate in an array index, slice, entry family, and possibly some cases I forgot about would be illegal (or raise Program_Error in a generic body). We don't want to prevent the construction of subtypes like: subtype Odd_Integer is Integer with Predicate => Odd_Integer mod 2 = 1; but we sure don't want them in arrays. I'm not throughly convinced that this is the right approach, but more work needs to be done on the proposal in any case. Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-08 4:30 ` stefan-lucks 2009-12-08 9:12 ` Dmitry A. Kazakov @ 2009-12-11 0:10 ` Robert A Duff 1 sibling, 0 replies; 132+ messages in thread From: Robert A Duff @ 2009-12-11 0:10 UTC (permalink / raw) stefan-lucks@see-the.signature writes: > On Mon, 7 Dec 2009, Randy Brukardt wrote: > >> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > >> > Why not to allow such constraints for subtypes? E.g. >> > >> > subtype Valid_Window_Type is Window_Type when Is_Valid; >> > >> > then simply: >> > >> > procedure Do_Something (Window : Valid_Window_Type; ...) >> >> That's also under consideration, > > Great! Yes, it is great. I am a strong advocate of this feature. But there are a lot of details to work out, and the feature might not make it into the language by 2012. We'll see. > Isn't that essentially the same as the invariants in Eiffel? Yes, it's quite similar. But there are some important differences, such as the fact that in Eiffel, a class is both a type and a module, whereas in Ada, we have types, subtypes, and packages as separate concepts. >> but there are some problems with it. One >> problem is that such types are very confusing in array indexes/slices (do >> the values all participate or just the ones that pass the predicate?) I consider such problems to be side issues. They are solvable, I think. > One way to solve^H^H^H^H^H^H circumvent this problem would be to prohibit > that kind of subtyping for discrete types. In Dmity's example above, > Window_Type is likely to be a record (probably a tagged one). If it is not > a discrete type, you can't use it as an array index. :-) I don't like that "solution" (or "circumvention", if you prefer). ;-) I think one important goal is to be able to have arbitrary subtypes of an enumeration type, in addition to just subranges. I don't much care about arrays indexed by such subtypes. subtype Vowel is Character with Predicate => To_Lower(Vowel) in ('a', 'e', 'i', 'o', 'u'); or something like that. Here, Vowel refers to the current instance of subtype Vowel. I don't particularly want an array indexed by Vowel, but I might want to say: case C is when Vowel => ...; when Consonant => ...; when Punctuation => ...; ... or: procedure P(X : Vowel); or: if X in Vowel ... - Bob ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-08 0:19 ` Randy Brukardt 2009-12-08 4:30 ` stefan-lucks @ 2009-12-08 9:22 ` Dmitry A. Kazakov 2009-12-08 10:06 ` Georg Bauhaus 1 sibling, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-08 9:22 UTC (permalink / raw) On Mon, 7 Dec 2009 18:19:11 -0600, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:1gcigitaii0u0.1psu2vj52e66g$.dlg@40tude.net... >> On Fri, 4 Dec 2009 20:45:19 -0600, Randy Brukardt wrote: >> >>> Writing in Ada 2012 as proposed today >>> (and that might change before it gets standardized): >>> >>> procedure Do_Something (Window : Claw.Basic_Window_Type; ...) >>> with Pre => Is_Valid (Window); >> >> Why not to allow such constraints for subtypes? E.g. >> >> subtype Valid_Window_Type is Window_Type when Is_Valid; >> >> then simply: >> >> procedure Do_Something (Window : Valid_Window_Type; ...) > > That's also under consideration, but there are some problems with it. One > problem is that such types are very confusing in array indexes/slices (do > the values all participate or just the ones that pass the predicate?) Only ones of the subtype, obviously. But the problem is same as with the index sliding: when two subsets of the index are equivalent, either nominally (when elements are same) or structurally (when the number of elements is same + maybe other conditions). Structural equivalence might appear convenient, but it is always a source of confusion. > are a couple of others as well. > > Another issue is that not all preconditions/postconditions can be written > this way. For one thing, a precondition can depend on multiple parameters at > once. Another issues is that the entry condition and exit conditions may be > different for a parameter. For instance, from Claw: > > procedure Create (Window : Basic_Window_Type; ...) > with Pre => not Is_Valid (Window), > Post => Is_Valid (Window); Mutating [sub]type? That looks like a case for a constructor to me (an old discussion). > So subtypes cannot completely replace pre and post conditions, but they can > be a complement to them. Absolutely. However I consider it differently. In my view pre-/postconditions and invariants should be static, used strictly for program correctness proofs. Subtypes should complement them for dynamic run-time checks (recoverable faults). -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-08 9:22 ` Dmitry A. Kazakov @ 2009-12-08 10:06 ` Georg Bauhaus 2009-12-08 10:23 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Georg Bauhaus @ 2009-12-08 10:06 UTC (permalink / raw) Dmitry A. Kazakov schrieb: > In my view pre-/postconditions and > invariants should be static, used strictly for program correctness proofs. > Subtypes should complement them for dynamic run-time checks (recoverable > faults). > Hm. What would be your subtype based expression for generic type E is private; package Stacks is type Stack is private; procedure push (Modified_Stack : in out Stack; Another : Element) with pre => not Full (Modified_Stack), post => Size (Modified_Stack'Exit) = Size (Modified_Stack); procedure pop (Modified_Stack : in out Stack) with pre => not Empty (Modified_Stack), post => Empty (Modified_Stack); ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-08 10:06 ` Georg Bauhaus @ 2009-12-08 10:23 ` Dmitry A. Kazakov 2009-12-08 10:33 ` Georg Bauhaus 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-08 10:23 UTC (permalink / raw) On Tue, 08 Dec 2009 11:06:54 +0100, Georg Bauhaus wrote: > Dmitry A. Kazakov schrieb: >> In my view pre-/postconditions and >> invariants should be static, used strictly for program correctness proofs. >> Subtypes should complement them for dynamic run-time checks (recoverable >> faults). > > Hm. What would be your subtype based expression for > > generic > type E is private; > package Stacks is > > type Stack is private; > > procedure push (Modified_Stack : in out Stack; > Another : Element) > with pre => not Full (Modified_Stack), > post => Size (Modified_Stack'Exit) = Size (Modified_Stack); > > procedure pop (Modified_Stack : in out Stack) > with pre => not Empty (Modified_Stack), > post => Empty (Modified_Stack); None. The above is wrong. You cannot implement this contract (if we deduced one from the given pre- and postconditions). Proof: loop Push (Stack, X); end loop; q.e.d. Therefore the contract of a stack must always contain ideals, e.g. 1. exceptions, like Full_Error, Empty_Error; 2. blocked states, like holding the caller until the stack state is changed from another task. Pre- and psotconditions are to be used to prove a contract to hold. They themselves are no contract. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-08 10:23 ` Dmitry A. Kazakov @ 2009-12-08 10:33 ` Georg Bauhaus 2009-12-08 10:49 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Georg Bauhaus @ 2009-12-08 10:33 UTC (permalink / raw) Dmitry A. Kazakov schrieb: > On Tue, 08 Dec 2009 11:06:54 +0100, Georg Bauhaus wrote: > >> Dmitry A. Kazakov schrieb: >>> In my view pre-/postconditions and >>> invariants should be static, used strictly for program correctness proofs. >>> Subtypes should complement them for dynamic run-time checks (recoverable >>> faults). >> Hm. What would be your subtype based expression for >> >> generic >> type E is private; >> package Stacks is >> >> type Stack is private; >> >> procedure push (Modified_Stack : in out Stack; >> Another : Element) >> with pre => not Full (Modified_Stack), >> post => Size (Modified_Stack'Exit) = Size (Modified_Stack); >> >> procedure pop (Modified_Stack : in out Stack) >> with pre => not Empty (Modified_Stack), >> post => Empty (Modified_Stack); > > None. The above is wrong. You cannot implement this contract (if we deduced > one from the given pre- and postconditions). Proof: > > loop > Push (Stack, X); > end loop; > > q.e.d. > > Therefore the contract of a stack must always contain ideals, e.g. > > 1. exceptions, like Full_Error, Empty_Error; I understand that exceptions are implied by Eiffel style conditions. The Eiffel camp might answer, for example, Lock (Stack); while not Full (Stack) loop Push (Stack, X); end loop; Unlock (Stack); q.e.d. > 2. blocked states, like holding the caller until the stack state is changed > from another task. Would you want this to be possible with Ada, or with SPARK? ;-) > > Pre- and psotconditions are to be used to prove a contract to hold. They > themselves are no contract. In Eiffel, pre post and inv are used to write a contract. The proof obligation rests on the programmer. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-08 10:33 ` Georg Bauhaus @ 2009-12-08 10:49 ` Dmitry A. Kazakov 0 siblings, 0 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-08 10:49 UTC (permalink / raw) On Tue, 08 Dec 2009 11:33:25 +0100, Georg Bauhaus wrote: > Dmitry A. Kazakov schrieb: >> On Tue, 08 Dec 2009 11:06:54 +0100, Georg Bauhaus wrote: >> >>> Dmitry A. Kazakov schrieb: >>>> In my view pre-/postconditions and >>>> invariants should be static, used strictly for program correctness proofs. >>>> Subtypes should complement them for dynamic run-time checks (recoverable >>>> faults). >>> Hm. What would be your subtype based expression for >>> >>> generic >>> type E is private; >>> package Stacks is >>> >>> type Stack is private; >>> >>> procedure push (Modified_Stack : in out Stack; >>> Another : Element) >>> with pre => not Full (Modified_Stack), >>> post => Size (Modified_Stack'Exit) = Size (Modified_Stack); >>> >>> procedure pop (Modified_Stack : in out Stack) >>> with pre => not Empty (Modified_Stack), >>> post => Empty (Modified_Stack); >> >> None. The above is wrong. You cannot implement this contract (if we deduced >> one from the given pre- and postconditions). Proof: >> >> loop >> Push (Stack, X); >> end loop; >> >> q.e.d. >> >> Therefore the contract of a stack must always contain ideals, e.g. >> >> 1. exceptions, like Full_Error, Empty_Error; > > I understand that exceptions are implied by Eiffel style > conditions. No, in that case the conditions should be: pre => true post => Size (Modified_Stack'Exit) = Size (Modified_Stack); or Full_Error Actually, the second part is more elaborated, it should also state that the stack was not modified, but you've got the idea. Ideals are postcondition things. >> 2. blocked states, like holding the caller until the stack state is changed >> from another task. > > Would you want this to be possible with Ada, or with SPARK? ;-) In what sense? Of course it is possible to implement in Ada using a protected object or a monitor task. >> Pre- and psotconditions are to be used to prove a contract to hold. They >> themselves are no contract. > > In Eiffel, pre post and inv are used to write a contract. > The proof obligation rests on the programmer. Yes, this is the core of the disagreement. If that rests on the programmer, then *-conditions are THE program. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 12:13 ` Georg Bauhaus 2009-12-01 12:23 ` Georg Bauhaus 2009-12-01 13:48 ` Dmitry A. Kazakov @ 2009-12-01 23:51 ` Randy Brukardt 2 siblings, 0 replies; 132+ messages in thread From: Randy Brukardt @ 2009-12-01 23:51 UTC (permalink / raw) "Georg Bauhaus" <rm.dash-bauhaus@futureapps.de> wrote in message news:4b150869$0$6732$9b4e6d93@newsspool2.arcor-online.net... ... > If so, and presuming the programming language Ada is very much, > and explicitly, about storing and manipulating bits in registers, > memory words, ... of digital computers in a strongly typed fashion: > In this case I would know the use of being carried away by functional, > uhm, phantasm, pardon the expression. Rather, why not have Ada turn > warnings about "uninitialized" variables into a rule like Java's? Two obvious reasons: (1) Compatibility. Adding such a rule to Ada would ensure that 98% of existing Ada code would not compile. This is too fundamental a capability to take such an incompatibility. (2) Ada has a design goal of being implementable in a single pass compiler. A correllary of that goal is that legality rules cannot require flow analysis. Which means that any rule would have to be essentially the same as the one Dmitry is proposing: initialization is required in most cases; delayed initialization is OK only if it is unconditional. One has to think that using a conditional expression (as in Ada 2012 and in GNAT extensions) to initialize would be better than constructing some complex rules to allow initialization such as the one Bob suggested initially. One can certainly make the argument that the original design of Ada should have taken this problem more seriously (indeed, I think it is one of the worst mistakes of the original design team), but that doesn't make it any more likely that the language will be changed here. Probably the best that we could do today would be to add an Annex H restriction against default initialized objects that could have invalid values. (You'd want to allow well-defined default initialization as occurs for access types, it doesn't present a problem.) Then a particular project could require the use of that pragma and thus program in a safer Ada subset. (We could also help by making it easier to define default initial values for types other record types.) Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 21:47 ` Robert A Duff 2009-11-23 3:42 ` stefan-lucks @ 2009-11-23 8:52 ` Dmitry A. Kazakov 2009-11-30 20:43 ` Robert A Duff 1 sibling, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-23 8:52 UTC (permalink / raw) On Sun, 22 Nov 2009 16:47:21 -0500, Robert A Duff wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > >> P.S. ":=" for initialization of limited objects might look misleading (and >> does look misleading to me), but that would be another discussion on Ada >> design, in which nobody would agree with me. So I prefer not to go into it. > > Well, I would agree. > > Ada uses the term "assignment" to refer to both "initial assignment / > initialization" and "assignment_statement / overwriting". > I'd prefer to use different symbols for the two. > We're not going to change Ada in that regard, for compatibility reasons, > but I'm thinking in my hobby language design to use the term "assignment" > for the initial one, and "reassignment" for the subsequent overwriting > one, and use different symbols for the two. > > So, for a limited type, "assignment" is legal, "reassignment" is not. > > What do you think? I would prefer conventional: allocation construction (initialization) assignment destruction (finalization) deallocation Semantically a limited object is never assigned. Its state comes into existence per construction and disappears per destruction. The word assignment for most people is associated with state change, assuming that there was some state before. So X : T := F (Y); looks equivalent to X : T; begin X := F (Y); But they are not. I would prefer proper constructors, e.g. X : T (Y); -- Y is a constraint of T, parameter of the constructor I don't like functions returning limited objects. But there also are two other forms of standard assignments. Depending on whether the left part is available: procedure ":=" (Left : in out T; Right : T); and function ":=" (Right : T) return T; Ada uses the second form, but obviously there are important cases where the first form is preferable (and conversely). And further, there are three variants per each concerning dispatch: procedure ":=" (Left : in out T; Right : T); -- Full MD procedure ":=" (Left : in out T; Right : T'Class); -- Target-controlled procedure ":=" (Left : in out T'Class; Right : T); -- Source-controlled It would be difficult to sort this out! (:-)) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-23 8:52 ` Dmitry A. Kazakov @ 2009-11-30 20:43 ` Robert A Duff 2009-12-01 9:00 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Robert A Duff @ 2009-11-30 20:43 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > The word assignment for most people is associated with state change, > assuming that there was some state before. So > > X : T := F (Y); > > looks equivalent to > > X : T; > begin > X := F (Y); > > But they are not. Right. I think they should be equivalent. My solution is to use two different symbols for (initial) assignment and (subsequent) reassignment. >... I would prefer proper constructors, e.g. > > X : T (Y); -- Y is a constraint of T, parameter of the constructor > > I don't like functions returning limited objects. I know you don't, but I don't understand why. Using named functions as constructors has a big advantage -- you can have more than one, and you can give them meaningful names. For example: X : Sequence := Empty_Seq; Y : Sequence := Singleton_Seq (Item => 123); Z : Sequence := Make_Sequence (Length => 123); With discriminants, what does the 123 mean? You have to pick one. > But there also are two other forms of standard assignments. Depending on > whether the left part is available: > > procedure ":=" (Left : in out T; Right : T); > > and > > function ":=" (Right : T) return T; > > Ada uses the second form, but obviously there are important cases where the > first form is preferable (and conversely). > > And further, there are three variants per each concerning dispatch: > > procedure ":=" (Left : in out T; Right : T); -- Full MD > procedure ":=" (Left : in out T; Right : T'Class); -- Target-controlled > procedure ":=" (Left : in out T'Class; Right : T); -- Source-controlled > > It would be difficult to sort this out! (:-)) Yeah, I'm not sure what the right answer is. - Bob ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-30 20:43 ` Robert A Duff @ 2009-12-01 9:00 ` Dmitry A. Kazakov 2009-12-01 5:45 ` stefan-lucks 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-01 9:00 UTC (permalink / raw) On Mon, 30 Nov 2009 15:43:21 -0500, Robert A Duff wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > >> The word assignment for most people is associated with state change, >> assuming that there was some state before. So >> >> X : T := F (Y); >> >> looks equivalent to >> >> X : T; >> begin >> X := F (Y); >> >> But they are not. > > Right. I think they should be equivalent. My solution is to use > two different symbols for (initial) assignment and (subsequent) > reassignment. But they cannot be, otherwise the semantics of ":=" would depend on the things done before: X : T; begin X := F (Y); -- Initialization X : T; begin X := Z; X := F (Y); -- [Re]assignment This is unacceptable, because it is untyped. The semantics of ":=" must be solely defined by the types and, maybe, be the context (declarative vs. imperative (as it is now). I don't like even the second part. >>... I would prefer proper constructors, e.g. >> >> X : T (Y); -- Y is a constraint of T, parameter of the constructor >> >> I don't like functions returning limited objects. > > I know you don't, but I don't understand why. Because they cannot return anything, so a "return statement" is invented together with an infinite chain of other unholy things bending and twisting otherwise clear and established notions. > Using named functions as constructors has a big advantage -- you can > have more than one, and you can give them meaningful names. > > For example: > > X : Sequence := Empty_Seq; > Y : Sequence := Singleton_Seq (Item => 123); > Z : Sequence := Make_Sequence (Length => 123); > > With discriminants, what does the 123 mean? You have to pick one. No problem: X : Sequence; Y : Sequence (Item => 123); Z : Sequence (Length => 123); In my imaginary world public discriminant (as well as a public record component) is only an interface, therefore it is no problem for a type have any collection of public discriminant sets. They are just like other operations, you could overload them if necessary. >> But there also are two other forms of standard assignments. Depending on >> whether the left part is available: >> >> procedure ":=" (Left : in out T; Right : T); >> >> and >> >> function ":=" (Right : T) return T; >> >> Ada uses the second form, but obviously there are important cases where the >> first form is preferable (and conversely). >> >> And further, there are three variants per each concerning dispatch: >> >> procedure ":=" (Left : in out T; Right : T); -- Full MD >> procedure ":=" (Left : in out T; Right : T'Class); -- Target-controlled >> procedure ":=" (Left : in out T'Class; Right : T); -- Source-controlled >> >> It would be difficult to sort this out! (:-)) > > Yeah, I'm not sure what the right answer is. I think that assignment should be considered a plain primitive procedure with no special treatment. Initialization has in my view nothing to do with it. The language should visually separate both. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 9:00 ` Dmitry A. Kazakov @ 2009-12-01 5:45 ` stefan-lucks 2009-12-01 11:12 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: stefan-lucks @ 2009-12-01 5:45 UTC (permalink / raw) On Tue, 1 Dec 2009, Dmitry A. Kazakov wrote: > On Mon, 30 Nov 2009 15:43:21 -0500, Robert A Duff wrote: > > Right. I think they should be equivalent. My solution is to use > > two different symbols for (initial) assignment and (subsequent) > > reassignment. > > But they cannot be, otherwise the semantics of ":=" would depend on the > things done before: > > X : T; > begin > X := F (Y); -- Initialization No, at this point of time, X has been initialised to *some* value, even if the value itself is undefined. So this is just a proper assignment. > X : T; > begin > X := Z; > X := F (Y); -- [Re]assignment That is a proper assignment, as well. The only difference is that we can be sure the before-assignment value of X is defined (assuming the value of Z is a defined one). An initialisation would be X : T ::= F(Y); begin ... But you are right, Dmitry, nobody would not want to distinguish an assignment to overwrite a potentially undefined value from an assignment ovwerwriting a previously defined value. -- ------ Stefan Lucks -- Bauhaus-University Weimar -- Germany ------ Stefan dot Lucks at uni minus weimar dot de ------ I love the taste of Cryptanalysis in the morning! ------ ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 5:45 ` stefan-lucks @ 2009-12-01 11:12 ` Dmitry A. Kazakov 2009-12-01 8:01 ` stefan-lucks 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-01 11:12 UTC (permalink / raw) On Tue, 1 Dec 2009 06:45:16 +0100, stefan-lucks@see-the.signature wrote: > On Tue, 1 Dec 2009, Dmitry A. Kazakov wrote: > >> On Mon, 30 Nov 2009 15:43:21 -0500, Robert A Duff wrote: > >>> Right. I think they should be equivalent. My solution is to use >>> two different symbols for (initial) assignment and (subsequent) >>> reassignment. >> >> But they cannot be, otherwise the semantics of ":=" would depend on the >> things done before: >> >> X : T; >> begin >> X := F (Y); -- Initialization > > No, at this point of time, X has been initialised to *some* value, even if > the value itself is undefined. So this is just a proper assignment. You should say that to Bob, because this is exactly my point. An object is *always* constructed <=> initialized before it can ever be used in any way (provided the language is typed <=> at any point of its existence any object has a definite type and only the operations defined for the type are available to the object). Initialization /= construction does not fit into this picture. >> X : T; >> begin >> X := Z; >> X := F (Y); -- [Re]assignment > > That is a proper assignment, as well. The only difference is that we > can be sure the before-assignment value of X is defined (assuming the > value of Z is a defined one). > > An initialisation would be > > X : T ::= F(Y); > begin > ... > > But you are right, Dmitry, nobody would not want to distinguish an > assignment to overwrite a potentially undefined value from an assignment > ovwerwriting a previously defined value. Of course I am! (:-)) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 11:12 ` Dmitry A. Kazakov @ 2009-12-01 8:01 ` stefan-lucks 2009-12-01 13:37 ` Dmitry A. Kazakov 2009-12-15 23:54 ` Robert A Duff 0 siblings, 2 replies; 132+ messages in thread From: stefan-lucks @ 2009-12-01 8:01 UTC (permalink / raw) On Tue, 1 Dec 2009, Dmitry A. Kazakov wrote: > On Tue, 1 Dec 2009 06:45:16 +0100, stefan-lucks@see-the.signature wrote: > > > On Tue, 1 Dec 2009, Dmitry A. Kazakov wrote: > > > >> On Mon, 30 Nov 2009 15:43:21 -0500, Robert A Duff wrote: > > > >>> Right. I think they should be equivalent. My solution is to use > >>> two different symbols for (initial) assignment and (subsequent) > >>> reassignment. > >> > >> But they cannot be, otherwise the semantics of ":=" would depend on the > >> things done before: > >> > >> X : T; > >> begin > >> X := F (Y); -- Initialization > > > > No, at this point of time, X has been initialised to *some* value, even if > > the value itself is undefined. So this is just a proper assignment. > > You should say that to Bob, because this is exactly my point. No! You just moved to a different topic: [...] > Initialization /= construction does not fit into this picture. The issue was initialisation /= assignment (*) and the fact that in Ada both look syntactically the same. You seem to be the first to mention "construction" at all. BTW, I don't think initialisation and construction are actually identical, even though they have to be performed in close temporal proximity. If construction fails, this is a Storage_Error. A failed Initialisation is much more powerful -- it can raise any of your favourite exceptions. ;-) --------- Footnote: (*) I prefer to avoid the word "reassignment", which Bob would use. -- ------ Stefan Lucks -- Bauhaus-University Weimar -- Germany ------ Stefan dot Lucks at uni minus weimar dot de ------ I love the taste of Cryptanalysis in the morning! ------ ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 8:01 ` stefan-lucks @ 2009-12-01 13:37 ` Dmitry A. Kazakov 2009-12-15 23:54 ` Robert A Duff 1 sibling, 0 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-12-01 13:37 UTC (permalink / raw) On Tue, 1 Dec 2009 09:01:11 +0100, stefan-lucks@see-the.signature wrote: > BTW, I don't think initialisation and construction are actually identical, > even though they have to be performed in close temporal proximity. If > construction fails, this is a Storage_Error. No, it is Program_Error in Ada. Construction /= allocation. Here I repeat the object's life cycle as I see it: allocation construction (=initialization) use (includes assignment and all public operations) destruction (=finalization) deallocation > A failed Initialisation is > much more powerful -- it can raise any of your favourite exceptions. ;-) Well, I think it is possible to roll back an initialization, though it was not attempted in Ada. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-12-01 8:01 ` stefan-lucks 2009-12-01 13:37 ` Dmitry A. Kazakov @ 2009-12-15 23:54 ` Robert A Duff 1 sibling, 0 replies; 132+ messages in thread From: Robert A Duff @ 2009-12-15 23:54 UTC (permalink / raw) stefan-lucks@see-the.signature writes: > On Tue, 1 Dec 2009, Dmitry A. Kazakov wrote: > >> On Tue, 1 Dec 2009 06:45:16 +0100, stefan-lucks@see-the.signature wrote: >> >> > On Tue, 1 Dec 2009, Dmitry A. Kazakov wrote: >> > >> >> On Mon, 30 Nov 2009 15:43:21 -0500, Robert A Duff wrote: >> > >> >>> Right. I think they should be equivalent. My solution is to use >> >>> two different symbols for (initial) assignment and (subsequent) >> >>> reassignment. >> >> >> >> But they cannot be, otherwise the semantics of ":=" would depend on the >> >> things done before: >> >> >> >> X : T; >> >> begin >> >> X := F (Y); -- Initialization >> > >> > No, at this point of time, X has been initialised to *some* value, even if >> > the value itself is undefined. So this is just a proper assignment. >> >> You should say that to Bob, because this is exactly my point. Bob, here. Sorry I didn't reply to many messages in this thread -- I don't follow this newsgroup every day. Too much real work to be done. ;-) More like every few months... I'm getting mixed up about arguments regarding semantics, versus arguments regarding terminology. Both are important. > No! > > You just moved to a different topic: > > [...] >> Initialization /= construction does not fit into this picture. > > The issue was > > initialisation /= assignment (*) Right. Ada distinguishes the two. But it uses the same syntax for both, which I think is confusing. Except we use "zee". Or "zed" if you prefer. ;-) Actually the Ada terminology is that "assignment" includes both: "initialization" and "assignment_statement". That's confusing. > and the fact that in Ada both look syntactically the same. You seem to be > the first to mention "construction" at all. > > BTW, I don't think initialisation and construction are actually identical, > even though they have to be performed in close temporal proximity. If > construction fails, this is a Storage_Error. A failed Initialisation is > much more powerful -- it can raise any of your favourite exceptions. ;-) More terminology confusion: Some folks think "construct" and "allocate storage" are synonymous. Other folks think "construct" and "initialize" are synonymous. Maybe in 100 years computer science will have terms everybody can agree on. In the mean time, we have "procedure", "function", "subprogram", "subroutine", "routine", "program", "method", etc. all of which mean more-or-less the same thing. Sigh. > --------- > Footnote: > (*) I prefer to avoid the word "reassignment", which Bob would use. Sigh. Several people have voted against my (clever? too clever?) "assign" vs. "reassign" terminology. I'm still not sure... - Bob ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-22 18:03 ` xorque 2009-11-22 18:08 ` xorque 2009-11-22 18:28 ` Dmitry A. Kazakov @ 2009-11-23 7:48 ` Georg Bauhaus 2009-11-23 7:58 ` Georg Bauhaus 2 siblings, 1 reply; 132+ messages in thread From: Georg Bauhaus @ 2009-11-23 7:48 UTC (permalink / raw) On 11/22/09 7:03 PM, xorque wrote: > On Nov 22, 5:41 pm, "Dmitry A. Kazakov"<mail...@dmitry-kazakov.de> > wrote: >> On Sun, 22 Nov 2009 08:52:05 -0800 (PST), xorque wrote: >> >>> I have to admit to not understanding that error. >> >> And I don't understand your design. If Archive is a Stream derive then it >> from Root_Stream_Type. If it deals with a stream then pass >> Root_Stream_Type'Class to the operations that need it. > > The design is both irrelevant and obsolete. I'm just trying to find > out if > the problem is *definitely* that code so that I can close a bug in the > GCC tracker. A seg fault is definitely not the answer I would expect from an Ada program that was compiled by GNAT in Ada mode. Even when a temporary is involved in dereferencing. Also, there are some of the "usual suspects" in the code, like others =>, return ... do ... end. No reason to close the report, I think. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-23 7:48 ` Georg Bauhaus @ 2009-11-23 7:58 ` Georg Bauhaus 0 siblings, 0 replies; 132+ messages in thread From: Georg Bauhaus @ 2009-11-23 7:58 UTC (permalink / raw) On 11/23/09 8:48 AM, I wrote: > Also, there are some of the "usual suspects" in the code, > like others =>, return ... do ... end. No reason to others => {box} Thunderbird post-Shredder still early release makes Character'Val(60) & Character'Val(62) vanish, sorry. > close the report, I think. > > ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-18 19:21 ` Dmitry A. Kazakov 2009-11-19 0:27 ` Randy Brukardt @ 2009-11-19 16:04 ` Adam Beneschan 1 sibling, 0 replies; 132+ messages in thread From: Adam Beneschan @ 2009-11-19 16:04 UTC (permalink / raw) On Nov 18, 11:21 am, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > Returning to the case where we do not want cross combinations. I thought > about it and came to the conclusion that this is single dispatch (tags are > synchronous). I think we should approach this differently. OK, this makes a lot of sense---the kind of thing I'm thinking of does seem a lot more related to "single dispatch" than to the sort of multiple dispatch in your earlier example involving a Printer and Contents. I haven't yet looked over the rest of your idea carefully yet... But at least I think we understand each other now. -- Adam ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-17 22:10 ` Adam Beneschan 2009-11-18 9:46 ` Dmitry A. Kazakov @ 2009-11-19 2:23 ` tmoran 2009-11-19 8:32 ` Dmitry A. Kazakov 1 sibling, 1 reply; 132+ messages in thread From: tmoran @ 2009-11-19 2:23 UTC (permalink / raw) > package Documents is > > type Document is abstract tagged private; > type Text_Region is abstract tagged private; > type Marker is abstract tagged private; > ... > procedure Move_Text (Doc : in out Document; > From : in Text_Region; > To : in Marker) > is abstract; There's really no way at compile time to require a particular relationship between two (or more) arbitrary parameters to a procedure. If that's necessary, you usually combine them into a single object that's a single parameter. Thus if a Document could have up to 10 defined Text_Regions and 20 Markers one could say type Document is abstract tagged private; -- Presumably includes an array or list of appropriate Text_Region -- descriptors and of Marker descriptors. type Text_Region_Ids is range 1 .. 10; type Marker_Ids is range 1 .. 20; procedure Move_Text (Doc : in out Document; From : in Text_Region_Ids; To : in Marker_Ids) is abstract; ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: Operation can be dispatching in only one type 2009-11-19 2:23 ` tmoran @ 2009-11-19 8:32 ` Dmitry A. Kazakov 0 siblings, 0 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2009-11-19 8:32 UTC (permalink / raw) On Thu, 19 Nov 2009 02:23:07 +0000 (UTC), tmoran@acm.org wrote: >> package Documents is >> >> type Document is abstract tagged private; >> type Text_Region is abstract tagged private; >> type Marker is abstract tagged private; >> ... >> procedure Move_Text (Doc : in out Document; >> From : in Text_Region; >> To : in Marker) >> is abstract; > > There's really no way at compile time to require a particular > relationship between two (or more) arbitrary parameters to a procedure. > If that's necessary, you usually combine them into a single object that's > a single parameter. Yes, this is why I consider these cases single dispatch on a tuple of types. It is like type Editing is interface (Document, Region, Marker); And the operations are in fact defined on Editing, which has only one tag. > Thus if a Document could have up to 10 defined > Text_Regions and 20 Markers one could say > > type Document is abstract tagged private; > -- Presumably includes an array or list of appropriate Text_Region > -- descriptors and of Marker descriptors. > > type Text_Region_Ids is range 1 .. 10; > type Marker_Ids is range 1 .. 20; > > procedure Move_Text (Doc : in out Document; > From : in Text_Region_Ids; > To : in Marker_Ids) > is abstract; It is not ADT. ID does not have finalization and cannot refer to a specific Document. You will not be able to transparently manage Regions and Markers. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* operation can be dispatching in only one type @ 2015-11-23 10:23 Serge Robyns 2015-11-23 11:29 ` Dmitry A. Kazakov 2015-11-23 17:40 ` Jeffrey R. Carter 0 siblings, 2 replies; 132+ messages in thread From: Serge Robyns @ 2015-11-23 10:23 UTC (permalink / raw) Guys, I'm facing an issue with Ada when it comes to use an operation on two tagged types. It isn't a road blocker and I've found my way around and are not using tagged types for these anymore. I've now a function that computes the total balance every time. Another option is to pass the id and then search the map. However in C++ it does work as I expected. Let's say I've the following tagged types. type T_Balance is tagged record Id : T_Balance_Id; Name : T_Balance_Name; Balance : T_Amount; end record; package Balance_Map is new Ada.Containers.Ordered_Maps (Key_Type => T_Balance_Id, Element_Type => T_Balance); type T_Account is tagged record Name : T_Account_Name; Total_Balance : T_Money; Balances : Balance_Map.Map; end record; And I want to have an operation increasing a balance and the total in the account. The following declaration is refused: procedure Update_Balance (Self : in out T_Account; AB : in out T_Balance; Amount : in T_Money); My hope was that it gets dispatched to the right operation on Self which in turns will call the right operation on AB. But the compiler seems not to agree. in C++: class account { public: account_id id; account_name name; money total_balance; balance_list balances; account(); virtual ~account(); void update_balance (account_balance& ab, money amount); }; void account::update_balance (account_balance& ab, money amount) { ab.update_balance(amount); total_balance.amount += amount; } ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-23 10:23 operation " Serge Robyns @ 2015-11-23 11:29 ` Dmitry A. Kazakov 2015-11-23 13:05 ` Serge Robyns 2015-11-23 17:40 ` Jeffrey R. Carter 1 sibling, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-11-23 11:29 UTC (permalink / raw) On Mon, 23 Nov 2015 02:23:57 -0800 (PST), Serge Robyns wrote: > I'm facing an issue with Ada when it comes to use an operation on two > tagged types. You can use tagged types, they cannot be dispatching arguments though. That is a big difference. > However in C++ it does work as I expected. C++ has no multiple dispatch either. > Let's say I've the following tagged types. > > type T_Balance is tagged record > Id : T_Balance_Id; > Name : T_Balance_Name; > Balance : T_Amount; > end record; > > package Balance_Map is new > Ada.Containers.Ordered_Maps (Key_Type => T_Balance_Id, > Element_Type => T_Balance); > > type T_Account is tagged record > Name : T_Account_Name; > Total_Balance : T_Money; > Balances : Balance_Map.Map; > end record; > > And I want to have an operation increasing a balance and the total in the account. > The following declaration is refused: > > procedure Update_Balance > (Self : in out T_Account; > AB : in out T_Balance; > Amount : in T_Money); > > My hope was that it gets dispatched to the right operation on Self which > in turns will call the right operation on AB. But the compiler seems not > to agree. It is too late to declare a dispatching operation on T_Balance when you already used T_Balance as a type somewhere else, e.g. to instantiate a package. I don't use Ada containers, but the pattern is: 1. Instantiate the package with T_Balance'Class 2. Declare the container-specific callback, e.g. Update_Balance with T_Balance'Class 3. Dispatch from Update_Balance, to an [abstract] primitive operation of T_Balance you declare in advance. E.g. On_Update. [ This is what happens in C++, because in C++ T and T'Class are [in]consistently confused. ] -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-23 11:29 ` Dmitry A. Kazakov @ 2015-11-23 13:05 ` Serge Robyns 2015-11-23 13:48 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Serge Robyns @ 2015-11-23 13:05 UTC (permalink / raw) On Monday, 23 November 2015 12:29:55 UTC+1, Dmitry A. Kazakov wrote: > On Mon, 23 Nov 2015 02:23:57 -0800 (PST), Serge Robyns wrote: > > > I'm facing an issue with Ada when it comes to use an operation on two > > tagged types. > > You can use tagged types, they cannot be dispatching arguments though. That > is a big difference. > > > However in C++ it does work as I expected. > > C++ has no multiple dispatch either. I'm not using multiple dispatching, I'm using "serial" dispatching. What I'm trying to achieve is very close to the concept of a mix-in. The code in C++ is doing what I want to achieve. Update the specific balance and the "parent" sum in a cheap and direct way. Obviously there is a potential "bug" as I can pass any balance to the operation, including ones from another account. > It is too late to declare a dispatching operation on T_Balance when you > already used T_Balance as a type somewhere else, e.g. to instantiate a > package. > As I said that operation is not dispatching on T_Balance, the T_Account operation will be dispatching. As with regards to callback constructs, this sounds like "ugly" workarounds IMHO. This weekend I've been re-writing the complete core "class" hierarchy in C++11 and 1 XML SAX parser into Xerces as a test. I still need to rewrite the existing business logic and decide on how to implement DAO for which I did a niece implementation in Ada (IMHO), as I can have mixture of data stores for different components. The abstract interface is using multiple limited interfaces. However I stumbled on something that looks very interesting: ODB (C++) https://en.wikipedia.org/wiki/ODB_%28C%2B%2B%29. For Ada I did use GNATCOLL but was also looking at Gnoga as an alternative. During that exercise I missed 2 major benefits of Ada. Very strong typing, although C++11's "enum class" gives me the same when it comes to the primitive data types, i.e. scalars. And last but not least it's readability. I love the Pascal family syntax. In my former C career I've suffered a lot of imbalanced '{}'. And I've been looking at C replacements since the early 1990's only to see that that syntax style is getting more and more spread. :-( Having said that, I still didn't decide on which one to pursue, Ada 2012 or C++ 11 or 14. One of the benefits of C++ is the very large collection of 3rd party libraries. I do recognize the work of David Botton and yourself in that area. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-23 13:05 ` Serge Robyns @ 2015-11-23 13:48 ` Dmitry A. Kazakov 2015-11-23 14:16 ` Serge Robyns 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-11-23 13:48 UTC (permalink / raw) On Mon, 23 Nov 2015 05:05:02 -0800 (PST), Serge Robyns wrote: > On Monday, 23 November 2015 12:29:55 UTC+1, Dmitry A. Kazakov wrote: >> On Mon, 23 Nov 2015 02:23:57 -0800 (PST), Serge Robyns wrote: >> >>> I'm facing an issue with Ada when it comes to use an operation on two >>> tagged types. >> >> You can use tagged types, they cannot be dispatching arguments though. That >> is a big difference. >> >>> However in C++ it does work as I expected. >> >> C++ has no multiple dispatch either. > > I'm not using multiple dispatching, I'm using "serial" dispatching. What > I'm trying to achieve is very close to the concept of a mix-in. Hmm, mix-in uses a class-wide that gets mixed in. Which is why you could dispatch on it. No class-wide, no dispatch. > The code in C++ is doing what I want to achieve. C++ code is broken, always broken, because C++ confuses T and T'Class and hence re-dispatches each time and every place (except for ctors). Ada's exact equivalent to void account::update_balance (account_balance& ab, money amount) is procedure Update_Balance (X : in out Account; AB : in out Account_Balance'Class, ...) Neither is dispatching in AB. Where is a problem? > Obviously there is a > potential "bug" as I can pass any balance to the operation, including ones > from another account. Re-dispatch is a potential bug because it ignores declared types. >> It is too late to declare a dispatching operation on T_Balance when you >> already used T_Balance as a type somewhere else, e.g. to instantiate a >> package. > > As I said that operation is not dispatching on T_Balance, the T_Account > operation will be dispatching. Which is good as T_Account is the type being declared. I don't see what is the problem. If Update_Balance should be a primitive operation of T_Balance, it must be declared so. At the declaration point of T_Balance there is no T_Account yet thus you could not use it there. Again, good, it is bad design to mix containers and elements of. You just are trying an unpattern, IMO. You can still inject an account interface there if you wanted: type Abstract_Account is limited interface ...; type T_Balance is ... procedure Update_Balance -- Primitive operation ( Account : in out Abstract_Account'Class; Balance : in out T_Balance; ... ); type T_Account is new Abstract_Account with ... > As with regards to callback constructs, this sounds like "ugly" workarounds IMHO. Possibly, but IMO because of an anti-OO design. What are operations of T_Balance. What are operations of T_Account? Types are useless without operations. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-23 13:48 ` Dmitry A. Kazakov @ 2015-11-23 14:16 ` Serge Robyns 2015-11-23 14:59 ` G.B. 2015-11-23 15:52 ` Dmitry A. Kazakov 0 siblings, 2 replies; 132+ messages in thread From: Serge Robyns @ 2015-11-23 14:16 UTC (permalink / raw) On Monday, 23 November 2015 14:48:45 UTC+1, Dmitry A. Kazakov wrote: > On Mon, 23 Nov 2015 05:05:02 -0800 (PST), Serge Robyns wrote: > > > On Monday, 23 November 2015 12:29:55 UTC+1, Dmitry A. Kazakov wrote: > >> On Mon, 23 Nov 2015 02:23:57 -0800 (PST), Serge Robyns wrote: > >> > >>> I'm facing an issue with Ada when it comes to use an operation on two > >>> tagged types. > >> > >> You can use tagged types, they cannot be dispatching arguments though. That > >> is a big difference. > >> > C++ code is broken, always broken, because C++ confuses T and T'Class and > hence re-dispatches each time and every place (except for ctors). Ada's > exact equivalent to > > void account::update_balance (account_balance& ab, money amount) > > is > > procedure Update_Balance (X : in out Account; > AB : in out Account_Balance'Class, ...) > This one did the trick. I do feel very frustrated with regards to the Ada idiosyncrasies, if I may say so. Recalls me the "'" notation I had to use when declaring a part of a constant that was from a parent class. I got this trick in this newsgroup too. Trying to google on Ada "'" is a challenge :-P. I'm wondering the best way to "learn" these without falling asleep while reading for the 10th time basic programming stuff and then missing that one important comment in the text. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-23 14:16 ` Serge Robyns @ 2015-11-23 14:59 ` G.B. 2015-11-23 15:52 ` Dmitry A. Kazakov 1 sibling, 0 replies; 132+ messages in thread From: G.B. @ 2015-11-23 14:59 UTC (permalink / raw) On 23.11.15 15:16, Serge Robyns wrote: > I do feel very frustrated with regards to the Ada idiosyncrasies To me, 'Class means openly stating the intent: C++ does many things "automatically" when possible. Which is why you can show excellence when you can tell how. (In case of interpreting compiler diagnostics, say.) An example is when different types inheriting from t_balance are placed in a container map<..., t_balance> If class t_balance_A : public t_balance, then the compiler may let you place an object declared of type t_balance_A in the map - you'd think. But it turns out to silently have become a t_balance. Trying a fix map<..., t_balance&> or similar, chances are that after this, a long list of errors will appear, about operators, value initialization, type differences etc. If Ada does fewer things "automatically", thus requesting that you specify type T'Class when you mean things class-wide, this reduced malleability of the meaning of names may become an advantage. A consequence is less effort at understanding what a name is referring to, and an increase of errors-related productivity, as no automatic choice needs to be unfolded in the diagnostic messages. T'Class means for all types with T a progenitor T means T, only. The 'Class thing marks the place where there could be a variation in run-time dispatching at all. Of course, any competent C++ programmer will always do the right thing, which is not using T where a T* or T& is in order. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-23 14:16 ` Serge Robyns 2015-11-23 14:59 ` G.B. @ 2015-11-23 15:52 ` Dmitry A. Kazakov 1 sibling, 0 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-11-23 15:52 UTC (permalink / raw) On Mon, 23 Nov 2015 06:16:38 -0800 (PST), Serge Robyns wrote: > On Monday, 23 November 2015 14:48:45 UTC+1, Dmitry A. Kazakov wrote: >> On Mon, 23 Nov 2015 05:05:02 -0800 (PST), Serge Robyns wrote: >> >>> On Monday, 23 November 2015 12:29:55 UTC+1, Dmitry A. Kazakov wrote: >>>> On Mon, 23 Nov 2015 02:23:57 -0800 (PST), Serge Robyns wrote: >>>> >>>>> I'm facing an issue with Ada when it comes to use an operation on two >>>>> tagged types. >>>> >>>> You can use tagged types, they cannot be dispatching arguments though. That >>>> is a big difference. >>>> > >> C++ code is broken, always broken, because C++ confuses T and T'Class and >> hence re-dispatches each time and every place (except for ctors). Ada's >> exact equivalent to >> >> void account::update_balance (account_balance& ab, money amount) >> >> is >> >> procedure Update_Balance (X : in out Account; >> AB : in out Account_Balance'Class, ...) > > This one did the trick. I do feel very frustrated with regards to the Ada > idiosyncrasies, if I may say so. Recalls me the "'" notation I had to use > when declaring a part of a constant that was from a parent class. It depends on what you believe is "right." If you programmed C++ in Ada you would indeed face difficulties, and conversely. But again C++ is inconsistent in its treatment of specific and class-wide types. Ada merely requires you to specify the exact type of the operation's argument, that is. > I'm wondering the best way to "learn" these without falling asleep while > reading for the 10th time basic programming stuff and then missing that > one important comment in the text. Regarding OO features, the best way is start thinking of it in terms of types rather than of [untyped] data structures and procedures. You will better understand both Ada and C++: class = set of types (closed upon inheritance) class-wide type = the closure of the class specific type = member of the class -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-23 10:23 operation " Serge Robyns 2015-11-23 11:29 ` Dmitry A. Kazakov @ 2015-11-23 17:40 ` Jeffrey R. Carter 2015-11-24 9:08 ` Serge Robyns 1 sibling, 1 reply; 132+ messages in thread From: Jeffrey R. Carter @ 2015-11-23 17:40 UTC (permalink / raw) On 11/23/2015 03:23 AM, Serge Robyns wrote: > > I'm facing an issue with Ada when it comes to use an operation on two tagged types. It isn't a road blocker and I've found my way around and are not using tagged types for these anymore. I've now a function that computes the total balance every time. Another option is to pass the id and then search the map. > > type T_Balance is tagged record > Id : T_Balance_Id; > Name : T_Balance_Name; > Balance : T_Amount; > end record; > > package Balance_Map is new > Ada.Containers.Ordered_Maps (Key_Type => T_Balance_Id, > Element_Type => T_Balance); > > type T_Account is tagged record > Name : T_Account_Name; > Total_Balance : T_Money; > Balances : Balance_Map.Map; > end record; > > And I want to have an operation increasing a balance and the total in the account. > The following declaration is refused: > > procedure Update_Balance > (Self : in out T_Account; > AB : in out T_Balance; > Amount : in T_Money); > > My hope was that it gets dispatched to the right operation on Self which in turns will call the right operation on AB. But the compiler seems not to agree. Ada has only single dispatch, so an operation can only be a primitive operation of a single tagged type (the "controlling" type). In Ada, neither the parameter name (Self) nor the position of the parameter in the parameter list determine the type for which an operation of a tagged type dispatches. Instead, it dispatches on the parameter of a tagged type for which it is a primitive operation. [A primitive operation is declared in the same declarative part as the type (usually a pkg spec).] Since here you have 2 tagged types in the same declarative part, this operation is primitive for both, which violates the Ada rule. Note that in the standard containers, type Cursor is not tagged for precisely this reason: They need operations that take both a container and a cursor as parameters. The standard approach to this is to put each tagged type in a separate pkg: package Balances is type Balance_Info is ... procedure Op (Balance : in out Balance_Info); end Balances; with Balances; package Accounts is type Account_Info is ... procedure Op (Account : in out Account_Info; Balance : in out Balances.Balance_Info); end Accounts; Another approach is to make one parameter classwide; then it is primitive for only one tagged type: procedure Op (Account : in out Account_Info; Balance : in out Balance_Info'Class); A third possibility is to make the operation non-primitive by putting it in a different declarative part: package Non_Primitive is procedure Op (Account : in out Account_Info; Balance : in out Balance_Info); end Non_Primitive; A fourth approach is to make one of the types non-tagged, as with Cursor in the containers. A fifth approach is to make all types non-tagged. This approach causes the fewest problems. > class account { > public: > account_id id; > account_name name; > money total_balance; > balance_list balances; > > account(); > virtual ~account(); > > void update_balance (account_balance& ab, money amount); This is a primitive operation of type account (to use the Ada terminology) because it occurs in account's class structure. Ada has a somewhat idiosyncratic syntax for programming by extension. > }; -- Jeff Carter "Now look, Col. Batguano, if that really is your name." Dr. Strangelove 31 ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-23 17:40 ` Jeffrey R. Carter @ 2015-11-24 9:08 ` Serge Robyns 2015-11-24 16:44 ` AdaMagica 2015-11-24 17:09 ` Jeffrey R. Carter 0 siblings, 2 replies; 132+ messages in thread From: Serge Robyns @ 2015-11-24 9:08 UTC (permalink / raw) On Monday, 23 November 2015 18:40:57 UTC+1, Jeffrey R. Carter wrote: > On 11/23/2015 03:23 AM, Serge Robyns wrote: > > > Ada has only single dispatch, so an operation can only be a primitive operation > of a single tagged type (the "controlling" type). That's fine as you must have noticed. > > In Ada, neither the parameter name (Self) nor the position of the parameter in > the parameter list determine the type for which an operation of a tagged type > dispatches. Instead, it dispatches on the parameter of a tagged type for which > it is a primitive operation. [A primitive operation is declared in the same > declarative part as the type (usually a pkg spec).] I didn't know about the position, I honestly believed it had to be the 1st. I don't know where I read that. Anyway I'm reading now the A-LRM 2012. I'm using "Self" as a naming convention. > Since here you have 2 tagged types in the same declarative part, this operation > is primitive for both, which violates the Ada rule. Note that in the standard > containers, type Cursor is not tagged for precisely this reason: They need > operations that take both a container and a cursor as parameters. > > The standard approach to this is to put each tagged type in a separate pkg: I though that packaging and typing were quite independent and hence this is why I merged these two types in one package as they form a coherent assembly. I also recall some "advertisement" about that "fact". But it seems not to be that fully true. > Another approach is to make one parameter classwide; then it is primitive for > only one tagged type: Yep, learned about that in a previous post. > A fourth approach is to make one of the types non-tagged, as with Cursor in the > containers. > > A fifth approach is to make all types non-tagged. This approach causes the > fewest problems. > > This is a primitive operation of type account (to use the Ada terminology) > because it occurs in account's class structure. Ada has a somewhat idiosyncratic > syntax for programming by extension. > This is what I did in the end. The only OO feature I'm now using is DAO in the business logic part of the application. It really works very well and it is clean to read. However it would have been nice not to have do abandon tagged types in general because of the issues above. This is my biggest "disappointment". I'm not in the camp of everything has to be an object but here I would love to have my main "business" objects be implemented as tagged types. Like for example creating new specialized versions of the T_Account and per the rules you can only write "type New_T is new Old_T" for tagged types. I'm fully convinced about Ada's code quality and reliability but I'm wondering if these types of issues are not what keeps Ada "locked" into its current niche and failing to gain attraction. This is where it "transpires" that tagged types came in Ada95 and that backwards compatibility with Ada85 is giving hard time. I recall reading about the issues of controlled tagged types. Now, other languages have their own issues. When I'm reading about C++ templates it can become really hard. Yesterday I've been trying to figuring out about C++ type traits and how they are constructed. Everything except readable if you ask my opinion. Serge ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-24 9:08 ` Serge Robyns @ 2015-11-24 16:44 ` AdaMagica 2015-11-24 17:09 ` Jeffrey R. Carter 1 sibling, 0 replies; 132+ messages in thread From: AdaMagica @ 2015-11-24 16:44 UTC (permalink / raw) Am Dienstag, 24. November 2015 10:08:07 UTC+1 schrieb Serge Robyns: > > In Ada, neither the parameter name (Self) nor the position of the parameter in > > the parameter list determine the type for which an operation of a tagged type > > dispatches. Instead, it dispatches on the parameter of a tagged type for which > > it is a primitive operation. [A primitive operation is declared in the same > > declarative part as the type (usually a pkg spec).] > > I didn't know about the position, I honestly believed it had to be the 1st. I don't know where I read that. Anyway I'm reading now the A-LRM 2012. I'm using "Self" as a naming convention. If you want to use prefix notation Obj.Op, then Obj has to be the first parameter. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-24 9:08 ` Serge Robyns 2015-11-24 16:44 ` AdaMagica @ 2015-11-24 17:09 ` Jeffrey R. Carter 2015-11-24 18:37 ` Serge Robyns 2015-11-24 20:25 ` Niklas Holsti 1 sibling, 2 replies; 132+ messages in thread From: Jeffrey R. Carter @ 2015-11-24 17:09 UTC (permalink / raw) On 11/24/2015 02:08 AM, Serge Robyns wrote: > > I didn't know about the position, I honestly believed it had to be the 1st. I don't know where I read that. Anyway I'm reading now the A-LRM 2012. I'm using "Self" as a naming convention. Dispatching is described in ARM 3.9.2(2/3). Note that a function call can dispatch on its result type. > I though that packaging and typing were quite independent and hence this is why I merged these two types in one package as they form a coherent assembly. I also recall some "advertisement" about that "fact". But it seems not to be that fully true. What is called a "class" in most languages (other than Ada) involves a type declaration (a set of values and a set of operations on those values), encapsulation of the value and its operations, and information hiding of the implementation of the values. In Ada, the pkg provides encapsulation and information hiding, independent of the mechanisms for declaring types. To get the equivalent of a "class" in Ada one must put the type and operation declarations into a pkg. Pkgs, unlike "classes", are intended to be used for other things as well. Note that the Ada construct that most closely matches a "class" is a protected type. > Like for example creating new specialized versions of the T_Account and per the rules you can only write "type New_T is new Old_T" for tagged types. I'm not sure what you're saying here, but one can derive a new type from any type, not just tagged types. This existed in Ada 83: type George is new Integer [range Low .. High]; The Ada-95 designers built on that to provide type extension: type George is new Martha with record ... The only thing you *need* tagged types for in Ada is finalization. They also provide Object.Operation notation, which is nice, and many types are declared tagged solely to provide that (the containers are examples of this). Type extension, combined with a container, also provides a kind of syntactic noise that allows self-referential types without access types/values. Other than those uses, with adequate thought before beginning coding, one can implement anything more clearly without tagged types than with. -- Jeff Carter "Whatever it is, I'm against it." Horse Feathers 46 ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-24 17:09 ` Jeffrey R. Carter @ 2015-11-24 18:37 ` Serge Robyns 2015-11-24 20:18 ` Jeffrey R. Carter 2015-11-24 20:25 ` Niklas Holsti 1 sibling, 1 reply; 132+ messages in thread From: Serge Robyns @ 2015-11-24 18:37 UTC (permalink / raw) On Tuesday, 24 November 2015 18:09:14 UTC+1, Jeffrey R. Carter wrote: > > Like for example creating new specialized versions of the T_Account and per the rules you can only write "type New_T is new Old_T" for tagged types. > > The Ada-95 designers built on that to provide type extension: > > type George is new Martha with record ... This is what I get with gnat2015: type derived from untagged type cannot have extension for: type T_Control is record Name : T_Control_Name; end record; type T_Counter_Control is new T_Control with record Counter : Natural; end record; PS: T_Control_Name is a type derived straight from T_Strings_20.Bounded_String (instance of the generic package) in case this should matter but I believe not. > The only thing you *need* tagged types for in Ada is finalization. They also > provide Object.Operation notation, which is nice, and many types are declared > tagged solely to provide that (the containers are examples of this). Type > extension, combined with a container, also provides a kind of syntactic noise > that allows self-referential types without access types/values. > > Other than those uses, with adequate thought before beginning coding, one can > implement anything more clearly without tagged types than with. I agree and so far the need is limited. However I do love the obj.method notation which was my initial reason for using tagged types everywhere. And I do expect needs on the business objects, like indeed finalization and a few specializations. True back in the 80's we did those 'tricks' without any OO language. Hence facing such issues now will avoid me to re-design the whole thing later on because of an oversight due to lack of Ada experience. This is my biggest fear for me; to pursue my project in Ada rather than to go more for my older friends. I'm more the guy that should read Ada for the C programmer with some experience in old C++ and Java. And besides this group, I know no one using Ada. Serge ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-24 18:37 ` Serge Robyns @ 2015-11-24 20:18 ` Jeffrey R. Carter 2015-11-24 20:40 ` Serge Robyns 0 siblings, 1 reply; 132+ messages in thread From: Jeffrey R. Carter @ 2015-11-24 20:18 UTC (permalink / raw) On 11/24/2015 11:37 AM, Serge Robyns wrote: > > This is what I get with gnat2015: > type derived from untagged type cannot have extension > > for: > type T_Control is record > Name : T_Control_Name; > end record; > > type T_Counter_Control is new T_Control with record > Counter : Natural; > end record; Yes, only a tagged type can be extended (and any derivation from a tagged type requires an extension), but a new type can be derived from any type: type Control_Info is record Active : Boolean; end record; type External_Control is new Control_Info; for External_Control use record Active at 1 range 3 .. 3; end record; > > I agree and so far the need is limited. However I do love the obj.method notation which was my initial reason for using tagged types everywhere. And I do expect needs on the business objects, like indeed finalization and a few specializations. True back in the 80's we did those 'tricks' without any OO language. Hence facing such issues now will avoid me to re-design the whole thing later on because of an oversight due to lack of Ada experience. This is my biggest fear for me; to pursue my project in Ada rather than to go more for my older friends. I'm more the guy that should read Ada for the C programmer with some experience in old C++ and Java. And besides this group, I know no one using Ada. Have you looked at /Ada Distilled/? It is Ada for the experienced programmer. -- Jeff Carter "Whatever it is, I'm against it." Horse Feathers 46 ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-24 20:18 ` Jeffrey R. Carter @ 2015-11-24 20:40 ` Serge Robyns 0 siblings, 0 replies; 132+ messages in thread From: Serge Robyns @ 2015-11-24 20:40 UTC (permalink / raw) On Tuesday, 24 November 2015 21:18:10 UTC+1, Jeffrey R. Carter wrote: > > Have you looked at /Ada Distilled/? It is Ada for the experienced programmer. > Yes I did and liked it, I read the version of May this year. But as I said earlier, I still do "miss" some "particularities". For 2012 specifics I did read the excellent 2012 rationale PDF's. Programming in Ada 2012 seems to be an interesting book but it isn't a cheap one. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-24 17:09 ` Jeffrey R. Carter 2015-11-24 18:37 ` Serge Robyns @ 2015-11-24 20:25 ` Niklas Holsti 2015-11-24 21:48 ` Jeffrey R. Carter 1 sibling, 1 reply; 132+ messages in thread From: Niklas Holsti @ 2015-11-24 20:25 UTC (permalink / raw) On 15-11-24 19:09 , Jeffrey R. Carter wrote: > Note that the Ada construct that most closely matches a "class" is a protected type. Huh? What have mutual exclusion and interrupt-handling to do with "class"? Ok, some languages have "synchronized" classes, mixing the two concepts. Perhaps you mean that there is a syntactic resemblance, in that the operations and data of a protected type are indeed enclosed in dedicated "is" .. "end" brackets? > The only thing you *need* tagged types for in Ada is finalization. Tagged types and finalization are never "needed" in the Turing sense :-) I find that tagged types are a huge help in decoupling dependencies between packages. The root type (and 'Class type) can often have many fewer dependencies than the child types, greatly helping to localize cross-package dependencies. YMMV of course. -- Niklas Holsti Tidorum Ltd niklas holsti tidorum fi . @ . ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-24 20:25 ` Niklas Holsti @ 2015-11-24 21:48 ` Jeffrey R. Carter 2015-11-25 8:24 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Jeffrey R. Carter @ 2015-11-24 21:48 UTC (permalink / raw) On 11/24/2015 01:25 PM, Niklas Holsti wrote: > On 15-11-24 19:09 , Jeffrey R. Carter wrote: > >> Note that the Ada construct that most closely matches a "class" is a protected >> type. > > Huh? What have mutual exclusion and interrupt-handling to do with "class"? Nothing. > Ok, some languages have "synchronized" classes, mixing the two concepts. > > Perhaps you mean that there is a syntactic resemblance, in that the operations > and data of a protected type are indeed enclosed in dedicated "is" .. "end" > brackets? I mean that a protected type defines a set of values and operations on those values, encapsulates the values and operations, and hides the implementation of the values. -- Jeff Carter "Whatever it is, I'm against it." Horse Feathers 46 ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-24 21:48 ` Jeffrey R. Carter @ 2015-11-25 8:24 ` Dmitry A. Kazakov 2015-11-25 11:22 ` Serge Robyns 2015-11-25 12:27 ` G.B. 0 siblings, 2 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-11-25 8:24 UTC (permalink / raw) On Tue, 24 Nov 2015 14:48:24 -0700, Jeffrey R. Carter wrote: > I mean that a protected type defines a set of values and operations on those > values, This is a type, not yet a class. BTW, it is not even a proper class-type (a root type in Ada terms) because you could not derive anything from it. > encapsulates the values and operations, and hides the implementation of > the values. In fact it does not hide the implementation of values. That is one of its design flaws. The implementation of protected components is mixed with the interface. It should have had public components with read access acting as a protected function and write access doing as a protected procedure. The private components should have been declared in the package's private section or the package body. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-25 8:24 ` Dmitry A. Kazakov @ 2015-11-25 11:22 ` Serge Robyns 2015-11-25 17:38 ` Dmitry A. Kazakov 2015-11-25 12:27 ` G.B. 1 sibling, 1 reply; 132+ messages in thread From: Serge Robyns @ 2015-11-25 11:22 UTC (permalink / raw) On Wednesday, 25 November 2015 09:25:07 UTC+1, Dmitry A. Kazakov wrote: > On Tue, 24 Nov 2015 14:48:24 -0700, Jeffrey R. Carter wrote: > > In fact it does not hide the implementation of values. That is one of its > design flaws. The implementation of protected components is mixed with the > interface. It should have had public components with read access acting as > a protected function and write access doing as a protected procedure. The > private components should have been declared in the package's private > section or the package body. I fully agree with you. This is what I really wanted to have. However, not all types need to be fully ADT's. Like in my example an account has a set of properties which may be revealed in the public part. Why hide them? Following your logic, we could have "business" objects with well know properties, then inherit from it to create implementation variants for persistent access with getters/setters. This is how I could use the "Bridge" Design Pattern or "Adapter". Now, I've used the following construct instead (which is very close to the Bridge pattern. package Accounts: type T_Account => defines the basic properties and operations. package Accounts.DAO: type Factory is limited interface => Defines an interface to read/write clients from a "data store" as a child package of accounts. package Data_Store : type T_Abstract_Date_Store is limited interface Accounts.DAO and Client.DAO and ....; => Assembles all the interfaces in a single interface type. package Data_Store.In_Memory: type T_Data_Store is limited new T_Abstract_Data_Store with private; => implements an in memory data_store package Data_Store.SQL: type T_Data_Store is limited new T_Abstract_Data_Store with private; => implements a GNATCOLL.SQL data_store. I could create a Gnoga version too. Which I'm seriously considering. This is the beauty of OO. Now all business functions have to use the DOA functions (getters/setters) to retrieve and update objects. Initially I was hoping to hide the DAO behind the business object. Now I've to do the two actions in the business logic: first update T_Account object and then call T_Account.DAO operations to "retrieve" or "store". After giving it some thought I've accept this as this enable me to have "control" on the moment for the updates, commits and roll-bacls to the "store". ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-25 11:22 ` Serge Robyns @ 2015-11-25 17:38 ` Dmitry A. Kazakov 2015-11-26 11:30 ` Serge Robyns 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-11-25 17:38 UTC (permalink / raw) On Wed, 25 Nov 2015 03:22:57 -0800 (PST), Serge Robyns wrote: > However, not all types need to be fully ADT's. They are. If you don't define operations, the compiler does anyway. > Like in my example an > account has a set of properties which may be revealed in the public part. > Why hide them? Nobody asked that. What you call "property" is certainly an operation of the ADT generated by the compiler. If you had type T is record X : ... end record; That would implicitly declare among others the operations to access the member X, i.e. a getter T.X and a setter T.X. > Following your logic, we could have "business" objects > with well know properties, then inherit from it to create implementation > variants for persistent access with getters/setters. Alas, you cannot override operations of built-in interfaces. It is an Ada 83 heritage. > Now all business functions have to use the DOA functions (getters/setters) > to retrieve and update objects. Initially I was hoping to hide the DAO > behind the business object. Now I've to do the two actions in the > business logic: first update T_Account object and then call T_Account.DAO > operations to "retrieve" or "store". After giving it some thought I've > accept this as this enable me to have "control" on the moment for the > updates, commits and roll-bacls to the "store". The object is a proxy to a persistent object. It is OK to update the proxy without committing changes to the persistent counterpart. But note that you still want to extend compiler-generated setters. E.g. for marking the proxy object out-of-sync. Thus when a transaction is getting committed the connection handler would store all updated objects. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-25 17:38 ` Dmitry A. Kazakov @ 2015-11-26 11:30 ` Serge Robyns 2015-11-26 13:14 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Serge Robyns @ 2015-11-26 11:30 UTC (permalink / raw) Dmitry, On Wednesday, 25 November 2015 18:38:53 UTC+1, Dmitry A. Kazakov wrote: > The object is a proxy to a persistent object. It is OK to update the proxy > without committing changes to the persistent counterpart. But note that you > still want to extend compiler-generated setters. E.g. for marking the proxy > object out-of-sync. Thus when a transaction is getting committed the > connection handler would store all updated objects. Can you please elaborate what you mean by a "compiler-generated" setter? Because from what I do understand from your explanation about marking the proxy is something I'm looking for and maybe to far while it's right under my nose. Serge ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-26 11:30 ` Serge Robyns @ 2015-11-26 13:14 ` Dmitry A. Kazakov 2015-11-26 14:27 ` Serge Robyns 2015-11-29 17:59 ` Jacob Sparre Andersen 0 siblings, 2 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-11-26 13:14 UTC (permalink / raw) On Thu, 26 Nov 2015 03:30:09 -0800 (PST), Serge Robyns wrote: > On Wednesday, 25 November 2015 18:38:53 UTC+1, Dmitry A. Kazakov wrote: >> The object is a proxy to a persistent object. It is OK to update the proxy >> without committing changes to the persistent counterpart. But note that you >> still want to extend compiler-generated setters. E.g. for marking the proxy >> object out-of-sync. Thus when a transaction is getting committed the >> connection handler would store all updated objects. > > Can you please elaborate what you mean by a "compiler-generated" setter? > Because from what I do understand from your explanation about marking the > proxy is something I'm looking for and maybe to far while it's right under > my nose. type T is record X : Integer; end record; The compiler generated setter is something like inventing Ada-like notation: procedure ".X" (Left : in out T; Right : Integer); Called as follows: Y : T; begin Y.X := 10; If Ada were a better language you could override getters/setters and provide your own implementation: type T is private record -- This is not Ada! X : Integer; end record; private type T is new DB.Handle with null record; -- No Integer members! procedure ".X" (Left : in out T; Right : Integer); function ".X" (Left : T) return Integer); P.S. Surely, at some point there would a kludge invented with aspects as it was done for array indexing... -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-26 13:14 ` Dmitry A. Kazakov @ 2015-11-26 14:27 ` Serge Robyns 2015-11-26 15:16 ` J-P. Rosen 2015-11-29 17:59 ` Jacob Sparre Andersen 1 sibling, 1 reply; 132+ messages in thread From: Serge Robyns @ 2015-11-26 14:27 UTC (permalink / raw) On Thursday, 26 November 2015 14:15:01 UTC+1, Dmitry A. Kazakov wrote: > If Ada were a better language you could override getters/setters and > provide your own implementation: > > type T is private record -- This is not Ada! > X : Integer; > end record; > private > type T is new DB.Handle with null record; -- No Integer members! > procedure ".X" (Left : in out T; Right : Integer); > function ".X" (Left : T) return Integer); > > P.S. Surely, at some point there would a kludge invented with aspects as it > was done for array indexing... Got you. Yes that would be nice but my need is current :-( I agree that this would be a marvelous enhancement. We could write code as below with the ability to "override" T.X function and procedure and also inherit it from parent types like when implementing interfaces. type T_Stored is new T and T_DOA; A : T; ... A.X := 10; ... if A.X /= 10 then null; end if; ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-26 14:27 ` Serge Robyns @ 2015-11-26 15:16 ` J-P. Rosen 2015-11-26 18:27 ` Serge Robyns 0 siblings, 1 reply; 132+ messages in thread From: J-P. Rosen @ 2015-11-26 15:16 UTC (permalink / raw) Le 26/11/2015 15:27, Serge Robyns a écrit : > n Thursday, 26 November 2015 14:15:01 UTC+1, Dmitry A. Kazakov wrote: >> > If Ada were a better language you could override getters/setters and >> > provide your own implementation: >> > >> > type T is private record -- This is not Ada! >> > X : Integer; >> > end record; >> > private >> > type T is new DB.Handle with null record; -- No Integer members! >> > procedure ".X" (Left : in out T; Right : Integer); >> > function ".X" (Left : T) return Integer); >> > >> > P.S. Surely, at some point there would a kludge invented with aspects as it >> > was done for array indexing... > Got you. Yes that would be nice but my need is current :-( > I agree that this would be a marvelous enhancement. Nothing marvelous, just saving keystrokes. You can always make a private type and explicit subprograms to access "virtual" components. -- J-P. Rosen Adalog 2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00 http://www.adalog.fr ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-26 15:16 ` J-P. Rosen @ 2015-11-26 18:27 ` Serge Robyns 2015-11-26 21:20 ` J-P. Rosen 0 siblings, 1 reply; 132+ messages in thread From: Serge Robyns @ 2015-11-26 18:27 UTC (permalink / raw) On Thursday, 26 November 2015 16:16:34 UTC+1, J-P. Rosen wrote: unction ".X" (Left : T) return Integer); > >> > > >> > P.S. Surely, at some point there would a kludge invented with aspects as it > >> > was done for array indexing... > > Got you. Yes that would be nice but my need is current :-( > > I agree that this would be a marvelous enhancement. > > Nothing marvelous, just saving keystrokes. You can always make a private > type and explicit subprograms to access "virtual" components. > Isn't the purpose of higher programming languages not to save keystrokes, simplifying abstractions, enabling more powerful algorithms to be expressed directly? My first language was assembly language. I've been programming 100K's of code and could literally do whatever I wanted, even change the OS behavior through hooking into or jumping at very specific places inside the OS ROM. I felt very mighty, my code was actually very readable and I was a very fast programmer on top of that. But it took always more effort to build or call a callable subroutine than just using high level programming language, I had to take care of building a stack, fill the right values into the right registers, recover return values, etc. where as otherwise I could just write I := Integer'Value(S); True, assembly isn't portable but if this is the only purpose then we would have stick to "simple" languages such BCPL for example. C would never have appeared, Ada would not have been invented nor Smalltalk and the list is almost endless. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-26 18:27 ` Serge Robyns @ 2015-11-26 21:20 ` J-P. Rosen 2015-11-27 8:37 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: J-P. Rosen @ 2015-11-26 21:20 UTC (permalink / raw) Le 26/11/2015 19:27, Serge Robyns a écrit : > Isn't the purpose of higher programming languages not to save > keystrokes, simplifying abstractions, enabling more powerful > algorithms to be expressed directly? Sure, but here the proposal is to offer the same functionality, with a different syntax. It may be more pleasant, but it's not higher level. That's what is called syntactic sugar. (Sugar is pleasant, isn't it? But not in my coffee, no thanks ;-) ) -- J-P. Rosen Adalog 2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00 http://www.adalog.fr ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-26 21:20 ` J-P. Rosen @ 2015-11-27 8:37 ` Dmitry A. Kazakov 2015-11-27 12:58 ` J-P. Rosen 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-11-27 8:37 UTC (permalink / raw) On Thu, 26 Nov 2015 22:20:41 +0100, J-P. Rosen wrote: > Le 26/11/2015 19:27, Serge Robyns a écrit : >> Isn't the purpose of higher programming languages not to save >> keystrokes, simplifying abstractions, enabling more powerful >> algorithms to be expressed directly? > Sure, but here the proposal is to offer the same functionality, with a > different syntax. It may be more pleasant, but it's not higher level. It is higher level because it introduces an abstract record interface. It also supports separation of interface and implementation which built-in record types fail to provide. And it would make the language type system more regular. Usual claims of separation OO language parts from non-OO parts is nonsense. There is no such thing, but reasonably designed types and compiler-magic assisted kludges. > That's what is called syntactic sugar. (Sugar is pleasant, isn't it? But > not in my coffee, no thanks ;-) ) Why do you use record types at all? It surely is just sugar dust put on a memory chunk. You certainly could declare Get and Set operations to extract and set members at the specified memory offsets. No? -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-27 8:37 ` Dmitry A. Kazakov @ 2015-11-27 12:58 ` J-P. Rosen 2015-11-27 13:39 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: J-P. Rosen @ 2015-11-27 12:58 UTC (permalink / raw) Le 27/11/2015 09:37, Dmitry A. Kazakov a écrit : >> That's what is called syntactic sugar. (Sugar is pleasant, isn't it? But >> > not in my coffee, no thanks ;-) ) > Why do you use record types at all? It surely is just sugar dust put on a > memory chunk. You certainly could declare Get and Set operations to extract > and set members at the specified memory offsets. No? 1) For types not exported by packages 2) For types exported by packages where you want the structure to be public, like information returned by queries. You don't always need to hide information. And when a construct provides higher abstraction level, it is NOT syntactic sugar. A record provides typing and controls over raw memory. -- J-P. Rosen Adalog 2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00 http://www.adalog.fr ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-27 12:58 ` J-P. Rosen @ 2015-11-27 13:39 ` Dmitry A. Kazakov 2015-11-30 22:22 ` Randy Brukardt 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-11-27 13:39 UTC (permalink / raw) On Fri, 27 Nov 2015 13:58:32 +0100, J-P. Rosen wrote: > Le 27/11/2015 09:37, Dmitry A. Kazakov a écrit : >>> That's what is called syntactic sugar. (Sugar is pleasant, isn't it? But >>> > not in my coffee, no thanks ;-) ) >> Why do you use record types at all? It surely is just sugar dust put on a >> memory chunk. You certainly could declare Get and Set operations to extract >> and set members at the specified memory offsets. No? > 1) For types not exported by packages > 2) For types exported by packages where you want the structure to be > public, like information returned by queries. You don't always need to > hide information. But when I need I must be able to do it. > And when a construct provides higher abstraction level, it is NOT > syntactic sugar. A record provides typing and controls over raw memory. q.e.d. 1. Raw memory is an implementation detail. A record could provide all that for a lot of things beyond raw memory. 2. What makes you think that the compiler always knows better how to control raw memory? -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-27 13:39 ` Dmitry A. Kazakov @ 2015-11-30 22:22 ` Randy Brukardt 2015-12-01 8:46 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: Randy Brukardt @ 2015-11-30 22:22 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:11das66l3vhic$.1stkau3dqp6ld.dlg@40tude.net... ... > 2. What makes you think that the compiler always knows better how to > control raw memory? Because it's true?? :-) In an ideal world (with an ideal programming language), the compiler always knows far more than the programmer about the target, the access patterns, and so on. To the extent that there is information that the compiler doesn't have, the focus should be on giving it that information, not on telling it what to do. (For instance, it is better to tell a compiler which subprograms are "hot" than to tell it that it must inline certain subprograms -- inlining is just one possibly way of improving the performance and the compiler is in a better position to juggle the trade-offs than any programmer could be. That's one of the reasons that just-in-time compilation produces better performance than one would imagine just based on traditionally compiling individual subprograms.) In the real world, of course, there are limitations on compilation time, execution information, and so on, but it still is the case that it is rare that a programmer can really do better at these things (especially at the "raw memory" level) than a decent compiler. That's why it's unfortunate that most programming languages (and Ada is no exception) focus too much on features that can be implemented easily and less on improving abstraction. (But of course, get too fancy and the features can't be implemented efficiently at all, which defeats the purpose.) Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-30 22:22 ` Randy Brukardt @ 2015-12-01 8:46 ` Dmitry A. Kazakov 2015-12-01 11:19 ` G.B. 2015-12-02 19:27 ` Randy Brukardt 0 siblings, 2 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-12-01 8:46 UTC (permalink / raw) On Mon, 30 Nov 2015 16:22:12 -0600, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:11das66l3vhic$.1stkau3dqp6ld.dlg@40tude.net... > ... >> 2. What makes you think that the compiler always knows better how to >> control raw memory? > > Because it's true?? :-) > > In an ideal world (with an ideal programming language), the compiler always > knows far more than the programmer about the target, In most cases yes. But note you focused on the hardware here. Even with hardware, the compiler knows nothing about the layout of a packet from some I/O protocol. Countless requests here in c.l.a. concerning instructing the compiler to do it right is a proof. But when it becomes the problem space the compiler knows absolutely nothing about handling proxy objects located in the memory. A row of a DB record result set is a record for which the compiler has no slightest idea how to map it (onto the DB environment, connection, cursor). > To the extent that there is information that the compiler doesn't > have, the focus should be on giving it that information, not on telling it > what to do. (For instance, it is better to tell a compiler which subprograms > are "hot" than to tell it that it must inline certain subprograms -- > inlining is just one possibly way of improving the performance and the > compiler is in a better position to juggle the trade-offs than any > programmer could be. That's one of the reasons that just-in-time compilation > produces better performance than one would imagine just based on > traditionally compiling individual subprograms.) Not exactly. It is true when we talk about non-functional requirements and wrong for the functional ones. The example with inlining works because it is non-functional. > In the real world, of course, there are limitations on compilation time, > execution information, and so on, but it still is the case that it is rare > that a programmer can really do better at these things (especially at the > "raw memory" level) than a decent compiler. That's why it's unfortunate that > most programming languages (and Ada is no exception) focus too much on > features that can be implemented easily and less on improving abstraction. There is no contradiction in providing simple building blocks for higher level abstractions. There is no reason why indexing or record member access should be less efficient when a user-defined implementation allowed, but not actually used. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-12-01 8:46 ` Dmitry A. Kazakov @ 2015-12-01 11:19 ` G.B. 2015-12-01 13:56 ` Dmitry A. Kazakov 2015-12-02 19:27 ` Randy Brukardt 1 sibling, 1 reply; 132+ messages in thread From: G.B. @ 2015-12-01 11:19 UTC (permalink / raw) On 01.12.15 09:46, Dmitry A. Kazakov wrote: > On Mon, 30 Nov 2015 16:22:12 -0600, Randy Brukardt wrote: > > some > I/O protocol. Countless requests here in c.l.a. concerning instructing the > compiler to do it right is a proof. > > But when it becomes the problem space the compiler knows absolutely nothing > about handling proxy objects located in the memory. A row of a DB record > result set is a record for which the compiler has no slightest idea how to > map it (onto the DB environment, connection, cursor). Compiler vendors convincing the bosses of the database vending families to agree on giving up control over I/O and representation, now that will be quite something! ;-) > There is no reason why indexing or record member access > should be less efficient when a user-defined implementation allowed, but > not actually used. Sounds a bit like user defined aspects of compilation? What guarantees would the compiler be able to generate that user defined mechanics will work at the same level of assurance as that of "regular" records? ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-12-01 11:19 ` G.B. @ 2015-12-01 13:56 ` Dmitry A. Kazakov 2015-12-01 16:05 ` G.B. 2015-12-02 19:33 ` Randy Brukardt 0 siblings, 2 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-12-01 13:56 UTC (permalink / raw) On Tue, 1 Dec 2015 12:19:02 +0100, G.B. wrote: > On 01.12.15 09:46, Dmitry A. Kazakov wrote: >> There is no reason why indexing or record member access >> should be less efficient when a user-defined implementation allowed, but >> not actually used. > > Sounds a bit like user defined aspects of compilation? It is not an aspect. Aspect is a view, characteristic, feature: http://www.thefreedictionary.com/aspect It means that aspect may not change the semantics. User-defined operation is exactly the opposite. It does not change the view. The object is still viewed as an array or a record. It is the semantics that gets changed. This is also what is wrong with the aspect approach in general. From the software design point of view it implies that you add a view to something already existing, already designed in its own way, a cherry on the top. That is either a wrong way to design software or else a superfluous thing. I suspect that creators of aspects considered the latter, as something unserious. The end users do it as the former, which results in poor design. > What > guarantees would the compiler be able to generate that user > defined mechanics will work at the same level of assurance > as that of "regular" records? Generated guaranties? You lost me here. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-12-01 13:56 ` Dmitry A. Kazakov @ 2015-12-01 16:05 ` G.B. 2015-12-01 17:58 ` Dmitry A. Kazakov 2015-12-02 19:33 ` Randy Brukardt 1 sibling, 1 reply; 132+ messages in thread From: G.B. @ 2015-12-01 16:05 UTC (permalink / raw) On 01.12.15 14:56, Dmitry A. Kazakov wrote: > On Tue, 1 Dec 2015 12:19:02 +0100, G.B. wrote: > >> On 01.12.15 09:46, Dmitry A. Kazakov wrote: > >>> There is no reason why indexing or record member access >>> should be less efficient when a user-defined implementation allowed, but >>> not actually used. >> >> Sounds a bit like user defined aspects of compilation? > > It is not an aspect. Aspect is a view, characteristic, feature: > > http://www.thefreedictionary.com/aspect > > It means that aspect may not change the semantics. > > User-defined operation is exactly the opposite. It does not change the > view. The object is still viewed as an array or a record. It is the > semantics that gets changed. I take it that by "semantics" you mean the simplest meaning of this word, viz. the operations that happen to become generated for a ".X" function, say, as a consequence of compilation? If, however, a user may define what "rec.x" will do, then this changes an aspect (sic) of compilation: what was previously defined by the LRM (or would appear to be defined by the language) does now seem to flow from the job of the programmer, and becomes a property of the program, as it redefines certain occurrences of ".". The compiler now needs to ensure something different when translating "rec.x". >> What >> guarantees would the compiler be able to generate that user >> defined mechanics will work at the same level of assurance >> as that of "regular" records? > > Generated guaranties? You lost me here. The current Ada language makes the compiler emit code for "Rec.X" that is going to have a somewhat predictable effect. It does so driven by what it the LRM says. That much is a guaranteed outcome. If users are allowed to change the semantics of "rec.x", then what level of assurance do we get? We'd take away a basic building block from the set of unequivocally predictable building blocks. Is it hubris or are there hidden powers in overloading what now are non-operators? ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-12-01 16:05 ` G.B. @ 2015-12-01 17:58 ` Dmitry A. Kazakov 2015-12-02 13:06 ` G.B. 0 siblings, 1 reply; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-12-01 17:58 UTC (permalink / raw) On Tue, 1 Dec 2015 17:05:28 +0100, in comp.lang.ada you wrote: > On 01.12.15 14:56, Dmitry A. Kazakov wrote: >> On Tue, 1 Dec 2015 12:19:02 +0100, G.B. wrote: >> >>> On 01.12.15 09:46, Dmitry A. Kazakov wrote: >> >>>> There is no reason why indexing or record member access >>>> should be less efficient when a user-defined implementation allowed, but >>>> not actually used. >>> >>> Sounds a bit like user defined aspects of compilation? >> >> It is not an aspect. Aspect is a view, characteristic, feature: >> >> http://www.thefreedictionary.com/aspect >> >> It means that aspect may not change the semantics. >> >> User-defined operation is exactly the opposite. It does not change the >> view. The object is still viewed as an array or a record. It is the >> semantics that gets changed. > > I take it that by "semantics" you mean the simplest meaning > of this word, viz. the operations that happen to become generated > for a ".X" function, say, as a consequence of compilation? > > If, however, a user may define what "rec.x" will do, then > this changes an aspect (sic) of compilation: what was previously > defined by the LRM (or would appear to be defined by the > language) does now seem to flow from the job of the programmer, > and becomes a property of the program, as it redefines certain > occurrences of ".". Who cares? Less defined by the RM then better. Any predefined semantics is a necessary evil. If not necessary, then just evil. > The compiler now needs to ensure something different when > translating "rec.x". The compiler needs to ensure what the programmer means (assuming correct language use) >>> What >>> guarantees would the compiler be able to generate that user >>> defined mechanics will work at the same level of assurance >>> as that of "regular" records? >> >> Generated guaranties? You lost me here. > > The current Ada language makes the compiler emit code for "Rec.X" > that is going to have a somewhat predictable effect. It does not make sense. For *any* legal construct the compiler should emit code with a predictable effect. Otherwise the language is ill-defined. The correct sentence is probably related to the program readability. That is up to the programmer and how he expresses the problem space in the language terms. I don't see how Employee.Salary is less readable, predictable, etc than Get_Salary(Employee). > If users are allowed to change the semantics of "rec.x", then > what level of assurance do we get? Same level as when the users are allowed to change the semantics of Foo(x), x+y etc. > We'd take away a basic building > block from the set of unequivocally predictable building blocks. Member extraction/setting operations is not a basic building block. Oh, I forgot, that you are in the anti-typed camp. So let me explain. There is *no* free operations. There are only types. The member operation .X is one of the built-in operations of the built-in record type. You don't loose it in no way if you allow *other* types have operations named this way or implementing same abstract interface. If you don't believe me, look at fully qualified names, they have "." inside. What a horror! -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-12-01 17:58 ` Dmitry A. Kazakov @ 2015-12-02 13:06 ` G.B. 2015-12-02 13:31 ` Dmitry A. Kazakov 0 siblings, 1 reply; 132+ messages in thread From: G.B. @ 2015-12-02 13:06 UTC (permalink / raw) On 01.12.15 18:58, Dmitry A. Kazakov wrote: > Less defined by the RM then better. (OK, yet, taking this to absurd extremes, the ideal language would be undefined...) > Any predefined semantics is > a necessary evil. If not necessary, then just evil. How much predefined semantics is (un)necessary? In what circumstances? How much less will be more productive? And conducive to correct use of the language? How? (request for example below) For example, Apple is making "object properties" (dot notation) a convenience for connecting them to key-value coding. >> The compiler now needs to ensure something different when >> translating "rec.x". > > The compiler needs to ensure what the programmer means (assuming correct > language use) Right. Also, the programmer might want ways of expressing volatility of virtual components, of addressing representation issues, etc. In this regard, could there be a novel approach to protecting access when defining ".X" instead of defining a plain old prim_op? > For *any* legal construct the compiler should emit > code with a predictable effect. Otherwise the language is ill-defined. Of course, what matters is the kind of predictability that a reviewer needs in order to check, efficiently, that the compiled code is of a kind that he could have predicted. This kind of efficient predictability suffers the more, I think, the more the programmer takes hold of defining things. Unless---which is why I'm asking--- the compiler can still manage to restore predictability in terms that are language terms of a kind that provide for said efficiency. So that no need for further study of the program's own definitions arises. > I don't see how Employee.Salary is less readable, > predictable, etc than Get_Salary(Employee). I'll be fine with ".X" if it offers any real benefit at virtually no cost. So, how will a record abstraction be usefully different from a private type? Can you give an example, say, built around procedure Op (X : ...; V : record'Class); -- (sic) I'm not opposed to new notations ("Who cares?" applies here ;): For example, I have often longed for a notation that expresses message sending. Related to that, in Ada terms, sequential calls look like concurrent calls. I don't always like that, because in one sense they are fundamentally different and I may want to state that they are. Granted, when calls are near "select", context does suggest "non-sequential". ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-12-02 13:06 ` G.B. @ 2015-12-02 13:31 ` Dmitry A. Kazakov 0 siblings, 0 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-12-02 13:31 UTC (permalink / raw) On Wed, 2 Dec 2015 14:06:04 +0100, G.B. wrote: > On 01.12.15 18:58, Dmitry A. Kazakov wrote: > >> Less defined by the RM then better. > > (OK, yet, taking this to absurd extremes, the ideal language > would be undefined...) It is similar to the minimum set of axioms in a formal system, or a minimal instruction set, minimal alphabet, minimal numeral set etc. Of course it does not mean that the ideal language must be minimal. But blown languages are not ideal either. >> Any predefined semantics is >> a necessary evil. If not necessary, then just evil. > > How much predefined semantics is (un)necessary? The lower boundary is Turing completeness, obviously. > In what circumstances? > > How much less will be more productive? And conducive to correct > use of the language? How? (request for example below) > For example, Apple is making "object properties" (dot notation) > a convenience for connecting them to key-value coding. There is some optimum. But anything you could move at the library level and beyond should be. >>> The compiler now needs to ensure something different when >>> translating "rec.x". >> >> The compiler needs to ensure what the programmer means (assuming correct >> language use) > > Right. Also, the programmer might want ways of expressing volatility > of virtual components, of addressing representation issues, etc. Syntactically? Probably not. At least not in Ada's way where same syntax is used for. > In this regard, could there be a novel approach to protecting > access when defining ".X" instead of defining a plain old prim_op? There is nothing specifically "protecting" in dot-syntax. It is used for all sorts of calls in Ada. >> For *any* legal construct the compiler should emit >> code with a predictable effect. Otherwise the language is ill-defined. > > Of course, what matters is the kind of predictability that a reviewer > needs in order to check, efficiently, that the compiled code is of > a kind that he could have predicted. It is not achievable in a higher level language, only in Assembler. Neither it is required. The reader should understand the semantics and programmer's intent rather than machine instructions generated. >> I don't see how Employee.Salary is less readable, >> predictable, etc than Get_Salary(Employee). > > I'll be fine with ".X" if it offers any real benefit at virtually > no cost. So, how will a record abstraction be usefully different > from a private type? Because in this case Employee is seen as a record in the problem space. > Can you give an example, say, built around > > procedure Op (X : ...; V : record'Class); -- (sic) Example of what? Of tuples? They are everywhere in almost all problem spaces. Simply observe that an implementation of tuple need not to be a record. E.g. Color with RGB , HLS, L*a*b record views. > For example, I have often longed for a notation that expresses > message sending. You should look no further than task interfaces. Granted, they should be fixed. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-12-01 13:56 ` Dmitry A. Kazakov 2015-12-01 16:05 ` G.B. @ 2015-12-02 19:33 ` Randy Brukardt 1 sibling, 0 replies; 132+ messages in thread From: Randy Brukardt @ 2015-12-02 19:33 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:51sbiz7sr8o7$.9wqd8z5xjlf8$.dlg@40tude.net... > On Tue, 1 Dec 2015 12:19:02 +0100, G.B. wrote: ... >> Sounds a bit like user defined aspects of compilation? > > It is not an aspect. Aspect is a view, characteristic, feature: > > http://www.thefreedictionary.com/aspect > > It means that aspect may not change the semantics. Ada 83 tried that fiction (both for representation clauses and pragmas), but the results were laughable. Almost anything you can do modifies the sematics in some subtle way. (The only pragma that really doesn't modify the semantics is List(On)). A better way to view them is as modifiers or specifiers that reign in implementation freedom and/or add additional capabilities. Perhaps a better term could have been used, but as they were already called aspects in Ada 95 (and probably Ada 83, I'm not going to look right now), we left the terminology the same. Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-12-01 8:46 ` Dmitry A. Kazakov 2015-12-01 11:19 ` G.B. @ 2015-12-02 19:27 ` Randy Brukardt 1 sibling, 0 replies; 132+ messages in thread From: Randy Brukardt @ 2015-12-02 19:27 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:6y03ogx0fsk8$.n0ldd6cud931$.dlg@40tude.net... > On Mon, 30 Nov 2015 16:22:12 -0600, Randy Brukardt wrote: ... >> To the extent that there is information that the compiler doesn't >> have, the focus should be on giving it that information, not on telling >> it >> what to do. (For instance, it is better to tell a compiler which >> subprograms >> are "hot" than to tell it that it must inline certain subprograms -- >> inlining is just one possibly way of improving the performance and the >> compiler is in a better position to juggle the trade-offs than any >> programmer could be. That's one of the reasons that just-in-time >> compilation >> produces better performance than one would imagine just based on >> traditionally compiling individual subprograms.) > > Not exactly. It is true when we talk about non-functional requirements and > wrong for the functional ones. The example with inlining works because it > is non-functional. "Functional" requirements are the entire point of the program and of course have to be described to the compiler. It's not the compiler's job to guess functional requirements! It would be writing your program for you in that case, and that only works in very limited circumstances (not for a general programming language like Ada). That includes all interfacing to things outside of the compiler's universe (that includes all of your other examples, such as streaming, DB access, etc.) After all, it's not "raw memory" if it is involved in functional requirements. Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-26 13:14 ` Dmitry A. Kazakov 2015-11-26 14:27 ` Serge Robyns @ 2015-11-29 17:59 ` Jacob Sparre Andersen 2015-11-30 22:29 ` Randy Brukardt 1 sibling, 1 reply; 132+ messages in thread From: Jacob Sparre Andersen @ 2015-11-29 17:59 UTC (permalink / raw) Dmitry A. Kazakov wrote: > type T is record > X : Integer; > end record; > > The compiler generated setter is something like inventing Ada-like > notation: > > procedure ".X" (Left : in out T; Right : Integer); > > Called as follows: > > Y : T; > begin > Y.X := 10; Wouldn't it be much easier to add the simple bit of syntactic sugar saying that given: procedure Setter (Object : in out T; Field : in U); For a variable object, V, of (a tagged) type T, and an expression, E, of type U, the statement: V.Setter := E; is equivalent to the statement: V.Setter (E); (Not because it is shorter - it isn't - but because it nicely mirrors matching "getter" functions, and because it makes it clear that the procedure call copies a value into an object and modifies it.) Greetings, Jacob -- Those who can't laugh at themselves leave the job to others. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-29 17:59 ` Jacob Sparre Andersen @ 2015-11-30 22:29 ` Randy Brukardt 0 siblings, 0 replies; 132+ messages in thread From: Randy Brukardt @ 2015-11-30 22:29 UTC (permalink / raw) "Jacob Sparre Andersen" <sparre@nbi.dk> wrote in message news:87two4d6uk.fsf@adaheads.sparre-andersen.dk... > Dmitry A. Kazakov wrote: > >> type T is record >> X : Integer; >> end record; >> >> The compiler generated setter is something like inventing Ada-like >> notation: >> >> procedure ".X" (Left : in out T; Right : Integer); >> >> Called as follows: >> >> Y : T; >> begin >> Y.X := 10; > > Wouldn't it be much easier to add the simple bit of syntactic sugar > saying that given: > > procedure Setter (Object : in out T; > Field : in U); > > For a variable object, V, of (a tagged) type T, and an expression, E, of > type U, the statement: > > V.Setter := E; > > is equivalent to the statement: > > V.Setter (E); > > (Not because it is shorter - it isn't - but because it nicely mirrors > matching "getter" functions, and because it makes it clear that the > procedure call copies a value into an object and modifies it.) Certainly. That's what Dmitry meant when he said "... a kludge invented with aspects ...". We can argue just how kludgy it really is (a user-defined anything ultimately maps into a subprogram call, after all, and the syntax of a call isn't engraved in stone), but I think Dmitry has an ideal vision where all of these sorts of operations are given the same first-class level. That won't ever happen for Ada, but it might make sense for a new language design. Randy. ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-25 8:24 ` Dmitry A. Kazakov 2015-11-25 11:22 ` Serge Robyns @ 2015-11-25 12:27 ` G.B. 2015-11-25 17:25 ` Dmitry A. Kazakov 1 sibling, 1 reply; 132+ messages in thread From: G.B. @ 2015-11-25 12:27 UTC (permalink / raw) On 25.11.15 09:24, Dmitry A. Kazakov wrote: > On Tue, 24 Nov 2015 14:48:24 -0700, Jeffrey R. Carter wrote: > >> I mean that a protected type defines a set of values and operations on those >> values, > > This is a type, not yet a class. BTW, it is not even a proper class-type (a > root type in Ada terms) because you could not derive anything from it. > >> encapsulates the values and operations, and hides the implementation of >> the values. > > In fact it does not hide the implementation of values. I understand that for abstraction, you'd use abstraction features of the language. interface_type_definition ::= [limited | task | protected | synchronized] interface [and interface_list] That is, type Abstraction is protected interface; ... operations ... And then derive a concrete protected type that implements the interface: protected type Concrete is new Abstraction with ... > That is one of its > design flaws. The implementation of protected components is mixed with the > interface. Every Ada package tends to have "implementation of components" after "private", such as a full type definition? That's a long standing flaw then, justified by separate compilation needing full types. > It should have had public components with read access acting as > a protected function and write access doing as a protected procedure. The > private components should have been declared in the package's private > section or the package body. That seems approximately possible, I think, even when it is not formally possible: package Approx is type Opaque is private; Initial_Value : constant Opaque; protected type P is entry E1; function F1 return Boolean; private Data : Opaque := Initial_Value; end P; private type Index is range 1 .. 10; type Opaque is array (Index) of Boolean; Initial_Value : constant Opaque := (others => False); end Approx; ^ permalink raw reply [flat|nested] 132+ messages in thread
* Re: operation can be dispatching in only one type 2015-11-25 12:27 ` G.B. @ 2015-11-25 17:25 ` Dmitry A. Kazakov 0 siblings, 0 replies; 132+ messages in thread From: Dmitry A. Kazakov @ 2015-11-25 17:25 UTC (permalink / raw) On Wed, 25 Nov 2015 13:27:08 +0100, G.B. wrote: > Every Ada package tends to have "implementation of components" > after "private", such as a full type definition? That's a long > standing flaw then, justified by separate compilation needing > full types. It is a flaw only when using nested packages: package P is package Q is ... private -- This should not appear here ... end Q; private ... end P; Should have been sort of package P is package Q is ... end Q; private private package Q is ... end Q; ... end P; -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 132+ messages in thread
end of thread, other threads:[~2015-12-02 19:33 UTC | newest] Thread overview: 132+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2009-11-13 20:12 Operation can be dispatching in only one type xorque 2009-11-13 20:34 ` Dmitry A. Kazakov 2009-11-13 20:43 ` xorque 2009-11-13 21:14 ` Dmitry A. Kazakov 2009-11-13 20:44 ` xorque 2009-11-16 17:43 ` Adam Beneschan 2009-11-16 20:28 ` Dmitry A. Kazakov 2009-11-16 20:32 ` Dmitry A. Kazakov 2009-11-16 21:35 ` Adam Beneschan 2009-11-16 22:28 ` Dmitry A. Kazakov 2009-11-17 22:10 ` Adam Beneschan 2009-11-18 9:46 ` Dmitry A. Kazakov 2009-11-18 16:39 ` Adam Beneschan 2009-11-18 19:21 ` Dmitry A. Kazakov 2009-11-19 0:27 ` Randy Brukardt 2009-11-19 2:11 ` Robert A Duff 2009-11-19 15:57 ` Adam Beneschan 2009-11-19 19:39 ` Robert A Duff 2009-11-19 23:43 ` Randy Brukardt 2009-11-19 8:50 ` Dmitry A. Kazakov 2009-11-19 23:54 ` Randy Brukardt 2009-11-20 8:34 ` Dmitry A. Kazakov 2009-11-20 10:58 ` Jean-Pierre Rosen 2009-11-21 6:02 ` Randy Brukardt 2009-11-21 13:07 ` Dmitry A. Kazakov 2009-11-22 5:45 ` xorque 2009-11-22 11:25 ` Georg Bauhaus 2009-11-22 11:30 ` xorque 2009-11-22 16:25 ` Dmitry A. Kazakov 2009-11-22 16:27 ` xorque 2009-11-22 16:42 ` Dmitry A. Kazakov 2009-11-22 16:52 ` xorque 2009-11-22 17:41 ` Dmitry A. Kazakov 2009-11-22 18:03 ` xorque 2009-11-22 18:08 ` xorque 2009-11-22 18:28 ` Dmitry A. Kazakov 2009-11-22 18:41 ` xorque 2009-11-22 21:47 ` Robert A Duff 2009-11-23 3:42 ` stefan-lucks 2009-11-30 20:36 ` Robert A Duff 2009-11-30 23:54 ` (see below) 2009-12-01 12:13 ` Georg Bauhaus 2009-12-01 12:23 ` Georg Bauhaus 2009-12-01 12:44 ` Georg Bauhaus 2009-12-01 13:48 ` Dmitry A. Kazakov 2009-12-01 15:02 ` Georg Bauhaus 2009-12-01 16:18 ` Dmitry A. Kazakov 2009-12-01 17:52 ` Georg Bauhaus 2009-12-01 18:47 ` Dmitry A. Kazakov 2009-12-01 21:53 ` John B. Matthews 2009-12-02 0:32 ` Georg Bauhaus 2009-12-02 11:18 ` John B. Matthews 2009-12-02 14:29 ` Jean-Pierre Rosen 2009-12-02 15:35 ` Georg Bauhaus 2009-12-02 1:13 ` Georg Bauhaus 2009-12-02 9:07 ` Dmitry A. Kazakov 2009-12-02 12:35 ` John B. Matthews 2009-12-02 13:35 ` Dmitry A. Kazakov 2009-12-03 5:23 ` Randy Brukardt 2009-12-03 20:21 ` John B. Matthews 2009-12-03 5:29 ` Randy Brukardt 2009-12-03 11:24 ` Georg Bauhaus 2009-12-03 23:08 ` Randy Brukardt 2009-12-04 8:52 ` Dmitry A. Kazakov 2009-12-05 2:45 ` Randy Brukardt 2009-12-05 10:32 ` Dmitry A. Kazakov 2009-12-08 0:19 ` Randy Brukardt 2009-12-08 4:30 ` stefan-lucks 2009-12-08 9:12 ` Dmitry A. Kazakov 2009-12-10 4:09 ` Randy Brukardt 2009-12-11 0:10 ` Robert A Duff 2009-12-08 9:22 ` Dmitry A. Kazakov 2009-12-08 10:06 ` Georg Bauhaus 2009-12-08 10:23 ` Dmitry A. Kazakov 2009-12-08 10:33 ` Georg Bauhaus 2009-12-08 10:49 ` Dmitry A. Kazakov 2009-12-01 23:51 ` Randy Brukardt 2009-11-23 8:52 ` Dmitry A. Kazakov 2009-11-30 20:43 ` Robert A Duff 2009-12-01 9:00 ` Dmitry A. Kazakov 2009-12-01 5:45 ` stefan-lucks 2009-12-01 11:12 ` Dmitry A. Kazakov 2009-12-01 8:01 ` stefan-lucks 2009-12-01 13:37 ` Dmitry A. Kazakov 2009-12-15 23:54 ` Robert A Duff 2009-11-23 7:48 ` Georg Bauhaus 2009-11-23 7:58 ` Georg Bauhaus 2009-11-19 16:04 ` Adam Beneschan 2009-11-19 2:23 ` tmoran 2009-11-19 8:32 ` Dmitry A. Kazakov -- strict thread matches above, loose matches on Subject: below -- 2015-11-23 10:23 operation " Serge Robyns 2015-11-23 11:29 ` Dmitry A. Kazakov 2015-11-23 13:05 ` Serge Robyns 2015-11-23 13:48 ` Dmitry A. Kazakov 2015-11-23 14:16 ` Serge Robyns 2015-11-23 14:59 ` G.B. 2015-11-23 15:52 ` Dmitry A. Kazakov 2015-11-23 17:40 ` Jeffrey R. Carter 2015-11-24 9:08 ` Serge Robyns 2015-11-24 16:44 ` AdaMagica 2015-11-24 17:09 ` Jeffrey R. Carter 2015-11-24 18:37 ` Serge Robyns 2015-11-24 20:18 ` Jeffrey R. Carter 2015-11-24 20:40 ` Serge Robyns 2015-11-24 20:25 ` Niklas Holsti 2015-11-24 21:48 ` Jeffrey R. Carter 2015-11-25 8:24 ` Dmitry A. Kazakov 2015-11-25 11:22 ` Serge Robyns 2015-11-25 17:38 ` Dmitry A. Kazakov 2015-11-26 11:30 ` Serge Robyns 2015-11-26 13:14 ` Dmitry A. Kazakov 2015-11-26 14:27 ` Serge Robyns 2015-11-26 15:16 ` J-P. Rosen 2015-11-26 18:27 ` Serge Robyns 2015-11-26 21:20 ` J-P. Rosen 2015-11-27 8:37 ` Dmitry A. Kazakov 2015-11-27 12:58 ` J-P. Rosen 2015-11-27 13:39 ` Dmitry A. Kazakov 2015-11-30 22:22 ` Randy Brukardt 2015-12-01 8:46 ` Dmitry A. Kazakov 2015-12-01 11:19 ` G.B. 2015-12-01 13:56 ` Dmitry A. Kazakov 2015-12-01 16:05 ` G.B. 2015-12-01 17:58 ` Dmitry A. Kazakov 2015-12-02 13:06 ` G.B. 2015-12-02 13:31 ` Dmitry A. Kazakov 2015-12-02 19:33 ` Randy Brukardt 2015-12-02 19:27 ` Randy Brukardt 2015-11-29 17:59 ` Jacob Sparre Andersen 2015-11-30 22:29 ` Randy Brukardt 2015-11-25 12:27 ` G.B. 2015-11-25 17:25 ` Dmitry A. Kazakov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox