* advice on package design @ 2005-03-07 16:23 spambox 2005-03-07 21:08 ` Dmitry A. Kazakov 0 siblings, 1 reply; 28+ messages in thread From: spambox @ 2005-03-07 16:23 UTC (permalink / raw) Hello, I've written a package for linked list operations for my personal use. After a while I figured I'd write another package for some string operations that I use frequently. On their own, they both work well. Now comes the problem. My string library uses my linked list library, which must be instantiated -- the structure of a node must be supplied. After that, the string library can use procedures from the linked list library. But what about the end user? The linked list library defines a type, say linked_list, and the string library instantiates it something like: type word is record ... package string_ll is new linkedlists(word); use string_ll; Ultimatelly, if some function from my strings package returns a linked_list (as defined in the linked list package), it will be of the type string_ll.linked_list, if we follow the above example. How could it be available for further processing with routines from the linked list package? I could "use string_ll" (exactly as in the package) in my end program, however that's probably a poor design, since the user need not know how something was instantiated in another package. If, on the other hand, I make another instance of linkedlist, I must supply the same argument the string package does ("word" in the above example), but then the resulting linked_list type would be some_new_name.linked_list, which is not compatible with what the string package uses (string_ll.linked_list in the example). What is the correct way of dealing with such problems? Thanks, andrej ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-07 16:23 advice on package design spambox @ 2005-03-07 21:08 ` Dmitry A. Kazakov 2005-03-08 12:48 ` spambox 2005-03-12 19:57 ` Robert A Duff 0 siblings, 2 replies; 28+ messages in thread From: Dmitry A. Kazakov @ 2005-03-07 21:08 UTC (permalink / raw) On 7 Mar 2005 08:23:58 -0800, spambox@volja.net wrote: > Hello, > I've written a package for linked list operations for my personal use. > After a while I figured I'd write another package for some string > operations that I use frequently. On their own, they both work well. > Now comes the problem. > My string library uses my linked list library, which must be > instantiated -- the structure of a node must be supplied. After that, > the string library can use procedures from the linked list library. But > what about the end user? The linked list library defines a type, say > linked_list, and the string library instantiates it something like: > > type word is record ... > package string_ll is new linkedlists(word); > use string_ll; > > Ultimatelly, if some function from my strings package returns a > linked_list (as defined in the linked list package), it will be of the > type string_ll.linked_list, if we follow the above example. How could > it be available for further processing with routines from the linked > list package? > > I could "use string_ll" (exactly as in the package) in my end program, > however that's probably a poor design, since the user need not know how > something was instantiated in another package. The public part of the package is its interface. If you instantiate something there, then it is OK for all to use it. > If, on the other hand, I make another instance of linkedlist, I must > supply the same argument the string package does ("word" in the above > example), but then the resulting linked_list type would be > some_new_name.linked_list, which is not compatible with what the string > package uses (string_ll.linked_list in the example). Yes. Ada has named type equivalence. > What is the correct way of dealing with such problems? There are many different ways to deal with that. If you have 1. package Foo is type Word is ... package Baz is new Bar (Word,...); ... end Foo; then of course you can "use" Baz referencing it as Foo.Baz everywhere Foo is "with"-ed. You can even rename it: with Foo; package Baz renames Foo.Baz; 2. You can instantiate your Baz outside Foo but with the things defined in the public part of Foo: with Foo; use Foo; package Baz is new Bar (Word,...); Note that the body of Foo may still use Baz: with Baz; package body Foo is ... end Foo; Though beware that there must exits an elaboration order of the specifications and bodies Foo and Bar. 2.a. If Bar needs privates of Foo, the you can make Bar a generic child of Foo. 3. You can rename parts of contents of Baz in Foo: package Foo is type Word is ... package Baz is new Bar (Word, ...); subtype Linked_List is Baz.Linked_List; -- "renaming" of a type procedure Insert (...) renames Baz.Insert; -- renaming of a procedure ... end Foo; -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-07 21:08 ` Dmitry A. Kazakov @ 2005-03-08 12:48 ` spambox 2005-03-08 17:18 ` Dmitry A. Kazakov 2005-03-12 19:57 ` Robert A Duff 1 sibling, 1 reply; 28+ messages in thread From: spambox @ 2005-03-08 12:48 UTC (permalink / raw) Dmitry A. Kazakov wrote: > There are many different ways to deal with that. If you have Dmitry, thank you very much for you help; this is the second time that you've posted elaborate answers to my problems. I apreciate it. > 2. You can instantiate your Baz outside Foo but with the things defined in > the public part of Foo: > Note that the body of Foo may still use Baz: My thoughts on this solution: the package would have to know how the user chose to instantiate another package, so that it could use the same name. On the other hand, let's say the package came with instructions as to how the package it depends on should be instantiated. However, that would leave the user no choice for the name and effectively we would get something similar to your first solution. I find this approach better; the package `a' comes with instructions, directing the user to "use" something that was instantiated in `a' with the same name, of course. Sure, it could also be renamed later. > 2.a. If Bar needs privates of Foo, the you can make Bar a generic child of > Foo. I have yet to investigate this one. > 3. You can rename parts of contents of Baz in Foo: At present, this is the accepted solution. It seems to me that it's also the most logical for ada? Since we're forced to follow the rules of strong typing, then it's only logical to have different subroutines for each type. There are only less than ten subroutines in my linked lists package, so renaming then was easy. Besides, the program becomes clearer (in the end, that's what ada encourages us to do, right?) -- now, for example, we can use a word_walk subroutine for word lists, and eg. node_walk for all the others. Agreed, a good idea. andrej -- http://sonet.homelinux.net ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-08 12:48 ` spambox @ 2005-03-08 17:18 ` Dmitry A. Kazakov 0 siblings, 0 replies; 28+ messages in thread From: Dmitry A. Kazakov @ 2005-03-08 17:18 UTC (permalink / raw) On 8 Mar 2005 04:48:41 -0800, spambox@volja.net wrote: > Dmitry A. Kazakov wrote: > >> There are many different ways to deal with that. If you have > > Dmitry, thank you very much for you help; this is the second time that > you've posted elaborate answers to my problems. I apreciate it. You are welcome. >> 2. You can instantiate your Baz outside Foo but with the things >> defined in the public part of Foo: > >> Note that the body of Foo may still use Baz: > > My thoughts on this solution: the package would have to know how the > user chose to instantiate another package, so that it could use the > same name. On the other hand, let's say the package came with > instructions as to how the package it depends on should be > instantiated. Ada also has formal generic packages. If Foo has to be dependent on an instantiated by user Bar, then you can make it generic: generic type Word is ...; with package Baz is new Bar (Word, ...); package Foo is ... end Foo; Though this would inverse the dependency between Foo and Baz. > However, that would leave the user no choice for the name > and effectively we would get something similar to your first solution. > I find this approach better; the package `a' comes with instructions, > directing the user to "use" something that was instantiated in `a' with > the same name, of course. Sure, it could also be renamed later. > >> 3. You can rename parts of contents of Baz in Foo: > > At present, this is the accepted solution. It seems to me that it's > also the most logical for ada? Be careful. Ada community is deeply divided on this issue! (:-)) There are people who depreciate use of use. Sorry for an unintended pun. (:-)) I am not among them. > Since we're forced to follow the rules > of strong typing, then it's only logical to have different subroutines > for each type. There are only less than ten subroutines in my linked > lists package, so renaming then was easy. Besides, the program becomes > clearer (in the end, that's what ada encourages us to do, right?) -- > now, for example, we can use a word_walk subroutine for word lists, and > eg. node_walk for all the others. Agreed, a good idea. Though it is probably not your case, but just to complete the picture... Ada supports implementation through renaming. For example, the following is legal: package Foo is type Word is ... procedure Insert (...); private package Baz is new Bar (Word, ...); -- They won't see it procedure Insert (...) renames Baz.Insert; -- Insert is implemented through renaming of a procedure ... end Foo; This way you can hide the fact of instantiation of Bar and that Insert is implemented in Bar. Unfortunately it does not work with types. There is no type renaming in Ada. Subtyping and deriving a new type sometimes can do the trick, sometimes not. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-07 21:08 ` Dmitry A. Kazakov 2005-03-08 12:48 ` spambox @ 2005-03-12 19:57 ` Robert A Duff 2005-03-12 20:45 ` Dmitry A. Kazakov 1 sibling, 1 reply; 28+ messages in thread From: Robert A Duff @ 2005-03-12 19:57 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > 3. You can rename parts of contents of Baz in Foo: > > package Foo is > type Word is ... > package Baz is new Bar (Word, ...); > > subtype Linked_List is Baz.Linked_List; -- "renaming" of a type > procedure Insert (...) renames Baz.Insert; -- renaming of a procedure > ... > end Foo; One variation on that is to declare a derived type: type Linked_List is new Baz.Linked_List; This causes all of the primitive subprograms of Baz.Linked_List to be inherited -- implicitly declared in Foo. But for anything that's not a primitive subprograms, you have to write some wrapper code by hand. If I were [re]designing Ada, I would make 'use' transitive. That is, if Foo says "use Linked_List" that would make the contents of Linked_List directly visible in Foo, and anything that says "use Foo;" would have direct visibility on everything directly visible in Foo -- not just the things *declared* in Foo. Or something like that. It really is a flaw in Ada that you can't easily re-export stuff. The renaming solution is bad because it is verbose, and therefore error prone. I still remember a bug I fixed many years ago: function "and"(...) return ... renames Something."and"; function "or"(...) return ... renames Something."or"; function "xor"(...) return ... renames Something."or"; The problem is that renaming is *not* usually used to rename things -- it is used to import them (with the *same* name) into a different scope. Given that mindset, it took me a *long* time to notice the bug in the above. The symptom was that xor was behaving strangely in some faraway place. That was Ada 83. In Ada 95, "use type" would have prevented the problem. - Bob ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-12 19:57 ` Robert A Duff @ 2005-03-12 20:45 ` Dmitry A. Kazakov 2005-03-12 21:59 ` Robert A Duff 0 siblings, 1 reply; 28+ messages in thread From: Dmitry A. Kazakov @ 2005-03-12 20:45 UTC (permalink / raw) On 12 Mar 2005 14:57:17 -0500, Robert A Duff wrote: > If I were [re]designing Ada, I would make 'use' transitive. That is, > if Foo says "use Linked_List" that would make the contents of > Linked_List directly visible in Foo, and anything that says "use Foo;" > would have direct visibility on everything directly visible in Foo -- > not just the things *declared* in Foo. Or something like that. Yes, I really miss it. What about: use all <package-name>; or use package <package-name>; with the effect importing the specification of the package? Also, differently to use, it should prevent hiding. So: package A is Foo (X : Integer); end A; with A; package B is use all A; Foo (X : out Integer); -- Illegal, can't hide A.Foo end B; It could be also used for testing if a child package is parent-friendly: package Foo.Child is ... -- Lot of stuff private use all Foo; -- Fails if Child hides something in Foo ... end Foo; -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-12 20:45 ` Dmitry A. Kazakov @ 2005-03-12 21:59 ` Robert A Duff 2005-03-13 9:23 ` Dmitry A. Kazakov 0 siblings, 1 reply; 28+ messages in thread From: Robert A Duff @ 2005-03-12 21:59 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > On 12 Mar 2005 14:57:17 -0500, Robert A Duff wrote: > > > If I were [re]designing Ada, I would make 'use' transitive. That is, > > if Foo says "use Linked_List" that would make the contents of > > Linked_List directly visible in Foo, and anything that says "use Foo;" > > would have direct visibility on everything directly visible in Foo -- > > not just the things *declared* in Foo. Or something like that. > > Yes, I really miss it. What about: > > use all <package-name>; > > or > > use package <package-name>; > > with the effect importing the specification of the package? Also, > differently to use, it should prevent hiding. So: > > package A is > Foo (X : Integer); > end A; > > with A; > package B is > use all A; > Foo (X : out Integer); -- Illegal, can't hide A.Foo > end B; Well, I happen to think that *all* hiding is evil. I would make the above legal, but all calls to Foo would be ambiguous, and therefore illegal. Ichbiah defined "Beaujolais Effect" to mean that if you add a subprogram to a use-d package, and that causes a legal program to change it's meaning (to another legal program) you have a Beaujolais Effect. And Beaujolais Effects are bad. Ada 83 had some Beaujolais Effects, but they were pretty obscure, and we eliminated them in Ada 95. But I think he should have extended that definition to include all visibility, not just use-visibility. So a local X should not hide a use-visible X, but be ambiguous with it. And similar rules for record (extension) components. In Pascal, a 'with' statement opens up the visibility of the record components, and if 'with R' is nested within some place where X is declared, then record component X hides that X. That's bad, and Ichbiah wisely designed 'use' clauses to not do that bad thing. The trick is to avoid "preference rules", where one decl takes precedence over another. But hiding is exactly a preference rule (preferring the inner one over an outer one, or preferring an outer one over a use-visible one). C++ is rife with Beaujolais Effects. - Bob P.S. The reason for the name is that Ichbiah offered to give a bottle (or was it a case?) of Beaujolais to anybody who could find such a problem in Ada. ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-12 21:59 ` Robert A Duff @ 2005-03-13 9:23 ` Dmitry A. Kazakov 2005-03-16 20:41 ` Robert A Duff 0 siblings, 1 reply; 28+ messages in thread From: Dmitry A. Kazakov @ 2005-03-13 9:23 UTC (permalink / raw) On 12 Mar 2005 16:59:26 -0500, Robert A Duff wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > >> On 12 Mar 2005 14:57:17 -0500, Robert A Duff wrote: >> >>> If I were [re]designing Ada, I would make 'use' transitive. That is, >>> if Foo says "use Linked_List" that would make the contents of >>> Linked_List directly visible in Foo, and anything that says "use Foo;" >>> would have direct visibility on everything directly visible in Foo -- >>> not just the things *declared* in Foo. Or something like that. >> >> Yes, I really miss it. What about: >> >> use all <package-name>; >> >> or >> >> use package <package-name>; >> >> with the effect importing the specification of the package? Also, >> differently to use, it should prevent hiding. So: >> >> package A is >> Foo (X : Integer); >> end A; >> >> with A; >> package B is >> use all A; >> Foo (X : out Integer); -- Illegal, can't hide A.Foo >> end B; > > Well, I happen to think that *all* hiding is evil. > I would make the above legal, but all calls to Foo would be ambiguous, > and therefore illegal. That makes sense, but it might be difficult for the programmer to resolve the problem. Somewhere in 20th child package of B he calls Foo and, oops, it is ambiguous. I think it is better to keep all scopes clean from the beginning. > Ichbiah defined "Beaujolais Effect" to mean that if you add a subprogram > to a use-d package, and that causes a legal program to change it's > meaning (to another legal program) you have a Beaujolais Effect. > And Beaujolais Effects are bad. Ada 83 had some Beaujolais Effects, > but they were pretty obscure, and we eliminated them in Ada 95. > > But I think he should have extended that definition to include all > visibility, not just use-visibility. So a local X should not hide a > use-visible X, but be ambiguous with it. Yes. Even in the cases like: declare I : Integer; begin for I in A'Range loop A (I) := 0; end loop; However I'd like to get error message one line above than you. > And similar rules for record > (extension) components. I think that this case is not so simple. IMO record components should be treated as primitive operations (getter/setter pair). As such they could be overridden upon extension. At the same time some of the components might be declared as a kind of "class-wide". For these the rules preventing hiding should apply. > In Pascal, a 'with' statement opens up the visibility of the record > components, and if 'with R' is nested within some place where X is > declared, then record component X hides that X. That's bad, and Ichbiah > wisely designed 'use' clauses to not do that bad thing. The trick is to > avoid "preference rules", where one decl takes precedence over another. > But hiding is exactly a preference rule (preferring the inner one over > an outer one, or preferring an outer one over a use-visible one). Alas, but preference rules cannot be avoided. Overriding is just that preference. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-13 9:23 ` Dmitry A. Kazakov @ 2005-03-16 20:41 ` Robert A Duff 2005-03-17 10:22 ` Dmitry A. Kazakov 0 siblings, 1 reply; 28+ messages in thread From: Robert A Duff @ 2005-03-16 20:41 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > On 12 Mar 2005 16:59:26 -0500, Robert A Duff wrote: > > > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > > > >> On 12 Mar 2005 14:57:17 -0500, Robert A Duff wrote: > >> > >>> If I were [re]designing Ada, I would make 'use' transitive. That is, > >>> if Foo says "use Linked_List" that would make the contents of > >>> Linked_List directly visible in Foo, and anything that says "use Foo;" > >>> would have direct visibility on everything directly visible in Foo -- > >>> not just the things *declared* in Foo. Or something like that. > >> > >> Yes, I really miss it. What about: > >> > >> use all <package-name>; > >> > >> or > >> > >> use package <package-name>; > >> > >> with the effect importing the specification of the package? Also, > >> differently to use, it should prevent hiding. So: > >> > >> package A is > >> Foo (X : Integer); > >> end A; > >> > >> with A; > >> package B is > >> use all A; > >> Foo (X : out Integer); -- Illegal, can't hide A.Foo > >> end B; > > > > Well, I happen to think that *all* hiding is evil. > > I would make the above legal, but all calls to Foo would be ambiguous, > > and therefore illegal. > > That makes sense, but it might be difficult for the programmer to resolve > the problem. Somewhere in 20th child package of B he calls Foo and, oops, > it is ambiguous. I think it is better to keep all scopes clean from the > beginning. I see your point, but I think that can be dealt with by having good error messages. In any language that allows overloading, there are going to potential ambiguities, and when they happen, I expect more from the compiler than just "error: I'm confused". In Ada, if you have: procedure Foo(X: Integer); procedure Foo(Y: Integer); in the same package, it's illegal. But if they're in two different packages, both use-visible, then it's legal, and you can call them using named notation: Foo(X => 1) vs. Foo(Y => 1). That seems odd to me. Should that use clause be illegal? I would prefer to allow declaring of anything, but then declare potentially-confusing uses ambiguous. And don't make the overload-resolution rules too "smart", so they make many things ambiguous (and therefore illegal). And rely on compilers to give useful error messages for ambiguity. That seems better than trying to prevent possibly-ambiguous things ahead of time, at the declaration points. > > Ichbiah defined "Beaujolais Effect" to mean that if you add a subprogram > > to a use-d package, and that causes a legal program to change it's > > meaning (to another legal program) you have a Beaujolais Effect. > > And Beaujolais Effects are bad. Ada 83 had some Beaujolais Effects, > > but they were pretty obscure, and we eliminated them in Ada 95. > > > > But I think he should have extended that definition to include all > > visibility, not just use-visibility. So a local X should not hide a > > use-visible X, but be ambiguous with it. > > Yes. Even in the cases like: > > declare > I : Integer; > begin > for I in A'Range loop > A (I) := 0; > end loop; Yes, I agree. > However I'd like to get error message one line above than you. OK, but I'm willing to trust the compiler to do that (i.e. say, "I" is ambiguous here, because one-line-above you have an I that conflicts with the I three-lines-above). > > And similar rules for record > > (extension) components. > > I think that this case is not so simple. IMO record components should be > treated as primitive operations (getter/setter pair). As such they could be > overridden upon extension. At the same time some of the components might be > declared as a kind of "class-wide". For these the rules preventing hiding > should apply. > > > In Pascal, a 'with' statement opens up the visibility of the record > > components, and if 'with R' is nested within some place where X is > > declared, then record component X hides that X. That's bad, and Ichbiah > > wisely designed 'use' clauses to not do that bad thing. The trick is to > > avoid "preference rules", where one decl takes precedence over another. > > But hiding is exactly a preference rule (preferring the inner one over > > an outer one, or preferring an outer one over a use-visible one). > > Alas, but preference rules cannot be avoided. Overriding is just that > preference. I don't see why. Overriding is different -- the old thing goes away. It's explicit, so it's OK. If we had this language: declare I : Integer; begin hide I; <<<<<<<<<<<<<<<<<<<<<<<< for I in A'Range loop A (I) := 0; end loop; then the hiding would be OK, IMHO. - Bob ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-16 20:41 ` Robert A Duff @ 2005-03-17 10:22 ` Dmitry A. Kazakov 2005-03-17 14:04 ` Robert A Duff 0 siblings, 1 reply; 28+ messages in thread From: Dmitry A. Kazakov @ 2005-03-17 10:22 UTC (permalink / raw) On 16 Mar 2005 15:41:55 -0500, Robert A Duff wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > >> On 12 Mar 2005 16:59:26 -0500, Robert A Duff wrote: >> >>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: >> >> That makes sense, but it might be difficult for the programmer to resolve >> the problem. Somewhere in 20th child package of B he calls Foo and, oops, >> it is ambiguous. I think it is better to keep all scopes clean from the >> beginning. > > I see your point, but I think that can be dealt with by having good > error messages. In any language that allows overloading, there are > going to potential ambiguities, and when they happen, I expect more from > the compiler than just "error: I'm confused". > > In Ada, if you have: > > procedure Foo(X: Integer); > procedure Foo(Y: Integer); > > in the same package, it's illegal. But if they're in two different > packages, both use-visible, then it's legal, and you can call them > using named notation: Foo(X => 1) vs. Foo(Y => 1). That seems odd > to me. Should that use clause be illegal? Yes, at least "transitive use" should be. Otherwise, overloading of two Foo's should be allowed (which would be bad). Good error messages might be OK from the perspective of a package user. But from the package designer's one it is not enough, when he wants to make his packages "use"-friendly, especially "transitive use"-friendly. > ... And don't make the > overload-resolution rules too "smart", so they make many things > ambiguous (and therefore illegal). I definitely agree with this. >> Alas, but preference rules cannot be avoided. Overriding is just that >> preference. > > I don't see why. Overriding is different -- the old thing goes away. > It's explicit, so it's OK. Presently it is not explicit. Syntactically overriding is undistinguishable from declaring a new operation. This is IMO bad. It should be sort of: procedure Override (X : Object) is ????; -- Overriding intended, fails if base types have no primitive Override procedure Override (X : Object); -- Overloading intended, fails if it hides any other Override > If we had this language: > > declare > I : Integer; > begin > hide I; <<<<<<<<<<<<<<<<<<<<<<<< > for I in A'Range loop > A (I) := 0; > end loop; > > then the hiding would be OK, IMHO. I agree with the idea, but I think that hiding should appear in a declarative part. Less probably it should also qualify the thing being hidden (like renames does): declare I : Integer; begin declare I : Integer is null; -- Know me, if you want to get rid of me! begin for I in A'Range loop A (I) := 0; end loop; -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 10:22 ` Dmitry A. Kazakov @ 2005-03-17 14:04 ` Robert A Duff 2005-03-17 15:59 ` Dmitry A. Kazakov 2005-03-17 23:23 ` Randy Brukardt 0 siblings, 2 replies; 28+ messages in thread From: Robert A Duff @ 2005-03-17 14:04 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > Presently it is not explicit. Syntactically overriding is undistinguishable > from declaring a new operation. This is IMO bad. It should be sort of: > > procedure Override (X : Object) is ????; > -- Overriding intended, fails if base types have no primitive Override > procedure Override (X : Object); > -- Overloading intended, fails if it hides any other Override I think Ada 2005 has something like that. > I agree with the idea, but I think that hiding should appear in a > declarative part. Less probably it should also qualify the thing being > hidden (like renames does): I don't believe in declarative parts. I think declare/begin/end should not be required just because I want to declare a constant or something. - Bob ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 14:04 ` Robert A Duff @ 2005-03-17 15:59 ` Dmitry A. Kazakov 2005-03-17 19:10 ` Robert A Duff 2005-03-17 23:23 ` Randy Brukardt 1 sibling, 1 reply; 28+ messages in thread From: Dmitry A. Kazakov @ 2005-03-17 15:59 UTC (permalink / raw) On 17 Mar 2005 09:04:26 -0500, Robert A Duff wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: >> I agree with the idea, but I think that hiding should appear in a >> declarative part. Less probably it should also qualify the thing being >> hidden (like renames does): > > I don't believe in declarative parts. I think declare/begin/end > should not be required just because I want to declare a constant > or something. Disagree. I don't like the idea of something changing its meaning within the same scope, even if that is non-existing -> exiting. I have to tolerate this in a declarative region because probably there is no better way to do it. But I don't want to let it leak out of the quarantine zone. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 15:59 ` Dmitry A. Kazakov @ 2005-03-17 19:10 ` Robert A Duff 2005-03-17 19:47 ` Martin Dowie 2005-03-17 20:48 ` Dmitry A. Kazakov 0 siblings, 2 replies; 28+ messages in thread From: Robert A Duff @ 2005-03-17 19:10 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > On 17 Mar 2005 09:04:26 -0500, Robert A Duff wrote: > > > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > > >> I agree with the idea, but I think that hiding should appear in a > >> declarative part. Less probably it should also qualify the thing being > >> hidden (like renames does): > > > > I don't believe in declarative parts. I think declare/begin/end > > should not be required just because I want to declare a constant > > or something. > > Disagree. I don't like the idea of something changing its meaning within > the same scope, even if that is non-existing -> exiting. I have to tolerate > this in a declarative region because probably there is no better way to do > it. But I don't want to let it leak out of the quarantine zone. I don't understand what you mean about "changing meaning". I just want to be able to write things like: for I in A'Range loop This_Component: constant T := A(I); ... several uses of This_Component end loop; without adding three extra (useless) lines of code. And we agreed that if there's another thing called This_Component, this This_Component should not hide that This_Component. The "hide" command I proposed is of dubious value, but if it were allowed, I wouldn't mind restricting it to whole procedures or something. Or not. But either way, I don't like the separation of "declarations" and "statements". - Bob ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 19:10 ` Robert A Duff @ 2005-03-17 19:47 ` Martin Dowie 2005-03-17 20:55 ` Robert A Duff 2005-03-17 20:48 ` Dmitry A. Kazakov 1 sibling, 1 reply; 28+ messages in thread From: Martin Dowie @ 2005-03-17 19:47 UTC (permalink / raw) Robert A Duff wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > > >>On 17 Mar 2005 09:04:26 -0500, Robert A Duff wrote: >> >> >>>"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: >> >>>>I agree with the idea, but I think that hiding should appear in a >>>>declarative part. Less probably it should also qualify the thing being >>>>hidden (like renames does): >>> >>>I don't believe in declarative parts. I think declare/begin/end >>>should not be required just because I want to declare a constant >>>or something. >> >>Disagree. I don't like the idea of something changing its meaning within >>the same scope, even if that is non-existing -> exiting. I have to tolerate >>this in a declarative region because probably there is no better way to do >>it. But I don't want to let it leak out of the quarantine zone. > > > I don't understand what you mean about "changing meaning". > I just want to be able to write things like: > > for I in A'Range loop > This_Component: constant T := A(I); > ... several uses of This_Component > end loop; > > without adding three extra (useless) lines of code. > And we agreed that if there's another thing called This_Component, > this This_Component should not hide that This_Component. > > The "hide" command I proposed is of dubious value, but if it were > allowed, I wouldn't mind restricting it to whole procedures > or something. Or not. But either way, I don't like the separation > of "declarations" and "statements". The bit about declarations and statements I don't like is where exceptions occur... e.g. with Ada.Text_IO; use Ada.Text_IO; procedure Main is function Foo (I : Integer) return Integer is I2 : Integer := I + 1; begin return I2; exception when others => return 0; end Foo; procedure Bar is I : Integer; begin I := Foo (Integer'Last); end Bar; begin Bar; exception when others => Put_Line ("Rats... why not catch it in 'Foo'?"); end Main; I've never understood why this needs to be this way... Cheers -- Martin ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 19:47 ` Martin Dowie @ 2005-03-17 20:55 ` Robert A Duff 2005-03-17 21:14 ` Marius Amado Alves 2005-03-18 9:38 ` Martin Dowie 0 siblings, 2 replies; 28+ messages in thread From: Robert A Duff @ 2005-03-17 20:55 UTC (permalink / raw) Martin Dowie <martin.dowie@btopenworld.com> writes: > The bit about declarations and statements I don't like is where > exceptions occur... > > e.g. > > with Ada.Text_IO; use Ada.Text_IO; > procedure Main is > function Foo (I : Integer) return Integer is > I2 : Integer := I + 1; > begin > return I2; > exception > when others => > return 0; > end Foo; > > procedure Bar is > I : Integer; > begin > I := Foo (Integer'Last); > end Bar; > begin > Bar; > exception > when others => > Put_Line ("Rats... why not catch it in 'Foo'?"); > end Main; > > I've never understood why this needs to be this way... Because I2 is visible in the handler. But it's declaration is never successfully elaborated, so it does not exist! procedure P is X: Integer := F(...); -- raises Some_Error Y: Integer := 123; begin ... exception when Some_Error => Put(X); Put(Y); -- What are the values of X and Y here? end P; Or worse: procedure P is X: Integer := F(...); -- raises Some_Error Y: String := Read_Line; begin ... exception when Some_Error => Put(Y); end P; At the point of "Put(Y);", not even the bounds of Y have been calculated, much less its value. But you're right -- the syntax is somewhat confusing, since the exception handler *looks* like it handles exceptions for the whole procedure. Another reason to get rid of declarative parts. ;-) I think a better syntax would a separate statement -- like try/catch in Java. That makes it clear what region of code is protected by the handler. - Bob ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 20:55 ` Robert A Duff @ 2005-03-17 21:14 ` Marius Amado Alves 2005-03-18 9:31 ` Martin Dowie 2005-03-18 9:38 ` Martin Dowie 1 sibling, 1 reply; 28+ messages in thread From: Marius Amado Alves @ 2005-03-17 21:14 UTC (permalink / raw) Cc: comp.lang.ada >> with Ada.Text_IO; use Ada.Text_IO; >> procedure Main is >> function Foo (I : Integer) return Integer is >> I2 : Integer := I + 1; >> begin >> return I2; >> exception >> when others => >> return 0; >> end Foo; ... >> exception >> when others => >> Put_Line ("Rats... why not catch it in 'Foo'?"); >> Bob told you why. I'll tell you how, just in case. Rewrite Foo thus: function Foo (I : Integer) return Integer is begin declare I2 : Integer := I + 1; begin return I2; end; exception when others => return 0; end Foo; (Of course this how follows from the why. I2 is no longer visible in the handler.) ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 21:14 ` Marius Amado Alves @ 2005-03-18 9:31 ` Martin Dowie 0 siblings, 0 replies; 28+ messages in thread From: Martin Dowie @ 2005-03-18 9:31 UTC (permalink / raw) Marius Amado Alves wrote: > Bob told you why. I'll tell you how, just in case. Rewrite Foo thus: > > function Foo (I : Integer) return Integer is > begin > declare > I2 : Integer := I + 1; > begin > return I2; > end; > exception > when others => > return 0; > end Foo; > > (Of course this how follows from the why. I2 is no longer visible in > the handler.) Thanks - I'm afraid I've already been through the pain of doing just this on several dozen function on my first Ada83 program when I discovered this little 'design feature' :-) Cheers -- Martin ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 20:55 ` Robert A Duff 2005-03-17 21:14 ` Marius Amado Alves @ 2005-03-18 9:38 ` Martin Dowie 2005-03-21 16:19 ` Robert A Duff 1 sibling, 1 reply; 28+ messages in thread From: Martin Dowie @ 2005-03-18 9:38 UTC (permalink / raw) Robert A Duff wrote: >> I've never understood why this needs to be this way... > > Because I2 is visible in the handler. But it's declaration is never > successfully elaborated, so it does not exist! > > procedure P is > X: Integer := F(...); -- raises Some_Error > Y: Integer := 123; > begin > ... > exception > when Some_Error => > Put(X); Put(Y); -- What are the values of X and Y here? > end P; And we couldn't have come up with some form of words to get round that?!?!?!?! :-) ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-18 9:38 ` Martin Dowie @ 2005-03-21 16:19 ` Robert A Duff 0 siblings, 0 replies; 28+ messages in thread From: Robert A Duff @ 2005-03-21 16:19 UTC (permalink / raw) "Martin Dowie" <martin.dowie@baesystems.com> writes: > Robert A Duff wrote: > >> I've never understood why this needs to be this way... > > > > Because I2 is visible in the handler. But it's declaration is never > > successfully elaborated, so it does not exist! > > > > procedure P is > > X: Integer := F(...); -- raises Some_Error > > Y: Integer := 123; > > begin > > ... > > exception > > when Some_Error => > > Put(X); Put(Y); -- What are the values of X and Y here? > > end P; > > And we couldn't have come up with some form of words to get > round that?!?!?!?! :-) It's not a wording issue. It just doesn't make any sense to allow code to refer to variables that do not exist. What's needed is a syntax that better reflects the semantics. It was a mistake in Ada 83 to attach handlers to scope-y things. - Bob ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 19:10 ` Robert A Duff 2005-03-17 19:47 ` Martin Dowie @ 2005-03-17 20:48 ` Dmitry A. Kazakov 2005-03-17 21:26 ` Robert A Duff 1 sibling, 1 reply; 28+ messages in thread From: Dmitry A. Kazakov @ 2005-03-17 20:48 UTC (permalink / raw) On 17 Mar 2005 14:10:11 -0500, Robert A Duff wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > >> On 17 Mar 2005 09:04:26 -0500, Robert A Duff wrote: >> >>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: >> >>>> I agree with the idea, but I think that hiding should appear in a >>>> declarative part. Less probably it should also qualify the thing being >>>> hidden (like renames does): >>> >>> I don't believe in declarative parts. I think declare/begin/end >>> should not be required just because I want to declare a constant >>> or something. >> >> Disagree. I don't like the idea of something changing its meaning within >> the same scope, even if that is non-existing -> exiting. I have to tolerate >> this in a declarative region because probably there is no better way to do >> it. But I don't want to let it leak out of the quarantine zone. > > I don't understand what you mean about "changing meaning". > I just want to be able to write things like: > > for I in A'Range loop Until here --> This_Component = nothing > This_Component: constant T := A(I); Below --> This_Component = constant T > ... several uses of This_Component > end loop; > > without adding three extra (useless) lines of code. > And we agreed that if there's another thing called This_Component, > this This_Component should not hide that This_Component. Maybe it is a matter of taste, but it is one of things in C++ which I hate the most. It is very difficult (to me) to understand a program with mixed declarations and statements. If you care about extra lines, then I'd propose: for I in A'Range -- All declarations be between for...loop This_Component: constant T := A(I); loop ... several uses of This_Component end loop; > The "hide" command I proposed is of dubious value, but if it were > allowed, I wouldn't mind restricting it to whole procedures > or something. Or not. But either way, I don't like the separation > of "declarations" and "statements". I like the separation very much. But let's consider getting rid of declarations. Wouldn't it be fake anyway? You know that better than me, but I suppose the compiler will need to move everything to the beginning of the closest scope. Otherwise: if Halt(x) then declare A; end if; Foo (A); -- Would it be legal? T := (1, 2, 4, declare X := 9, others => declare Y := 10); -- What would be this? How many Y's could be here? So I see no advantage in this. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 20:48 ` Dmitry A. Kazakov @ 2005-03-17 21:26 ` Robert A Duff 2005-03-18 3:06 ` Jared 2005-03-18 10:00 ` Dmitry A. Kazakov 0 siblings, 2 replies; 28+ messages in thread From: Robert A Duff @ 2005-03-17 21:26 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > I like the separation very much. You're not alone. Part of my reason is that declarations in Ada execute code at run time. In other languages (e.g. Pascal) there is a strict separation: declarations declare, statements execute, so it makes sense to have a syntactic line drawn between the two. But in Ada, the decls can do real work, so the line drawn by the "begin" is misleading. In some cases, the decls do *all* the work, especially when you're working with Strings, where the length usually depends on the initial value. Then the procedure body says "null;", meaning "this does nothing", which is a lie. Sometimes you end up with long chains of deeply nested declare blocks: function F(...) return String is A: String := ...; begin Do_Something(A); declare B: constant String := G(A); begin ... and so on .................................. declare Z: String ... begin return Z; Yuck. Another thing that I trip over once in a while is I want a *statement* near the beginning of a package body. It is important that it execute at the point, because following code (yes, decls are code!) depends on it. You can say this: package body P is package Defeat_Annoying_Syntax_Rules is end; package body Defeat_Annoying_Syntax_Rules is begin Initialize_Database; end; ... -- 200 lines of code end P; But that seems less readable (to me) than just writing "Initialize_Database;" where I want it to happen. So a rule (requiring "declare") that is intended to increase readability actually decreases it. And if you want a statement in a package *spec* (say, to initialize something), it's far worse. There's no direct workaround; you have to totally rearrange the structure of your design in order to get around a silly syntax rule. It seems to me that: Mumble: T := Blah(...); should be precisely equivalent to: Mumble: T; Mumble := Blah(...); which should be precisely equivalent to: Mumble: T; Blah(Mumble); (where in the last example we replace function Blah with a procedure with an 'out' parameter). The programmer should be able to switch from one of these forms to another, without changing the semantics of the program. But the syntax rules require stirring the code around when you make these changes. (Another reason Ada does not have the above desirable property is that function results of subtype String behave differently from 'out' parameters of subtype String. I don't like that.) > But let's consider getting rid of declarations. Wouldn't it be fake anyway? > You know that better than me, but I suppose the compiler will need to move > everything to the beginning of the closest scope. Otherwise: > > if Halt(x) then > declare A; > end if; > Foo (A); -- Would it be legal? By "declare A;" I assume you mean something like "A: Integer;". No, A would not be visible at the call to Foo -- it would be just like putting a declare block inside the if statement in the current language. I'm not proposing anything different in the semantics, here -- just a minor syntax change. > T := (1, 2, 4, declare X := 9, others => declare Y := 10); > -- What would be this? How many Y's could be here? No, I don't propose to allow mixing of decls and expressions! Just decls and statements. Decls don't return values. > So I see no advantage in this. Convinced? Partly convinced? ;-) - Bob ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 21:26 ` Robert A Duff @ 2005-03-18 3:06 ` Jared 2005-03-18 10:00 ` Dmitry A. Kazakov 1 sibling, 0 replies; 28+ messages in thread From: Jared @ 2005-03-18 3:06 UTC (permalink / raw) Robert A Duff <bobduff@shell01.TheWorld.com> wrote in news:wccsm2u6k1t.fsf@shell01.TheWorld.com: > But that seems less readable (to me) than just writing > "Initialize_Database;" where I want it to happen. > So a rule (requiring "declare") that is intended to increase > readability actually decreases it. > > And if you want a statement in a package *spec* (say, to initialize > something), it's far worse. There's no direct workaround; you have to > totally rearrange the structure of your design in order to get around > a silly syntax rule. Is this terribly common, though? I'm just a hobbyist, so when I run across something like that, I just assume I have a bad design. > It seems to me that: > > Mumble: T := Blah(...); > > should be precisely equivalent to: > > Mumble: T; > Mumble := Blah(...); > > which should be precisely equivalent to: > > Mumble: T; > Blah(Mumble); > > (where in the last example we replace function Blah with a procedure > with an 'out' parameter). The programmer should be able to switch > from one of these forms to another, without changing the semantics of > the program. But the syntax rules require stirring the code around > when you make these changes. > > (Another reason Ada does not have the above desirable property is that > function results of subtype String behave differently from 'out' > parameters of subtype String. I don't like that.) This is the case for all unconstrained array types. In order to use unconstrained variables, we three distasteful choices. Invoke the entire object mechanism for a single controlled type, fiddle with access types and hope not to drop any references, or create code that looks like: > function F(...) return String is > A: String := ...; > begin > Do_Something(A); > declare > B: constant String := G(A); > begin > ... and so on .................................. > declare > Z: > String ... > begin > return > Z; > > Yuck. There's always Ada.Strings.Unbounded.Unbounded_String, but that's only good for character arrays. Incidentally, I'm just a hobbyist, so I don't know about this; how common is it in production code to declare a controlled type just to guarantee uniqueness of reference? Of those, how many are used for arrays? ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 21:26 ` Robert A Duff 2005-03-18 3:06 ` Jared @ 2005-03-18 10:00 ` Dmitry A. Kazakov 2005-03-21 16:17 ` Robert A Duff 1 sibling, 1 reply; 28+ messages in thread From: Dmitry A. Kazakov @ 2005-03-18 10:00 UTC (permalink / raw) On 17 Mar 2005 16:26:22 -0500, Robert A Duff wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > >> I like the separation very much. > > You're not alone. > > Part of my reason is that declarations in Ada execute code at run time. > In other languages (e.g. Pascal) there is a strict separation: > declarations declare, statements execute, so it makes sense to have > a syntactic line drawn between the two. But in Ada, the decls can > do real work, so the line drawn by the "begin" is misleading. Yes, indirectly they do. For the following body this is the code that never fail. Similarly, each "end;" is in fact also a code which again never fails (for the body above). I can buy this as modularization (can I sell it so too? (:-)) > In some cases, the decls do *all* the work, especially when you're > working with Strings, where the length usually depends on the > initial value. Then the procedure body says "null;", meaning > "this does nothing", which is a lie. Yes. A similar case is: if 0 = Some_Win_API (...) then null; -- Don't care about your silly return codes! end if; > Sometimes you end up with long chains of deeply nested declare blocks: > > function F(...) return String is > A: String := ...; > begin > Do_Something(A); > declare > B: constant String := G(A); > begin > ... and so on .................................. > declare > Z: String ... > begin > return Z; > > Yuck. I also dislike unnecessary indentations, but what if exception handling is needed? Then you have to create blocks anyway. > It seems to me that: > > Mumble: T := Blah(...); > > should be precisely equivalent to: > > Mumble: T; > Mumble := Blah(...); So Mumble is first constructed and then assigned? Would you leave that to the optimizer? I wouldn't. I prefer a clear distinction between ":=" in declarations (= in-place construction with parameters) and ":=" in the body (= assignment). > which should be precisely equivalent to: > > Mumble: T; > Blah(Mumble); > > (where in the last example we replace function Blah with a procedure > with an 'out' parameter). The programmer should be able to switch from > one of these forms to another, without changing the semantics of the > program. But that will require switching to heap. I would like to leave that all to the programmer. Let he be able to *easily* create access types acting as light-weight handles. Then "out" will be just a handle. Why to overload the compiler with things it cannot do optimal and require from a programmer an in-depth understanding of what compiler will do IF? > But the syntax rules require stirring the code around > when you make these changes. > (Another reason Ada does not have the above desirable property is that > function results of subtype String behave differently from 'out' > parameters of subtype String. I don't like that.) Neither I do. But I think that the difference is rather fundamental. It appears all over many other places: in object construction, in discriminated types, in class-wide types etc. For this reason, I would like to see some general mechanism for evaluation of the constraints *independently* from the values; and also for forcing the compiler to evaluate statically known constraints at compile time and to remove them from all sorts of dopes. So instead of hiding the skeleton in the cupboard I would openly present it to the public! (:-)) >> But let's consider getting rid of declarations. Wouldn't it be fake anyway? >> You know that better than me, but I suppose the compiler will need to move >> everything to the beginning of the closest scope. Otherwise: >> >> if Halt(x) then >> declare A; >> end if; >> Foo (A); -- Would it be legal? > > By "declare A;" I assume you mean something like "A: Integer;". > No, A would not be visible at the call to Foo -- it would be > just like putting a declare block inside the if statement > in the current language. I'm not proposing anything different > in the semantics, here -- just a minor syntax change. I see. But that will require definition of which parts of which statements may act as scopes: loop, if alternative, case choice, exception choice (already so in the "X : others"-kludge), select choice etc. How many pages of ARM? (:-)) >> T := (1, 2, 4, declare X := 9, others => declare Y := 10); >> -- What would be this? How many Y's could be here? > > No, I don't propose to allow mixing of decls and expressions! > Just decls and statements. Decls don't return values. Once you let them in, then: if I can put declarations among statements, why cannot I put statements among declarations? >> So I see no advantage in this. > > Convinced? Partly convinced? ;-) I see the rationale and the problem, but the solution ... -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-18 10:00 ` Dmitry A. Kazakov @ 2005-03-21 16:17 ` Robert A Duff 2005-03-21 18:16 ` Dmitry A. Kazakov 0 siblings, 1 reply; 28+ messages in thread From: Robert A Duff @ 2005-03-21 16:17 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > On 17 Mar 2005 16:26:22 -0500, Robert A Duff wrote: > > > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > > > >> I like the separation very much. > > > > You're not alone. > > > > Part of my reason is that declarations in Ada execute code at run time. > > In other languages (e.g. Pascal) there is a strict separation: > > declarations declare, statements execute, so it makes sense to have > > a syntactic line drawn between the two. But in Ada, the decls can > > do real work, so the line drawn by the "begin" is misleading. > > Yes, indirectly they do. For the following body this is the code that never > fail. Similarly, each "end;" is in fact also a code which again never fails > (for the body above). I can buy this as modularization (can I sell it so > too? (:-)) The problem is that whether something goes above or below the "begin" depends on all kinds of extraneous factors. If I want to initialize a variable with a function, it goes above the line. If I want to use a procedure call, or a loop, it goes below the line. Etc. > > In some cases, the decls do *all* the work, especially when you're > > working with Strings, where the length usually depends on the > > initial value. Then the procedure body says "null;", meaning > > "this does nothing", which is a lie. > > Yes. A similar case is: > > if 0 = Some_Win_API (...) then > null; -- Don't care about your silly return codes! > end if; It doesn't seem similar to me. Here, we are explicitly saying that the "then" part does nothing, and that's a good thing. (Some other languages are more error prone in this regard.) But a procedure that does a lot of work that happens to be above the "begin" still needs "null", which seems sort of silly. > I also dislike unnecessary indentations, but what if exception handling is > needed? Then you have to create blocks anyway. I would prefer a separate try/catch-like statement for exception handling. Exception handling seems totally unrelated to declaring things, so I don't see why they should be mixed in the same syntax. Yes, exception handling requires indentation levels -- seems OK to me. > > It seems to me that: > > > > Mumble: T := Blah(...); > > > > should be precisely equivalent to: > > > > Mumble: T; > > Mumble := Blah(...); > > So Mumble is first constructed and then assigned? Would you leave that to > the optimizer? I wouldn't. I prefer a clear distinction between ":=" in > declarations (= in-place construction with parameters) and ":=" in the body > (= assignment). OK, I take it back. You're right. I do not want default-initialization to happen if there's an explicit initialization. > Neither I do. But I think that the difference is rather fundamental. It > appears all over many other places: in object construction, in > discriminated types, in class-wide types etc. For this reason, I would like > to see some general mechanism for evaluation of the constraints > *independently* from the values; Yes. >... and also for forcing the compiler to > evaluate statically known constraints at compile time and to remove them > from all sorts of dopes. So instead of hiding the skeleton in the cupboard > I would openly present it to the public! (:-)) I don't know how a language standard can *force* things like that. > I see. But that will require definition of which parts of which statements > may act as scopes: loop, if alternative, case choice, exception choice > (already so in the "X : others"-kludge), select choice etc. How many pages > of ARM? (:-)) A small fraction of one page. ;-) Any statement list should act as a scope. > >> T := (1, 2, 4, declare X := 9, others => declare Y := 10); > >> -- What would be this? How many Y's could be here? > > > > No, I don't propose to allow mixing of decls and expressions! > > Just decls and statements. Decls don't return values. > > Once you let them in, then: if I can put declarations among statements, why > cannot I put statements among declarations? ^^^^^^^^^^^^ You mean expressions? You can't put statements (or declarations) among expressions because the type is wrong -- a statement does not return a value of the right type (it doesn't return a value at all, or in C terminology, it returns "void".) > >> So I see no advantage in this. > > > > Convinced? Partly convinced? ;-) > > I see the rationale and the problem, but the solution ... Fair enough. - Bob ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-21 16:17 ` Robert A Duff @ 2005-03-21 18:16 ` Dmitry A. Kazakov 2005-03-21 20:35 ` Robert A Duff 0 siblings, 1 reply; 28+ messages in thread From: Dmitry A. Kazakov @ 2005-03-21 18:16 UTC (permalink / raw) On 21 Mar 2005 11:17:28 -0500, Robert A Duff wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > >> On 17 Mar 2005 16:26:22 -0500, Robert A Duff wrote: >> >>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: >>> >>>> I like the separation very much. >>> >>> You're not alone. >>> >>> Part of my reason is that declarations in Ada execute code at run time. >>> In other languages (e.g. Pascal) there is a strict separation: >>> declarations declare, statements execute, so it makes sense to have >>> a syntactic line drawn between the two. But in Ada, the decls can >>> do real work, so the line drawn by the "begin" is misleading. >> >> Yes, indirectly they do. For the following body this is the code that never >> fail. Similarly, each "end;" is in fact also a code which again never fails >> (for the body above). I can buy this as modularization (can I sell it so >> too? (:-)) > > The problem is that whether something goes above or below the "begin" > depends on all kinds of extraneous factors. If I want to initialize a > variable with a function, it goes above the line. If I want to use > a procedure call, or a loop, it goes below the line. Etc. The effects of the declaration of an initialized variable and of a procedure call are fundamentally different. The first creates a new object for following scope. Evaluation of that object is rather a side effect. All things visible within the scope should be elaborated before "begin". What if I rename "declare" to "require", i.e. consider the declaration block as a precondition for following "begin". Will it look logical then? require X : T := ...; begin -- do something with X end; -- ensure that there is no X anymore The problem and difference with a procedure call is that it has no visible effect. If it had, then that could be put in the declaration block. >>> In some cases, the decls do *all* the work, especially when you're >>> working with Strings, where the length usually depends on the >>> initial value. Then the procedure body says "null;", meaning >>> "this does nothing", which is a lie. >> >> Yes. A similar case is: >> >> if 0 = Some_Win_API (...) then >> null; -- Don't care about your silly return codes! >> end if; > > It doesn't seem similar to me. Here, we are explicitly saying that the > "then" part does nothing, and that's a good thing. But the only effect of Some_Win_API is its side effect. Using if-then-else here is as misleading as null body of a procedure. It could even be written as such a procedure! procedure Some_Win_API_Wrapper (...) is Dummy : BOOL := Some_Win_API (...); begin null; end Some_Win_API_Wrapper; > (Some other > languages are more error prone in this regard.) But a procedure that > does a lot of work that happens to be above the "begin" still needs > "null", which seems sort of silly. I agree. But I think that the solution is a better balance between effects and side effects. I'd like to think of declarations as things done in parallel as opposed to serialized statements. Unfortunately this is also untrue in Ada. Serialization is extra coupling, I just feel that something must be wrong with that (like that "and" vs. "and then" debate, no good arguments for "and", but still...) >> I also dislike unnecessary indentations, but what if exception handling is >> needed? Then you have to create blocks anyway. > > I would prefer a separate try/catch-like statement for exception > handling. In C++ that only increases nesting as compared to Ada. > Exception handling seems totally unrelated to declaring > things, so I don't see why they should be mixed in the same syntax. It is rather mixed with scopes, which seems reasonable. Especially, if we consider exceptions as a part of the contract, which IMO is the only right way. >>... and also for forcing the compiler to >> evaluate statically known constraints at compile time and to remove them >> from all sorts of dopes. So instead of hiding the skeleton in the cupboard >> I would openly present it to the public! (:-)) > > I don't know how a language standard can *force* things like that. But it forces evaluation of static constants. We have to allow user-defined operations there. The type developer should be able to specify whether the discriminant (constraint) is embedded in each value or not. Then there should be a mechanism of constraint propagation like in: type (Size : Positive) is array (...) of Object (Size); >> I see. But that will require definition of which parts of which statements >> may act as scopes: loop, if alternative, case choice, exception choice >> (already so in the "X : others"-kludge), select choice etc. How many pages >> of ARM? (:-)) > > A small fraction of one page. ;-) > Any statement list should act as a scope. That will do the trick! (:-)) >>>> T := (1, 2, 4, declare X := 9, others => declare Y := 10); >>>> -- What would be this? How many Y's could be here? >>> >>> No, I don't propose to allow mixing of decls and expressions! >>> Just decls and statements. Decls don't return values. >> >> Once you let them in, then: if I can put declarations among statements, why >> cannot I put statements among declarations? > ^^^^^^^^^^^^ > You mean expressions? You can't put statements (or declarations) among > expressions because the type is wrong -- a statement does not return a > value of the right type (it doesn't return a value at all, or in C > terminology, it returns "void".) Parameterless functions do something out of nothing (:-)). But in the first place I meant declarations. Isn't it equivalent: if declarations and statements can be mixed then it is commutative and there is no more any distinction between them. So the following should be legal: package Funny is for I in 1..100 loop ... end loop; end Foo; package body Foo is X : Integer := 10; end Foo; BTW, it seems that there is a parallel to if-elsif-elsif-else. What about something in this spirit: declare ... begin <statements> then declare ... begin -- Better keywords needed <statements> then declare ... begin <statements> then declare ... begin <statements> end; It looks more structured than floating declarations. One could allow it in all lists of statements. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-21 18:16 ` Dmitry A. Kazakov @ 2005-03-21 20:35 ` Robert A Duff 2005-03-22 10:55 ` Dmitry A. Kazakov 0 siblings, 1 reply; 28+ messages in thread From: Robert A Duff @ 2005-03-21 20:35 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > The effects of the declaration of an initialized variable and of a > procedure call are fundamentally different. The first creates a new object > for following scope. Evaluation of that object is rather a side effect. All > things visible within the scope should be elaborated before "begin". Well, I've considered a language design in which initialization (or should it be called "creation") is allowed to happen after the declaration. In this language, initialization has a different syntax than assignment (":=" for init, ":==" for assignment, or some such). So you could do: procedure P is X: T'Class; -- perhaps some syntax saying "do not default init". if ... then X := ...; else X := ...; end if; ... You can do that in Ada, but you have to introduce pointers, and you don't always want pointer semantics. I would have compile-time rules that ensure an object is initialized (created?) before being used or assigned; the compiler would have to do a bit of flow analysis to check these rules. If you do *that*, then an 'out' parameter can either assign into the actual, or initialize the actual. Programmer's choice. >... What > if I rename "declare" to "require", i.e. consider the declaration block as > a precondition for following "begin". Will it look logical then? > > require > X : T := ...; > begin > -- do something with X > end; -- ensure that there is no X anymore Sorry, I guess this is a matter of taste, and my taste says I don't like the extra verbiage required just to declare X. Note that I'm not against block statements. I would allow: procedure P is begin X: ...; Y: ...; some statements end; begin -- X and Y not visible here. A: ...; B: ...; some statements end; end P; or something like that. But I wouldn't *require* the begin/end, if you already have "then/end if" or something that delimits the scope. > The problem and difference with a procedure call is that it has no visible > effect. If it had, then that could be put in the declaration block. > > >>> In some cases, the decls do *all* the work, especially when you're > >>> working with Strings, where the length usually depends on the > >>> initial value. Then the procedure body says "null;", meaning > >>> "this does nothing", which is a lie. > >> > >> Yes. A similar case is: > >> > >> if 0 = Some_Win_API (...) then > >> null; -- Don't care about your silly return codes! > >> end if; > > > > It doesn't seem similar to me. Here, we are explicitly saying that the > > "then" part does nothing, and that's a good thing. > > But the only effect of Some_Win_API is its side effect. Using if-then-else > here is as misleading as null body of a procedure. It could even be written > as such a procedure! Yes, I suppose you're right about that. I suppose the best way to write that would be: Ignore_Result(Some_Win_API(...)); where Ignore_Result is a predefined procedure that takes a parameter of type Anything'Class. > procedure Some_Win_API_Wrapper (...) is > Dummy : BOOL := Some_Win_API (...); > begin > null; > end Some_Win_API_Wrapper; > > > (Some other > > languages are more error prone in this regard.) But a procedure that > > does a lot of work that happens to be above the "begin" still needs > > "null", which seems sort of silly. > > I agree. But I think that the solution is a better balance between effects > and side effects. > > I'd like to think of declarations as things done in parallel as opposed to > serialized statements. Unfortunately this is also untrue in Ada. > Serialization is extra coupling, I just feel that something must be wrong > with that (like that "and" vs. "and then" debate, no good arguments for > "and", but still...) I agree, but I think the same is true of statements. I've thought of giving the programmer the choice: evaluate a sequence in parallel (and the compiler checks that there are no bad interactions) or evaluate a sequence in textual order. Perhaps use "," to separate parallel things, and ";" to separate sequential things. So: X: Integer := 1, Y: Integer := 2; Z: Integer := X + Y; meaning first create and initialize X and Y in parallel, and then create and init Z. I agree with your "extra coupling" point, but I think it applies equally to decls and statements. And I think the programmer should have the choice of specifying sequential order (in both cases). > >> I also dislike unnecessary indentations, but what if exception handling is > >> needed? Then you have to create blocks anyway. > > > > I would prefer a separate try/catch-like statement for exception > > handling. > > In C++ that only increases nesting as compared to Ada. That's OK -- the extra nesting implies some meaning. > > Exception handling seems totally unrelated to declaring > > things, so I don't see why they should be mixed in the same syntax. > > It is rather mixed with scopes, which seems reasonable. Especially, if we > consider exceptions as a part of the contract, which IMO is the only right > way. I agree about the contract. Java does this, but it gets some details wrong. I want a way to specify defaults. For example, in a real-time embedded program, I might want to explicitly mark every procedure that can raise Storage_Error, but in a compiler, that's a waste, since pretty-much anything can raise S_E. Also, when passing procedures as parameters, as in iterators, I want a way to say "this thing propagates whatever the passed procedure propagates". > >>... and also for forcing the compiler to > >> evaluate statically known constraints at compile time and to remove them > >> from all sorts of dopes. So instead of hiding the skeleton in the cupboard > >> I would openly present it to the public! (:-)) > > > > I don't know how a language standard can *force* things like that. > > But it forces evaluation of static constants. It pretends to. I claim you can't write an ACATS test that determines whether an Ada compiler evaluates static constants at compile time. But that's OK -- compiler writers are sometimes stupid or lazy, but we can trust them to not deliberately sabotage their products! >...We have to allow user-defined > operations there. The type developer should be able to specify whether the > discriminant (constraint) is embedded in each value or not. Then there > should be a mechanism of constraint propagation like in: > > type (Size : Positive) is array (...) of Object (Size); Yes, I have similar notions about how compilers should deal with discrims (stored with objects vs. not stored with objects). Pragmas seem appropriate here, since it's purely an efficiency choice. It's like pragma Inline -- sometimes you want it, sometimes you don't (both for efficiency), but there's no ACATS test that can detect whether the compiler is obeying your pragmas Inline. Note that Ada 9X proposed to allow discriminated arrays, but unfortunately that was rejected. > >> I see. But that will require definition of which parts of which statements > >> may act as scopes: loop, if alternative, case choice, exception choice > >> (already so in the "X : others"-kludge), select choice etc. How many pages > >> of ARM? (:-)) > > > > A small fraction of one page. ;-) > > Any statement list should act as a scope. > > That will do the trick! (:-)) > > >>>> T := (1, 2, 4, declare X := 9, others => declare Y := 10); > >>>> -- What would be this? How many Y's could be here? > >>> > >>> No, I don't propose to allow mixing of decls and expressions! > >>> Just decls and statements. Decls don't return values. > >> > >> Once you let them in, then: if I can put declarations among statements, why > >> cannot I put statements among declarations? > > ^^^^^^^^^^^^ > > You mean expressions? You can't put statements (or declarations) among > > expressions because the type is wrong -- a statement does not return a > > value of the right type (it doesn't return a value at all, or in C > > terminology, it returns "void".) > > Parameterless functions do something out of nothing (:-)). But in the first > place I meant declarations. Isn't it equivalent: if declarations and > statements can be mixed then it is commutative and there is no more any > distinction between them. So the following should be legal: > > package Funny is > for I in 1..100 loop > ... > end loop; > end Foo; > > package body Foo is > X : Integer := 10; > end Foo; > > BTW, it seems that there is a parallel to if-elsif-elsif-else. What about > something in this spirit: > > declare ... begin > <statements> > then declare ... begin -- Better keywords needed > <statements> > then declare ... begin > <statements> > then declare ... begin > <statements> > end; > > It looks more structured than floating declarations. One could allow it in > all lists of statements. How about if I just promise to leave a blank line between groups of decls and groups of statements? ;-) begin Obj1: ...; Obj2: ...; Grind_Upon(Obj1, Obj2); Obj3: ...; Obj3 := Obj1 + Obj2; Obj3 := Obj3 + 1; end; > -- > Regards, > Dmitry A. Kazakov > http://www.dmitry-kazakov.de - Bob P.S. I suppose the folks who want to talk about Ada on comp.lang.ada (as opposed to some mythical language that is like Ada, only different) are rather bored by this discussion. But I think language design is fun! ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-21 20:35 ` Robert A Duff @ 2005-03-22 10:55 ` Dmitry A. Kazakov 0 siblings, 0 replies; 28+ messages in thread From: Dmitry A. Kazakov @ 2005-03-22 10:55 UTC (permalink / raw) On 21 Mar 2005 15:35:36 -0500, Robert A Duff wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > >> The effects of the declaration of an initialized variable and of a >> procedure call are fundamentally different. The first creates a new object >> for following scope. Evaluation of that object is rather a side effect. All >> things visible within the scope should be elaborated before "begin". > > Well, I've considered a language design in which initialization (or > should it be called "creation") is allowed to happen after the > declaration. In this language, initialization has a different syntax > than assignment (":=" for init, ":==" for assignment, or some such). > > So you could do: > > procedure P is > X: T'Class; -- perhaps some syntax saying "do not default init". > > if ... then > X := ...; > else > X := ...; > end if; > ... What about singleton functions? procedure P is X : T'Class is begin if ... then return ...; else return ...; end if; end X; equivalent to: function <anonymous> return T'Class is begin if ... then return ...; else return ...; end if; end <anonymous>; X : T'Class := <anonymous>; > You can do that in Ada, but you have to introduce pointers, > and you don't always want pointer semantics. > > I would have compile-time rules that ensure an object is initialized > (created?) before being used or assigned; the compiler would have to do > a bit of flow analysis to check these rules. > > If you do *that*, then an 'out' parameter can either assign into the > actual, or initialize the actual. Programmer's choice. Hmm, lazy parameters, could they do the trick? You pass an out parameter as lazy. The compiler skips any construction until first use in the body. So construction vs. assignment would be controlled on the caller's side. >>... What >> if I rename "declare" to "require", i.e. consider the declaration block as >> a precondition for following "begin". Will it look logical then? >> >> require >> X : T := ...; >> begin >> -- do something with X >> end; -- ensure that there is no X anymore > > Sorry, I guess this is a matter of taste, and my taste says I don't like > the extra verbiage required just to declare X. > > Note that I'm not against block statements. I would allow: > > procedure P is > begin > X: ...; > Y: ...; > > some statements > end; > begin > -- X and Y not visible here. > A: ...; > B: ...; > > some statements > end; > end P; > > or something like that. In two steps away from curly brackets!... (:-)) >>> (Some other >>> languages are more error prone in this regard.) But a procedure that >>> does a lot of work that happens to be above the "begin" still needs >>> "null", which seems sort of silly. >> >> I agree. But I think that the solution is a better balance between effects >> and side effects. >> >> I'd like to think of declarations as things done in parallel as opposed to >> serialized statements. Unfortunately this is also untrue in Ada. >> Serialization is extra coupling, I just feel that something must be wrong >> with that (like that "and" vs. "and then" debate, no good arguments for >> "and", but still...) > > I agree, but I think the same is true of statements. I've thought of > giving the programmer the choice: evaluate a sequence in parallel (and > the compiler checks that there are no bad interactions) or evaluate a > sequence in textual order. Perhaps use "," to separate parallel things, > and ";" to separate sequential things. So: > > X: Integer := 1, > Y: Integer := 2; > Z: Integer := X + Y; > > meaning first create and initialize X and Y in parallel, and then create > and init Z. I believe there was something like that in Occam. However if there are two "gluing operators" ',' and ';' then there must be also order brackets for them. > I agree about the contract. Java does this, but it gets some details > wrong. I want a way to specify defaults. For example, in a real-time > embedded program, I might want to explicitly mark every procedure that > can raise Storage_Error, but in a compiler, that's a waste, since > pretty-much anything can raise S_E. Also, when passing procedures as > parameters, as in iterators, I want a way to say "this thing propagates > whatever the passed procedure propagates". Yes. Some kind of contract algebra is definitely needed. The difficult issue is to get it right for interfaces, overriding, generics. >>...We have to allow user-defined >> operations there. The type developer should be able to specify whether the >> discriminant (constraint) is embedded in each value or not. Then there >> should be a mechanism of constraint propagation like in: >> >> type (Size : Positive) is array (...) of Object (Size); > > Yes, I have similar notions about how compilers should deal with > discrims (stored with objects vs. not stored with objects). > Pragmas seem appropriate here, since it's purely an efficiency choice. > It's like pragma Inline -- sometimes you want it, sometimes you don't > (both for efficiency), but there's no ACATS test that can detect > whether the compiler is obeying your pragmas Inline. Yes, if it does not influence the semantics (pragma's def), then there is no way to detect if the compiler obey it... (:-)) However, for differently constrained subtypes there is a backdoor: T'Size = S'Size. Actually T'Size and X'Size violate a lot of things and should probably be disallowed. On the other hand it could be interesting to allow something like: type T is ...; for T'Class'Size use <some-upper-limit>; type Array_Of_T is array (...) of T'Class; -- OK because definite type S is new T with ...; -- Compiler checks the size constraint Though size in bits is too low-level and potentially non-portable. There should be a better way... >> BTW, it seems that there is a parallel to if-elsif-elsif-else. What about >> something in this spirit: >> >> declare ... begin >> <statements> >> then declare ... begin -- Better keywords needed >> <statements> >> then declare ... begin >> <statements> >> then declare ... begin >> <statements> >> end; >> >> It looks more structured than floating declarations. One could allow it in >> all lists of statements. > > How about if I just promise to leave a blank line between groups of > decls and groups of statements? ;-) I believe you (:-)), but what would you do with: begin Obj1: ...; Obj2: ...; Grind_Upon(Obj1, Obj2); if Halt (x) then goto A; end if; <<B>> Obj3: ...; <<A>> Obj3 := Obj1 + Obj2; Obj3 := Obj3 + 1; if Halt (y) then goto B; end if; end; The above would require multiple construction/destruction of Obj3, stack wind-ups all at run-time etc. > P.S. I suppose the folks who want to talk about Ada on comp.lang.ada (as > opposed to some mythical language that is like Ada, only different) are > rather bored by this discussion. They are busy writing AIs for Ada 2010! (:-)) > But I think language design is fun! Yes -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: advice on package design 2005-03-17 14:04 ` Robert A Duff 2005-03-17 15:59 ` Dmitry A. Kazakov @ 2005-03-17 23:23 ` Randy Brukardt 1 sibling, 0 replies; 28+ messages in thread From: Randy Brukardt @ 2005-03-17 23:23 UTC (permalink / raw) "Robert A Duff" <bobduff@shell01.TheWorld.com> wrote in message news:wccpsxy1i8l.fsf@shell01.TheWorld.com... > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > > > Presently it is not explicit. Syntactically overriding is undistinguishable > > from declaring a new operation. This is IMO bad. It should be sort of: > > > > procedure Override (X : Object) is ????; > > -- Overriding intended, fails if base types have no primitive Override > > procedure Override (X : Object); > > -- Overloading intended, fails if it hides any other Override > > I think Ada 2005 has something like that. Yes. overriding procedure Override (X : Object); -- Overriding intended, fails if base types have no primitive Override not overriding procedure Override (X : Object); -- Overloading intended, fails if it hides any other Override Note the suggested indentation. If you have neither, you revert to the Ada 95 rules. That turns out to be necessary in some cases for private types and for generics. This would have saved us a lot of headaches constructing Claw. Randy. ^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2005-03-22 10:55 UTC | newest] Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2005-03-07 16:23 advice on package design spambox 2005-03-07 21:08 ` Dmitry A. Kazakov 2005-03-08 12:48 ` spambox 2005-03-08 17:18 ` Dmitry A. Kazakov 2005-03-12 19:57 ` Robert A Duff 2005-03-12 20:45 ` Dmitry A. Kazakov 2005-03-12 21:59 ` Robert A Duff 2005-03-13 9:23 ` Dmitry A. Kazakov 2005-03-16 20:41 ` Robert A Duff 2005-03-17 10:22 ` Dmitry A. Kazakov 2005-03-17 14:04 ` Robert A Duff 2005-03-17 15:59 ` Dmitry A. Kazakov 2005-03-17 19:10 ` Robert A Duff 2005-03-17 19:47 ` Martin Dowie 2005-03-17 20:55 ` Robert A Duff 2005-03-17 21:14 ` Marius Amado Alves 2005-03-18 9:31 ` Martin Dowie 2005-03-18 9:38 ` Martin Dowie 2005-03-21 16:19 ` Robert A Duff 2005-03-17 20:48 ` Dmitry A. Kazakov 2005-03-17 21:26 ` Robert A Duff 2005-03-18 3:06 ` Jared 2005-03-18 10:00 ` Dmitry A. Kazakov 2005-03-21 16:17 ` Robert A Duff 2005-03-21 18:16 ` Dmitry A. Kazakov 2005-03-21 20:35 ` Robert A Duff 2005-03-22 10:55 ` Dmitry A. Kazakov 2005-03-17 23:23 ` Randy Brukardt
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox