* Limited use for limited with? @ 2010-09-28 7:37 Maciej Sobczak 2010-09-28 9:04 ` Alex R. Mosteo ` (4 more replies) 0 siblings, 5 replies; 54+ messages in thread From: Maciej Sobczak @ 2010-09-28 7:37 UTC (permalink / raw) I have found a problem with the intended application of limited with. Consider a package specifying some object-oriented construct: package Objects is type Object is interface; type Object_Access is access all Object'Class; procedure Do_Something (X : in out Object) is abstract; end Objects; In such cases I routinely define the XYZ_Access type and later use it wherever pointers to class-wide XYZ are needed. Now consider a package that uses the above in a limited way (pun intended), where only pointers to class-wide type are needed: with Objects; package Object_Users is procedure Use_Object (X : Objects.Object_Access); end Object_Users; The problem is that is some cases it would be more convenient (or just more self-documenting from the design perspective) to do limited with instead, but unfortunately this makes Object_Access unavailable. It is OK to use anonymous access type, at least in some cases like here: procedure Use_Object (X : access Objects.Object'Class); but I find that uncomfortable - after all, the proper access type is already defined for exactly this purpose. In such cases, where the design intent is pretty clear (pass around references to Objects) I find that limited with does not really bring the functionality that it is supposed to provide. In some more involving cases I was forced to introduce additional and completely artificial packages, where limited with would be a perfect fit. Any thoughts on this? -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 7:37 Limited use for limited with? Maciej Sobczak @ 2010-09-28 9:04 ` Alex R. Mosteo 2010-09-30 7:24 ` Stephen Leake 2010-09-28 9:18 ` Ludovic Brenta ` (3 subsequent siblings) 4 siblings, 1 reply; 54+ messages in thread From: Alex R. Mosteo @ 2010-09-28 9:04 UTC (permalink / raw) Maciej Sobczak wrote: > I have found a problem with the intended application of limited with. > > Consider a package specifying some object-oriented construct: > > package Objects is > > type Object is interface; > type Object_Access is access all Object'Class; > > procedure Do_Something (X : in out Object) is abstract; > > end Objects; > > In such cases I routinely define the XYZ_Access type and later use it > wherever pointers to class-wide XYZ are needed. > > Now consider a package that uses the above in a limited way (pun > intended), where only pointers to class-wide type are needed: > > with Objects; > package Object_Users is > > procedure Use_Object (X : Objects.Object_Access); > > end Object_Users; > > The problem is that is some cases it would be more convenient (or just > more self-documenting from the design perspective) to do limited with > instead, but unfortunately this makes Object_Access unavailable. It is > OK to use anonymous access type, at least in some cases like here: > > procedure Use_Object (X : access Objects.Object'Class); > > but I find that uncomfortable - after all, the proper access type is > already defined for exactly this purpose. > > In such cases, where the design intent is pretty clear (pass around > references to Objects) I find that limited with does not really bring > the functionality that it is supposed to provide. In some more > involving cases I was forced to introduce additional and completely > artificial packages, where limited with would be a perfect fit. > > Any thoughts on this? Completely unhelpful on my part -- but my limited (ha) attempts at using "limited with" have been a failure. I'm no expert at OO or C++, like you, so I can't bring any expertise to the table. Something that can be of interest in this context is a discussion that happened not too long ago (half a year?) in some mailing list -- I thought it was the GAP one, but combing the topics I can't find it. People trying to do "proper" OO with the new Ada features was complaining about some unavoidable quirks. The discussion point was on the truth of "unavoidable". I'm failing at locating it, if it rings some bells for someone... Alex. > > -- > Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 9:04 ` Alex R. Mosteo @ 2010-09-30 7:24 ` Stephen Leake 2010-09-30 9:21 ` Alex R. Mosteo 0 siblings, 1 reply; 54+ messages in thread From: Stephen Leake @ 2010-09-30 7:24 UTC (permalink / raw) "Alex R. Mosteo" <alejandro@mosteo.invalid> writes: > Maciej Sobczak wrote: > > Something that can be of interest in this context is a discussion that > happened not too long ago (half a year?) in some mailing list -- I thought > it was the GAP one, but combing the topics I can't find it. People trying to > do "proper" OO with the new Ada features was complaining about some > unavoidable quirks. The discussion point was on the truth of "unavoidable". > I'm failing at locating it, if it rings some bells for someone... Perhaps you are thinking of this: http://groups.google.com/group/comp.lang.ada/browse_thread/thread/7ff1de84a8945e80/664c96862ae388aa?q=Access+types+as+parameters&pli=1 My results are summarized here: http://www.stephe-leake.org/ada/access_vs_object.html -- -- Stephe ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-30 7:24 ` Stephen Leake @ 2010-09-30 9:21 ` Alex R. Mosteo 0 siblings, 0 replies; 54+ messages in thread From: Alex R. Mosteo @ 2010-09-30 9:21 UTC (permalink / raw) Stephen Leake wrote: > "Alex R. Mosteo" <alejandro@mosteo.invalid> writes: > >> Maciej Sobczak wrote: >> >> Something that can be of interest in this context is a discussion that >> happened not too long ago (half a year?) in some mailing list -- I >> thought it was the GAP one, but combing the topics I can't find it. >> People trying to do "proper" OO with the new Ada features was complaining >> about some unavoidable quirks. The discussion point was on the truth of >> "unavoidable". I'm failing at locating it, if it rings some bells for >> someone... > > Perhaps you are thinking of this: > > http://groups.google.com/group/comp.lang.ada/browse_thread/thread/7ff1de84a8945e80/664c96862ae388aa?q=Access+types+as+parameters&pli=1 > > My results are summarized here: > > http://www.stephe-leake.org/ada/access_vs_object.html Although very interesting, that was not the one. There were some guys teaching OO with Ada (perhaps one of them was Italian?)... ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 7:37 Limited use for limited with? Maciej Sobczak 2010-09-28 9:04 ` Alex R. Mosteo @ 2010-09-28 9:18 ` Ludovic Brenta 2010-09-28 12:59 ` Maciej Sobczak ` (2 more replies) 2010-09-28 9:32 ` Vadim Godunko ` (2 subsequent siblings) 4 siblings, 3 replies; 54+ messages in thread From: Ludovic Brenta @ 2010-09-28 9:18 UTC (permalink / raw) Maciej Sobczak wrote on comp.lang.ada: > I have found a problem with the intended application of limited with. > > Consider a package specifying some object-oriented construct: > > package Objects is > > type Object is interface; > type Object_Access is access all Object'Class; > > procedure Do_Something (X : in out Object) is abstract; > > end Objects; > > In such cases I routinely define the XYZ_Access type and later use it > wherever pointers to class-wide XYZ are needed. > > Now consider a package that uses the above in a limited way (pun > intended), where only pointers to class-wide type are needed: > > with Objects; > package Object_Users is > > procedure Use_Object (X : Objects.Object_Access); > > end Object_Users; > > The problem is that is some cases it would be more convenient (or just > more self-documenting from the design perspective) to do limited with > instead, but unfortunately this makes Object_Access unavailable. It is > OK to use anonymous access type, at least in some cases like here: > > procedure Use_Object (X : access Objects.Object'Class); > > but I find that uncomfortable - after all, the proper access type is > already defined for exactly this purpose. > > In such cases, where the design intent is pretty clear (pass around > references to Objects) I find that limited with does not really bring > the functionality that it is supposed to provide. In some more > involving cases I was forced to introduce additional and completely > artificial packages, where limited with would be a perfect fit. > > Any thoughts on this? I generally think twice or three times before declaring an access type in the same package as the object type. In fact, I think twice before declaring any access type at all :) To me, an access type makes the package unclean. Since, in Ada, all objects of tagged types are passed by reference and since Ada has class-wide types, you do not need any access type to achieve pass-by-reference semantics or dynamic dispatching. This leaves dynamic memory allocation as the only remaining justification for access types. If I need dynamic memory allocation, I'd consider using a container (possibly indefinite) before introducing an access type. By this reasoning, a general (!) class-wide access type to an interface (as opposed to a tagged type) may not be justified as you're never going to dynamically allocate interfaces, only tagged types that implement the interface. If, after all these musings, I decide that an access type is really necessary after all (perhaps because I'm writing my own container), then I usually declare it where I need the access type, i.e. in another unit, possibly even in the body. Like so: package Objects is type Object is interface; procedure Do_Something (X : in out Object) is abstract; end Objects; with Objects; procedure Client is type Object_Access is access Objects.Object'Class; -- not "access all" O : Object_Access := new Objects.Object; begin ... end Client; Taking this approach further, I can declare the access type in a nested scope. When the access type goes out of scope, all objects allocated through it are deallocated. This is a form of automatic garbage collection. If you take this approach, your "limited with" problem will disappear. As an extra bonus, you can make your package Objects Preelaborated, or even Pure. -- Ludovic Brenta. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 9:18 ` Ludovic Brenta @ 2010-09-28 12:59 ` Maciej Sobczak 2010-09-28 13:45 ` Dmitry A. Kazakov 2010-09-28 15:15 ` Ludovic Brenta 2010-09-28 15:54 ` Robert A Duff 2010-09-30 7:27 ` Stephen Leake 2 siblings, 2 replies; 54+ messages in thread From: Maciej Sobczak @ 2010-09-28 12:59 UTC (permalink / raw) On 28 Wrz, 11:18, Ludovic Brenta <ludo...@ludovic-brenta.org> wrote: > I generally think twice or three times before declaring an access type > in the same package as the object type. In fact, I think twice before > declaring any access type at all :) So let's say that I already did the thinking and the most prevalent use case for access types is callback registration. Think about AWS dispatchers for the closest analogy. In fact, the AWS.Server.Start procedure has a version that takes the callback by access. It is an access to function, but I need it to be object-oriented. Hiding the use of access values behind the scenes (by virtue of tagged types being always passed by reference) would obstruct the code without clear benefit. This is what AWS does in its other versions of Start, but I don't like it. I want to express this: 1. Object is an interface type for the callback that will be implemented by user. 2. Object_Access is a type that will be used for declaring and passing around callback references. I don't want everybody to define their own types for what is a common functionality. I totally agree that in Ada the pressure for using access values is much smaller than in C++, but object registration (in a map, perhaps) is not addressed by any other language feature. BTW - it is exactly for the fact that there is little need for dynamic allocation that Object_Access has to be "access all" and not just "access". Should I drop the Object_Access type altogether and mess with locally- defined access types in other parts of the code? -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 12:59 ` Maciej Sobczak @ 2010-09-28 13:45 ` Dmitry A. Kazakov 2010-09-28 21:57 ` Maciej Sobczak 2010-09-28 15:15 ` Ludovic Brenta 1 sibling, 1 reply; 54+ messages in thread From: Dmitry A. Kazakov @ 2010-09-28 13:45 UTC (permalink / raw) On Tue, 28 Sep 2010 05:59:59 -0700 (PDT), Maciej Sobczak wrote: > Hiding the use of access values behind the scenes (by virtue of tagged > types being always passed by reference) would obstruct the code > without clear benefit. The benefit is clear - no access types. > This is what AWS does in its other versions of > Start, but I don't like it. > > I want to express this: > > 1. Object is an interface type for the callback that will be > implemented by user. > 2. Object_Access is a type that will be used for declaring and passing > around callback references. I don't want everybody to define their own > types for what is a common functionality. You should register the object itself rather than its pointer. Use a constructing function which registers the object by placing it in a list or do an explicit call to Register. Upon finalization Finalize would remove it from there. The type of the object must be limited of course. This is the schema I am using. > BTW - it is exactly for the fact that there is little need for dynamic > allocation that Object_Access has to be "access all" and not just > "access". I tend to avoid "all" everywhere I can. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 13:45 ` Dmitry A. Kazakov @ 2010-09-28 21:57 ` Maciej Sobczak 2010-09-29 6:03 ` Ludovic Brenta 2010-09-29 7:51 ` Dmitry A. Kazakov 0 siblings, 2 replies; 54+ messages in thread From: Maciej Sobczak @ 2010-09-28 21:57 UTC (permalink / raw) On 28 Wrz, 15:45, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > Hiding the use of access values behind the scenes (by virtue of tagged > > types being always passed by reference) would obstruct the code > > without clear benefit. > > The benefit is clear - no access types. There are still access types. The fact that they are not explicitly seen in operation signatures just makes it more obscure. No benefit. Consider: procedure Register (X : in Object'Class); X will be passed by reference, but there is still nothing in the signature (apart from the name of the operation) that would suggest that the reference will be leaked out of the operation's scope. So the user does this: declare X : My_Concrete_Object; begin Register (X); endl; and bang, everything breaks into pieces. Whereas this: procedure Register (X : Object_Access); at least forces the user to think about scopes. I therefore consider it to be a safer construct. > You should register the object itself rather than its pointer. Use a > constructing function which registers the object by placing it in a list or > do an explicit call to Register. No way. There are many Registers and it's up to the user to decide where the given object should be registered; constructor has no such knowledge. And *when* it should be registered, which is not necessarily at construction time. > I tend to avoid "all" everywhere I can. Apparently here I cannot. -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 21:57 ` Maciej Sobczak @ 2010-09-29 6:03 ` Ludovic Brenta 2010-09-29 8:25 ` Maciej Sobczak 2010-09-29 7:51 ` Dmitry A. Kazakov 1 sibling, 1 reply; 54+ messages in thread From: Ludovic Brenta @ 2010-09-29 6:03 UTC (permalink / raw) Maciej Sobczak writes on comp.lang.ada: > Consider: > > procedure Register (X : in Object'Class); > > X will be passed by reference, but there is still nothing in the > signature (apart from the name of the operation) that would suggest > that the reference will be leaked out of the operation's scope. So the > user does this: > > declare > X : My_Concrete_Object; > begin > Register (X); > endl; > > and bang, everything breaks into pieces. No, because this is Ada :) Suppose Register does something like (where Registry is a suitably defined indefinite vector): procedure Register (X : in Object'Class) is begin Registry.Append (X); end Register; This does copy a "hidden" reference to X into the Registry, it copies X itself (i.e. deep copy). So there is no leak and the construct is safe. Not so with explicit access values. -- Ludovic Brenta. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-29 6:03 ` Ludovic Brenta @ 2010-09-29 8:25 ` Maciej Sobczak 0 siblings, 0 replies; 54+ messages in thread From: Maciej Sobczak @ 2010-09-29 8:25 UTC (permalink / raw) On 29 Wrz, 08:03, Ludovic Brenta <ludo...@ludovic-brenta.org> wrote: > > Consider: > > > procedure Register (X : in Object'Class); > > > X will be passed by reference, but there is still nothing in the > > signature (apart from the name of the operation) that would suggest > > that the reference will be leaked out of the operation's scope. So the > > user does this: > > > declare > > X : My_Concrete_Object; > > begin > > Register (X); > > endl; > > > and bang, everything breaks into pieces. > > No, because this is Ada :) [...] I was thinking about Register storing the access value somewhere, not copying the whole object. The whole idea of registering something is that state changes that are provoked by the registry are visible to the original creator of the object. Copying X by value makes no sense in this scenario, because object updates would not affect the original object. I want to logically register *my* object, not its copy. And of course there is still the possibility of Object to be limited (which is actually the case) - then no deep copy is possible. -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 21:57 ` Maciej Sobczak 2010-09-29 6:03 ` Ludovic Brenta @ 2010-09-29 7:51 ` Dmitry A. Kazakov 2010-09-29 8:38 ` Maciej Sobczak 1 sibling, 1 reply; 54+ messages in thread From: Dmitry A. Kazakov @ 2010-09-29 7:51 UTC (permalink / raw) On Tue, 28 Sep 2010 14:57:49 -0700 (PDT), Maciej Sobczak wrote: > On 28 Wrz, 15:45, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>> Hiding the use of access values behind the scenes (by virtue of tagged >>> types being always passed by reference) would obstruct the code >>> without clear benefit. >> >> The benefit is clear - no access types. > > There are still access types. The fact that they are not explicitly > seen in operation signatures just makes it more obscure. No, that makes it (the public interface) more clear. Pointer is an implementation detail to be hidden. > Consider: > > procedure Register (X : in Object'Class); > > X will be passed by reference, but there is still nothing in the > signature (apart from the name of the operation) that would suggest > that the reference will be leaked out of the operation's scope. It shall not leak. This is why Finalize has to unregister the object before killing it. > So the > user does this: > > declare > X : My_Concrete_Object; > begin > Register (X); > endl; > > and bang, everything breaks into pieces. Nothing breaks, X is finalized and removed from the internal list. >> You should register the object itself rather than its pointer. Use a >> constructing function which registers the object by placing it in a list or >> do an explicit call to Register. > > No way. There are many Registers and it's up to the user to decide > where the given object should be registered; constructor has no such > knowledge. He decides that upon construction: declare X : Object := Create (My_Repository); > And *when* it should be registered, which is not necessarily at > construction time. A good design rule is that all objects are usable at each point of its existence. But if unregistered object are OK, then provide a Register operation: procedure Register (X : in out Object, Y : in out Repository'Class); >> I tend to avoid "all" everywhere I can. > > Apparently here I cannot. Because you have a model in mind, which treats objects as dynamically allocated entities. This is a possible design, though it has disadvantages comparing to scoped object. But even with this design you don't necessarily need public pointers. You use handles to objects encapsulating private pointers to privately defined object types. It is more difficult to pursue in Ada because Ada lacks delegation (or, alternatively, abstract access type). Nevertheless in Ada 2005 it is quite possible. The idea is that you declare an interface type. Then you implement it in the handle object and in the private implementation object. Handle's operation implementation consists of wrappers. Interface / \ / \ Handle ---> Object Object goes into private children packages. Handle's component (pointer to class-wide) are private. It is quite boring because of the wrappers, and a combinatorial explosion upon derivation. But I deployed this schema in some projects. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-29 7:51 ` Dmitry A. Kazakov @ 2010-09-29 8:38 ` Maciej Sobczak 2010-09-29 9:16 ` Dmitry A. Kazakov 2010-10-05 7:25 ` Randy Brukardt 0 siblings, 2 replies; 54+ messages in thread From: Maciej Sobczak @ 2010-09-29 8:38 UTC (permalink / raw) On 29 Wrz, 09:51, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > There are still access types. The fact that they are not explicitly > > seen in operation signatures just makes it more obscure. > > No, that makes it (the public interface) more clear. Pointer is an > implementation detail to be hidden. Reference leak is not an implementation detail, it is a very important part of the contract. > > No way. There are many Registers and it's up to the user to decide > > where the given object should be registered; constructor has no such > > knowledge. > > He decides that upon construction: > > declare > X : Object := Create (My_Repository); No way. There are *many* Registers, meaning that a single object can be registered in several registries, not just one. Will you suggest passing an array of registries to the constructor? I hope not. The problem with your approach is that it tries to bend the design of the system in order to work around some language limitation. I prefer having it the other way round. > > And *when* it should be registered, which is not necessarily at > > construction time. > > A good design rule is that all objects are usable at each point of its > existence. But if unregistered object are OK, then provide a Register > operation: > > procedure Register (X : in out Object, Y : in out Repository'Class); And we are back to the beginning. If this uses pointers internally, then the reference leak is not expressed in the signature. It is inherently unsafe. And if it performs deep-copy of X, then it breaks the association with original object. > >> I tend to avoid "all" everywhere I can. > > > Apparently here I cannot. > > Because you have a model in mind, which treats objects as dynamically > allocated entities. Not at all. In practice all these objects will be created at library level or as locals in subprograms (perhaps in the main procedure). No dynamic memory is needed here, but the ability for objects to refer each other is still essential. This is the case where pointers seem to be necessary even though nothing was allocated dynamically. And that's why we are talking "access all" here. > But even with this design you don't necessarily need public pointers. You > use handles to objects encapsulating private pointers to privately defined > object types. [...] > Interface > / \ > / \ > Handle ---> Object Sure, but: - it does not solve the problem, it only changes an explicit problem into a wrapped problem - this is exactly what I meant when I said that in more involved scenario I have to create additional and artificial packages - they don't correspond to any system design entity In short, I still don't see a plausible solution to my problem and from all poor solutions that I'm aware of, the one I already use seems to be the simplest. -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-29 8:38 ` Maciej Sobczak @ 2010-09-29 9:16 ` Dmitry A. Kazakov 2010-09-29 12:22 ` Maciej Sobczak 2010-10-05 7:25 ` Randy Brukardt 1 sibling, 1 reply; 54+ messages in thread From: Dmitry A. Kazakov @ 2010-09-29 9:16 UTC (permalink / raw) On Wed, 29 Sep 2010 01:38:40 -0700 (PDT), Maciej Sobczak wrote: > On 29 Wrz, 09:51, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>> There are still access types. The fact that they are not explicitly >>> seen in operation signatures just makes it more obscure. >> >> No, that makes it (the public interface) more clear. Pointer is an >> implementation detail to be hidden. > > Reference leak is not an implementation detail, it is a very important > part of the contract. It does not leak. >>> No way. There are many Registers and it's up to the user to decide >>> where the given object should be registered; constructor has no such >>> knowledge. >> >> He decides that upon construction: >> >> declare >> � �X : Object := Create (My_Repository); > > No way. There are *many* Registers, meaning that a single object can > be registered in several registries, not just one. Will you suggest > passing an array of registries to the constructor? I hope not. If that is the object's property to be registered in multiple lists, e.g. when the list is static, then yes. Otherwise you register it dynamically. >>> And *when* it should be registered, which is not necessarily at >>> construction time. >> >> A good design rule is that all objects are usable at each point of its >> existence. But if unregistered object are OK, then provide a Register >> operation: >> >> � �procedure Register (X : in out Object, Y : in out Repository'Class); > > And we are back to the beginning. If this uses pointers internally, > then the reference leak is not expressed in the signature. It is > inherently unsafe. I see no unsafety. References are maintained by the object itself. It is the safest possible way. [I presume that the scope of the list heads encloses ones of the objects.] > And if it performs deep-copy of X, then it breaks the association with > original object. No it does not. Object is a limited type. >>>> I tend to avoid "all" everywhere I can. >> >>> Apparently here I cannot. >> >> Because you have a model in mind, which treats objects as dynamically >> allocated entities. > > Not at all. In practice all these objects will be created at library > level or as locals in subprograms (perhaps in the main procedure). No > dynamic memory is needed here, but the ability for objects to refer > each other is still essential. OK, that would be a directed graph. Graphs are doable, but difficult without pointers, fortunately graphs are very rarely needed. > In short, I still don't see a plausible solution to my problem and > from all poor solutions that I'm aware of, the one I already use seems > to be the simplest. You didn't state it yet. The point is that it is a good advise not to expose both referential types and the object types in public interfaces. You do either of them and hide another. If that is impossible in some particular cases, that is an Ada problem, which is better to address, rather than keep on mounting language kludges, which "limited with" possibly is, IMO. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-29 9:16 ` Dmitry A. Kazakov @ 2010-09-29 12:22 ` Maciej Sobczak 2010-09-29 13:41 ` Dmitry A. Kazakov 0 siblings, 1 reply; 54+ messages in thread From: Maciej Sobczak @ 2010-09-29 12:22 UTC (permalink / raw) On 29 Wrz, 11:16, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > I see no unsafety. References are maintained by the object itself. It is > the safest possible way. [I presume that the scope of the list heads > encloses ones of the objects.] This would create circular dependency between Object and Registry's internals. That is, Registry refers to Object (to fulfill the design objective) and Object refers to Registry (to implement the automatic unregistration). Yuck. Note also that Object is an interface. I don't control the concrete implementation - in particular I don't implement the factory (constructor function). For this reason I cannot do what you propose. Similarly, I don't implement the finalizer and I don't even want to impose Controlled on the concrete type - that would not only expose implementation mess in the public API, but also prevent me from using interfaces. Good bye multiple inheritance, for example. The solution that you propose is not only very complex, it also introduces new problems that would have to be addressed and that would make the API much more difficult to use. > The point is that it is a good advise not to expose both referential types > and the object types in public interfaces. I don't see any explanation of this advice. The solution with explicitly defined access types seems to be the simplest one, as it is the least intrusive with respect to the system design. -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-29 12:22 ` Maciej Sobczak @ 2010-09-29 13:41 ` Dmitry A. Kazakov 2010-09-29 15:07 ` Georg Bauhaus 2010-09-29 20:51 ` Maciej Sobczak 0 siblings, 2 replies; 54+ messages in thread From: Dmitry A. Kazakov @ 2010-09-29 13:41 UTC (permalink / raw) On Wed, 29 Sep 2010 05:22:59 -0700 (PDT), Maciej Sobczak wrote: > On 29 Wrz, 11:16, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >> I see no unsafety. References are maintained by the object itself. It is >> the safest possible way. [I presume that the scope of the list heads >> encloses ones of the objects.] > > This would create circular dependency between Object and Registry's > internals. You add one derived type to either of the hierarchies and that breaks the dependency. You have to with or without access types. The dependency is here, the "limited with" kludge does not remove it. > That is, Registry refers to Object (to fulfill the design > objective) and Object refers to Registry (to implement the automatic > unregistration). Yuck. Object would call a class-wide operation of Registry from its Finalize. > Note also that Object is an interface. I customary derive first abstract root type if I forced to use interfaces (I dislike interfaces). > I don't control the concrete > implementation - in particular I don't implement the factory > (constructor function). You declare the factory function abstract to be implemented by derived types. > Similarly, I don't implement the finalizer and I don't even want to > impose Controlled on the concrete type - that would not only expose > implementation mess in the public API, but also prevent me from using > interfaces. Good bye multiple inheritance, for example. That should be decided for each concrete case. Something close to MI could be achieved by using storage pools. But API infested by pointers is the worst thing to imagine. > The solution that you propose is not only very complex, I don't see how removing a type could add complexity. It didn't add new operations, it only moved access types elsewhere. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-29 13:41 ` Dmitry A. Kazakov @ 2010-09-29 15:07 ` Georg Bauhaus 2010-09-29 19:22 ` Dmitry A. Kazakov 2010-09-29 20:51 ` Maciej Sobczak 1 sibling, 1 reply; 54+ messages in thread From: Georg Bauhaus @ 2010-09-29 15:07 UTC (permalink / raw) On 29.09.10 15:41, Dmitry A. Kazakov wrote: > On Wed, 29 Sep 2010 05:22:59 -0700 (PDT), Maciej Sobczak wrote: >> That is, Registry refers to Object (to fulfill the design >> objective) and Object refers to Registry (to implement the automatic >> unregistration). Yuck. > > Object would call a class-wide operation of Registry from its Finalize. If Object's design needs to be informed about future Registry objects, how so? And what about decoupling? Georg ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-29 15:07 ` Georg Bauhaus @ 2010-09-29 19:22 ` Dmitry A. Kazakov 0 siblings, 0 replies; 54+ messages in thread From: Dmitry A. Kazakov @ 2010-09-29 19:22 UTC (permalink / raw) On Wed, 29 Sep 2010 17:07:01 +0200, Georg Bauhaus wrote: > On 29.09.10 15:41, Dmitry A. Kazakov wrote: >> On Wed, 29 Sep 2010 05:22:59 -0700 (PDT), Maciej Sobczak wrote: > >>> That is, Registry refers to Object (to fulfill the design >>> objective) and Object refers to Registry (to implement the automatic >>> unregistration). Yuck. >> >> Object would call a class-wide operation of Registry from its Finalize. > > If Object's design needs to be informed about future Registry > objects, how so? It need not, and you can dispatch from it anyway. Since Ada does not have full MD, one of the objects has to be class-wide. It is difficult to maintain two full-scale hierarchies of Objects and Registries. Normally the designer is forced to flatten one of them. This is why absence of MD is damaging to the design, but not in this case. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-29 13:41 ` Dmitry A. Kazakov 2010-09-29 15:07 ` Georg Bauhaus @ 2010-09-29 20:51 ` Maciej Sobczak 2010-09-29 21:18 ` Dmitry A. Kazakov 2010-10-05 7:35 ` Randy Brukardt 1 sibling, 2 replies; 54+ messages in thread From: Maciej Sobczak @ 2010-09-29 20:51 UTC (permalink / raw) On 29 Wrz, 15:41, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > > This would create circular dependency between Object and Registry's > > internals. > > You add one derived type to either of the hierarchies and that breaks the > dependency. You have to with or without access types. Not at all. In my design Object knows nothing about registry. There is no such dependency and therefore it does not have to be broken. You have introduced this dependency, but this is one of the many reasons why I don't like your solution. > The dependency is > here, the "limited with" kludge does not remove it. The motivation to use "limited with" came from other reasons, which are not directly related to registry. > Object would call a class-wide operation of Registry from its Finalize. This assumes that Registry is object-oriented and that all registries have some common interface. I don't want this assumption, it does not reflect any system requirement. > > I don't control the concrete > > implementation - in particular I don't implement the factory > > (constructor function). > > You declare the factory function abstract to be implemented by derived > types. And how can I enforce that user will properly use the registry from the constructor? I cannot assume this. I don't even want to impose the existence of any factory functions - they are not needed as far as Object and Registry are concerned. You have introduced this concept artificially as part of your solution, but it does not reflect any design objective. This is yet another reason why I don't like it. > > The solution that you propose is not only very complex, > > I don't see how removing a type could add complexity. In order to "remove a type" you have introduced: - factory functions, perhaps abstract - Controlled with Finalized - circular dependencies between Object and Registry (ironically, you have then proposed to introduce *another derived type* to break that dependency - does it still count as "removing a type"?) - common tagged root for all registries (this seems to be also an additional type) - storage pools (?) As far as I'm concerned, your solution evolved to a monster. I therefore stick to my single access type. :-) -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-29 20:51 ` Maciej Sobczak @ 2010-09-29 21:18 ` Dmitry A. Kazakov 2010-10-05 7:35 ` Randy Brukardt 1 sibling, 0 replies; 54+ messages in thread From: Dmitry A. Kazakov @ 2010-09-29 21:18 UTC (permalink / raw) On Wed, 29 Sep 2010 13:51:53 -0700 (PDT), Maciej Sobczak wrote: > On 29 Wrz, 15:41, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> > wrote: > >>> This would create circular dependency between Object and Registry's >>> internals. >> >> You add one derived type to either of the hierarchies and that breaks the >> dependency. You have to with or without access types. > > Not at all. In my design Object knows nothing about registry. There is > no such dependency and therefore it does not have to be broken. > You have introduced this dependency, but this is one of the many > reasons why I don't like your solution. I did not. You have to pass an object or its pointer to the registry, or a registry to the object. It is no matter, the operation has two arguments. The dependency is here. >> Object would call a class-wide operation of Registry from its Finalize. > > This assumes that Registry is object-oriented and that all registries > have some common interface. I don't want this assumption, it does not > reflect any system requirement. It is not an assumption. All registries are capable to maintain objects. The set of registry types shares this property, even if you don't introduce this set as a class, which would be an unwise decision, the class is here. >>> I don't control the concrete >>> implementation - in particular I don't implement the factory >>> (constructor function). >> >> You declare the factory function abstract to be implemented by derived >> types. > > And how can I enforce that user will properly use the registry from > the constructor? By hiding other ways to construct the parent type. If the object is declared with (<>) discriminants and its full view is hidden you have to use the construction function in the aggregate that creates the object. >>> The solution that you propose is not only very complex, >> >> I don't see how removing a type could add complexity. > > In order to "remove a type" you have introduced: > - factory functions, perhaps abstract I don't expose object components anyway. It is a good style to have components private. If Initialize does not do the job, factory function is the only choice, because, unfortunately, Ada does not have proper constructors with parameters. > - Controlled with Finalized Same as above. Non-controlled tagged types make no sense, because, again, Ada lacks constructors and destructors. > - circular dependencies between Object and Registry (ironically, you > have then proposed to introduce *another derived type* to break that > dependency - does it still count as "removing a type"?) The dependency exists anyway. [ And it is not circular. The relation "there exists an operation with arguments of both types" is symmetric. ] > - common tagged root for all registries (this seems to be also an > additional type) A big advantage, because later I might wish to write some class-wide operations dealing with any registry. It happened to me so many times, that I practically always use controlled bases. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-29 20:51 ` Maciej Sobczak 2010-09-29 21:18 ` Dmitry A. Kazakov @ 2010-10-05 7:35 ` Randy Brukardt 2010-10-08 8:05 ` Maciej Sobczak 1 sibling, 1 reply; 54+ messages in thread From: Randy Brukardt @ 2010-10-05 7:35 UTC (permalink / raw) "Maciej Sobczak" <see.my.homepage@gmail.com> wrote in message news:f059316d-9994-4ea4-b984-f9530768bfbc@d25g2000yqc.googlegroups.com... ... > Not at all. In my design Object knows nothing about registry. There is > no such dependency and therefore it does not have to be broken. But the Registries are completely unsafe! They have no way to protect against dangling pointers, so any client mistake (and clients make *lots* of mistakes) will cause the entire system to crash or malfunction. (And this will be the worst kind of crash, with no possible way to finding where it came from, since the crash will occur long after the actual error and not be tied to it in any way.) It's always better to prevent abuse in the interface. And in this case, I don't think you have any choice unless you are willing to assume that your clients are perfect (ha!). Storing arbitrary general access values is no safer than storing a link to an object without finalization protection. You shouldn't do either one of them. Unless you like to spend many hours in a debugger, single-stepping your code, you will find some way to protect the registries against the possibility of dangling pointers. And that is going to require some sort of cooperation (or deep copies, which I agree will often not work). Randy. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-05 7:35 ` Randy Brukardt @ 2010-10-08 8:05 ` Maciej Sobczak 2010-10-09 6:29 ` Randy Brukardt 0 siblings, 1 reply; 54+ messages in thread From: Maciej Sobczak @ 2010-10-08 8:05 UTC (permalink / raw) On 5 Paź, 09:35, "Randy Brukardt" <ra...@rrsoftware.com> wrote: > > Not at all. In my design Object knows nothing about registry. There is > > no such dependency and therefore it does not have to be broken. > > But the Registries are completely unsafe! They have no way to protect > against dangling pointers, That's right. The problem is that none of the proposed solutions is adequate here, for reasons that I have explained already. My point is that this is one of those places where using plain pointers is the best solution, even taking into account all its potential problems. Important note: in my design dangling pointers are prevented not by registry or its obscure API, but by the purpose of the whole. In 100% cases that are known to me the objects outlive the registry, so there is no possibility to create dangling pointers. Granted, users *can* write an artificial and nonsense code (perhaps to prove the point) that will create dangling pointers, but no amount of protection will prevent such intentional misuse. Note that even now creating a dangling pointer requires explicit use of 'Unchecked_Access. No matter what you do, you cannot protect the user against him using Unchecked_XXX tools, which will always make all your protections useless, no matter how sophisticated. In short: you can protect against Murphy, but not against Machiavelli. > It's always better to prevent abuse in the interface. Unless the "protection" makes the system unusable. Clarity and ease of use are important goals, too. Ironically, they even promote correctness. -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-08 8:05 ` Maciej Sobczak @ 2010-10-09 6:29 ` Randy Brukardt 0 siblings, 0 replies; 54+ messages in thread From: Randy Brukardt @ 2010-10-09 6:29 UTC (permalink / raw) "Maciej Sobczak" <see.my.homepage@gmail.com> wrote in message news:37e167dc-1741-4627-bef4-1fd8b32bdbeb@c10g2000yqh.googlegroups.com... ... >Important note: in my design dangling pointers are prevented not by >registry or its obscure API, but by the purpose of the whole. In 100% >cases that are known to me the objects outlive the registry, so there >is no possibility to create dangling pointers. I suppose it depends upon the use. If you are the *only* person that will ever work on this code, and you never make mistakes, then that might in fact be OK. >Granted, users *can* write an artificial and nonsense code (perhaps to >prove >the point) that will create dangling pointers, but no amount of protection >will >prevent such intentional misuse. Careful: I agree that intentional misuse (such as using Unchecked_Conversion to avoid checks) isn't really protectable. But most misuse is accidental, and can even happen by someone that knows better. It's really easy to declare an object inside of a subprogram by mistake. Someone (probably you) *will* do it sometime soon. Accessibility *might* protect you, but I've only have one single instance in my entire programming career where I was able to use 'Access; everywhere else I *had* to use 'Unchecked_Access (usually because the argument was a parameter) -- so I wouldn't count on that to be much help. If there are other programming using your packages, then you pretty much have to assume these sorts errors will happen. So what do you want to happen when someone accidentally misuses your code? The entire program mysteriously locks up? Or some sort of detection? I know which I prefer -- in Claw, we went to great lengths to ensure that everything will work cleanly no matter where someone declares a window -- in part because handling support calls are expensive. (And even helping programmers on your team in such cases is expensive.) >> It's always better to prevent abuse in the interface. > >Unless the "protection" makes the system unusable. Clarity and ease of >use are important goals, too. Ironically, they even promote >correctness. True, but there is nothing clear about a callback registry. It completely sacrifies safety (not just via dangling pointers, but also type safety) in order to make something easier for the programmer (the one person for whom "ease" is irrelevant). I think it is just a hack. Randy. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-29 8:38 ` Maciej Sobczak 2010-09-29 9:16 ` Dmitry A. Kazakov @ 2010-10-05 7:25 ` Randy Brukardt 2010-10-08 8:23 ` Maciej Sobczak 1 sibling, 1 reply; 54+ messages in thread From: Randy Brukardt @ 2010-10-05 7:25 UTC (permalink / raw) "Maciej Sobczak" <see.my.homepage@gmail.com> wrote in message news:292dd0bd-1fc4-4715-bb70-7655d0dc04eb@j24g2000yqa.googlegroups.com... On 29 Wrz, 09:51, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: >> No, that makes it (the public interface) more clear. Pointer is an >> implementation detail to be hidden. > >Reference leak is not an implementation detail, it is a very important >part of the contract. Dmitry is right, the *reference* ought to be part of the object, so a leak is impossible. In any case, using an access type in the specification of Register doesn't do anything to say how long the access will be used for, so that doesn't help any. (C and C++ interfaces are notorious for this problem: there is often no way to tell whether the access needs to remain valid until some later time, nor what time that is.) >No way. There are *many* Registers, meaning that a single object can >be registered in several registries, not just one. Will you suggest >passing an array of registries to the constructor? I hope not. This sounds like chaos, not a design. There is no way to figure out anything that happens with callbacks, registered or not. Callbacks are dubious to begin with; moreover, when objects need to be registered that should only happen with a very small number of places (hopefully one). Note that Claw uses exactly the design that Dmitry is describing. Each Window can be linked into a handful of data structures -- but we use Finalize to ensure it is not linked on any when the object is destroyed. The alternative of making all of this the client's problem is simply not "the Ada way". (It also would have made Claw technical support impossible; we had plenty of bizarre problems even with all of the automatic cleanup, some because compiler bugs didn't always clean things up. Without that cleanup, it would have been total chaos -- it is viritually impossible to manually clean up objects when exceptions happen [some are always missed] -- and exceptions are *always* happening in an Ada program [especially during testing].) >The problem with your approach is that it tries to bend the design of >the system in order to work around some language limitation. I prefer >having it the other way round. The "design" sounds more like copying bad ideas from some other implementation for some other programming language. >In short, I still don't see a plausible solution to my problem and >from all poor solutions that I'm aware of, the one I already use seems >to be the simplest. When you start with a poor design, it's not surprising you can only find poor solutions... Randy. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-05 7:25 ` Randy Brukardt @ 2010-10-08 8:23 ` Maciej Sobczak 2010-10-09 6:13 ` Randy Brukardt 0 siblings, 1 reply; 54+ messages in thread From: Maciej Sobczak @ 2010-10-08 8:23 UTC (permalink / raw) On 5 Paź, 09:25, "Randy Brukardt" <ra...@rrsoftware.com> wrote: > >No way. There are *many* Registers, meaning that a single object can > >be registered in several registries, not just one. Will you suggest > >passing an array of registries to the constructor? I hope not. > > This sounds like chaos, not a design. Let me guess: comp.lang.ada is the only group you are registered to, right? And of course, you are subscribed to only one mailing list, right? The possibility to register somewhere should be orthogonal to the number of registries. If I was able to subscribe to comp.lang.ada, I want to be able to subscribe to several other groups, too. It's not a chaos, it's an obvious result of decoupling. > >The problem with your approach is that it tries to bend the design of > >the system in order to work around some language limitation. I prefer > >having it the other way round. > > The "design" sounds more like copying bad ideas from some other > implementation for some other programming language. This design does not contain any elements that would be particular to "some other programming language", or at least you have not shown it yet. Access values are inherent part of Ada and for example Timing Events (D.15) are nothing else but callback registries, so I don't think I'm doing anything extraordinary or outside of the "Ada way". In other words, "chaos" and "bad ideas" are not appropriate words here. > When you start with a poor design, it's not surprising you can only find > poor solutions... Translation: "I don't understand the purpose of the system, but the fact that my proposed solutions are not adequate is a sure proof that the original design must be broken." Is that right? -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-08 8:23 ` Maciej Sobczak @ 2010-10-09 6:13 ` Randy Brukardt 2010-10-10 14:13 ` Maciej Sobczak 0 siblings, 1 reply; 54+ messages in thread From: Randy Brukardt @ 2010-10-09 6:13 UTC (permalink / raw) "Maciej Sobczak" <see.my.homepage@gmail.com> wrote in message news:09c36bd6-edfa-42bf-8f33-e91b0a9b0737@26g2000yqv.googlegroups.com... On 5 Paz, 09:25, "Randy Brukardt" <ra...@rrsoftware.com> wrote: >> >No way. There are *many* Registers, meaning that a single object can >> >be registered in several registries, not just one. Will you suggest >> >passing an array of registries to the constructor? I hope not. >> >> This sounds like chaos, not a design. > >Let me guess: comp.lang.ada is the only group you are registered to, right? Yes. >And of course, you are subscribed to only one mailing list, right? More than one of those. >The possibility to register somewhere should be orthogonal to the >number of registries. If I was able to subscribe to comp.lang.ada, I >want to be able to subscribe to several other groups, too. It's not a >chaos, it's an obvious result of decoupling. But those things are registered by name (an simple text string), not registering an object. When you want to put something in lots of places, you have have an abstract way to refer to it. Yes, you can use an access type to do that, but it is a lousy solution, because of dangling pointers, because it can't be stored, because there is no structure. An example: in the Claw Builder, I tried to use access types to tie the various objects together. But it didn't work, because the structure is a graph of elements that are combined in various ways. There wasn't any way to restore the structure once it was saved to disk, saving the structure often saved elements multiple times, and similar problems. Moreover, the declarations all have to be placed in one giant package (a problem that Ada 2005 tries to solve, but not very successfully). Eventually, I changed most of "links" to use names, which are then looked up in an appropriate index. ... >> When you start with a poor design, it's not surprising you can only find >> poor solutions... >Translation: "I don't understand the purpose of the system, but the >fact that my proposed solutions are not adequate is a sure proof that >the original design must be broken." Is that right? When I'm writing an Ada program, I want a design that can be expressed well in Ada. I don't much care whether it can be expressed well in some other language. And "well" includes safety against accidental misuse. Use of access types in object interfaces almost always compromises safety. A design that can't be expressed well in Ada is a poor design (presuming you are planning to write the program in Ada). It would be the same for Java, C++, or any other programming language. I personally think that callbacks are a poor fit for Ada (possibly because I started with Ada 83, where callbacks were impossible). It's not always possible to avoid them (i.e. GUI programming), but I don't think they should ever be introduced when there is an alternative. And if you do that, you don't need the "registration" in the first place. So I think a design using callbacks and registration is a "poor design". YMMV. Randy. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-09 6:13 ` Randy Brukardt @ 2010-10-10 14:13 ` Maciej Sobczak 2010-10-11 6:23 ` Randy Brukardt 0 siblings, 1 reply; 54+ messages in thread From: Maciej Sobczak @ 2010-10-10 14:13 UTC (permalink / raw) On 9 Paź, 08:13, "Randy Brukardt" <ra...@rrsoftware.com> wrote: > Eventually, I changed most > of "links" to use names, which are then looked up in an appropriate index. And what's in the index? The index becomes a registry with the same problems, just moved elsewhere. There are three options: 1. index/registry contains the actual objects - this has a big impact on how the objects are created; this might impose limitations that users will not want to accept 2. index/registry contains deep copies of the actual objects - then, the updates will not be propagated 3. index/registry contains access values to actual objects - then, beware dangling pointers 3a. objects know how to remove themselves from the index/registry - this has a big impact on how the objects are destroyed and on how the type is defined (forget interfaces); this in turn might impose limitations that users will not want to accept, either ? > A design that can't be expressed well in Ada is a poor design So let's be constructive. The problem is the following: - there is some activity happening in the environment that generates stimulation that should be handled by the program - there is some common interface for the propagation of this stimulation - users want to implement their handling routines In short (hey, that's a candidate for a design pattern): Event Handler. Ada provides nice solution for interrupts, but not all such scenarios are related to interrupts. Think about HTTP requests (AWS), RPC invocations (PolyORB), alarm notifications, and so on - in many cases all such things are delivered over the network, but this is not necessary. How would you express such a pattern in Ada? I still don't see a plausible solution and declaring the one that is simplest, most readable, and most flexible for the final user to be not "the Ada way" is not constructive, either. > I personally think that callbacks are a poor fit for Ada So how to solve the above problem in Ada properly? Or should we say that Ada is a poor fit for this class of problems instead? > It's not always > possible to avoid them (i.e. GUI programming), but I don't think they should > ever be introduced when there is an alternative. So what is the alternative? Please do not repeat the ones that were already dismissed. -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-10 14:13 ` Maciej Sobczak @ 2010-10-11 6:23 ` Randy Brukardt 2010-10-12 19:29 ` Maciej Sobczak 0 siblings, 1 reply; 54+ messages in thread From: Randy Brukardt @ 2010-10-11 6:23 UTC (permalink / raw) "Maciej Sobczak" <see.my.homepage@gmail.com> wrote in message news:c76f3559-7c89-4536-b92f-951da5a44170@m1g2000vbh.googlegroups.com... On 9 Paz, 08:13, "Randy Brukardt" <ra...@rrsoftware.com> wrote: >> Eventually, I changed most >> of "links" to use names, which are then looked up in an appropriate >> index. > >And what's in the index? The index becomes a registry with the same >problems, just moved elsewhere. Well, I'd call it a database, not a "registry". > There are three options: > >1. index/registry contains the actual objects - this has a big impact >on how the objects are created; this might impose limitations that >users will not want to accept Clients of your abstraction have to live with whatever restrictions you (the abstraction author) imposes. Or they have to find a different abstraction. In our case, we did this, because we needed the capability to make the object database persistent. That can't be usefully done with access types (at least not when you have a web of interconnecting links). The clients of the abstraction had to live within the limitations; else the users of the end program would have been very unhappy (Save or Open wouldn't work). >2. index/registry contains deep copies of the actual objects - then, >the updates will not be propagated I don't think this is a real choice. >3. index/registry contains access values to actual objects - then, >beware dangling pointers >3a. objects know how to remove themselves from the index/registry - >this has a big impact on how the objects are destroyed and on how the >type is defined (forget interfaces); this in turn might impose >limitations that users will not want to accept, either IMHO, Interfaces are worthless. For me, only implementation inheritance is worth bothering with, and you can't do that with interfaces. The contortions that we've gone through trying to use them for the queue containers pretty much have proved the point to me. But YMMV. After all, the implementation of controlled types in Janus/Ada is exactly this model: each object "knows" how to finalize itself, and the "registry" determines when the object needs to be finalized. There cannot be any dangling pointers (at least not from this mechanism, other program bugs can damage the links that register the objects and cause bugs that are nearly impossible to debug). To use this model, the only issue is that you have to derive all of your types from a "Registerable" type. That wouldn't be a big deal, unless you need to add this property to the middle of an existing derivation tree. Ada doesn't have sane way to do that (mixin generics can work, but are horrible to write and horrible to use). > ? >> A design that can't be expressed well in Ada is a poor design > >So let's be constructive. The problem is the following: > >- there is some activity happening in the environment that generates >stimulation that should be handled by the program >- there is some common interface for the propagation of this >stimulation I don't believe this is true often enough in practice to be a reasonable stipulation. >- users want to implement their handling routines > In short (hey, that's a candidate for a design pattern): Event Handler. >Ada provides nice solution for interrupts, but not all such scenarios >are related to interrupts. Think about HTTP requests (AWS), RPC >invocations (PolyORB), alarm notifications, and so on - in many cases >all such things are delivered over the network, but this is not >necessary. Generally, such notifications include data. So you have to deliver the data somehow. If you use a single interface, you break type safety with accessing the data (at best, you have some sort of runtime check which adds to your testing load). In every instance where I've had to do some sort of event handling, I've written a separate specification for each handler. And a separate way of invoking (since generally the notications come from some non-Ada mechanism: TCP/IP, or a Windows Message, or an interrupt, etc.; and each of these things triggers an event in a different way). >How would you express such a pattern in Ada? I still don't see a >plausible solution and declaring the one that is simplest, most >readable, and most flexible for the final user to be not "the Ada way" >is not constructive, either. You have already decided on a solution, and are trying to shoehorn that into Ada. But you are losing safety: both type safety and program safety (because of the dangling pointer issues) by doing so. Every solution has tradeoffs, and you are picking the one that has the worst attributes from an Ada perspective for the purpose of making programming easier by the "final user" (really a programmer, and not really final; my mother is a "final user" and I'm certain she's not creating event handlers :-). [Note: I usually reserve "user" for the person who uses the program created by the programmer, and use "programmer" or "client" when talking about the person who uses a library package or other Ada code. That's very important when talking about something like Claw, where the user of Claw and the user of the program created by the use of Claw are very different people -- and the needs of both have to be considered.] >> I personally think that callbacks are a poor fit for Ada >So how to solve the above problem in Ada properly? > >Or should we say that Ada is a poor fit for this class of problems >instead? Ada is a poor fit for the "Event Handler" as you described it, but I believe that pattern is likely to be a bad choice in most instances. >> It's not always >> possible to avoid them (i.e. GUI programming), but I don't think they >> should >> ever be introduced when there is an alternative. > >So what is the alternative? Please do not repeat the ones that were >already dismissed. Well, if you're not going to consider the solutions, it's probably not worth discussing further. I don't believe that there is any general pattern that makes sense (for Ada or any other language for that matter); it depends on the use of the events, the data that needs to be carried along, and other issues. For instance, for Claw, we used OOP dispatching to deliver events. This didn't require any Ada registration because Windows (and all other GUIs I've looked at) include a window handle in the event message; from the window handle you can get the associated Ada object (it's stored within Windows when the window is created), and then we make the appropriate dispatching call. The majority of the handlers have separate interfaces with custom parameter lists so that strong typing prevents mistakes in the use of the data. OOP IS harder for the programmers to use (Ada requires a lot of typing to overriding routines), and harder for the author as well, but is better than raw callbacks as it is impossible to "register" the wrong kind of object to get a callback. (Doing that is one of the hardest bugs to figure out, as your object just doesn't get its event.) In a few cases in Claw we used an OOP data block with a number of extensions (so we could send different data types to a single handler, by declaring appropriate extensions). This worked but is not very satisfactory, because any mistakes cannot be caught until runtime. That causes them to get past testing. We've already talked about the Claw Builder; I don't have much to add about that. Janus/Ada doesn't use any events. In the Web Server and Spam Filter, the events come from TCP/IP. In this case, we don't want a callback per-se because we want to treat each event as a "job", and schedule each "job" to be run by one of a pool of tasks. The tasks are generally declared by one or more task types; I've always used dedicated task types for particular kinds of jobs (for instance, receiving, filtering, and sending e-mail is done by different sets of tasks). The primary purpose of this is to ensure that a single expensive request doesn't prevent other requests from being processed. This reduces the chance of a denial-of-service problem. I guess my point is that you need different patterns for handling "events" depending upon whether you need tasking, whether OOP is acceptable, and so on. I don't think that there is a general pattern that is acceptable. I also think that any pattern that you use has to tie the events and objects together, else you've lost any hope of compile-time type safety and quite possibly made the system much harder to test. Randy. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-11 6:23 ` Randy Brukardt @ 2010-10-12 19:29 ` Maciej Sobczak 2010-10-12 20:19 ` Dmitry A. Kazakov 2010-10-13 2:09 ` Randy Brukardt 0 siblings, 2 replies; 54+ messages in thread From: Maciej Sobczak @ 2010-10-12 19:29 UTC (permalink / raw) On 11 Paź, 08:23, "Randy Brukardt" <ra...@rrsoftware.com> wrote: > > There are three options: > > >1. index/registry contains the actual objects - this has a big impact > >on how the objects are created; this might impose limitations that > >users will not want to accept > In our case, we did this, because we needed the capability to make the > object database persistent. This is not my goal and there is no such requirement in my system, so I don't consider it a reason for not using access types. > >3. index/registry contains access values to actual objects > IMHO, Interfaces are worthless. I hope you understand that this is a very strong statement. It basically stands in direct opposition to the work that has been done to introduce them into the language. > The contortions > that we've gone through trying to use them for the queue containers There is no obligation to use them where they are not adequate. However, this says absolutely nothing about their use in those places where they *are* useful. > >So let's be constructive. The problem is the following: > > >- there is some activity happening in the environment that generates > >stimulation that should be handled by the program > >- there is some common interface for the propagation of this > >stimulation > > I don't believe this is true often enough in practice to be a reasonable > stipulation. Let's leave this requirement out of the picture. In fact, this is not a requirement at all, but since Ada is a strongly-typed language, there *must* be some specification of the protocol to pass the event between program modules. > >- users want to implement their handling routines > > In short (hey, that's a candidate for a design pattern): Event Handler. > Generally, such notifications include data. Typically, yes. > So you have to deliver the data > somehow. Typically, the data will be passed as a parameter of the dispatching operation that defines the protocol. > If you use a single interface, you break type safety with accessing > the data No, not at all. The type safety is not compromised in any way. Consider: procedure Handle_Event (H : Handler_Access; Data : in Integer); (where type Handler_Access is access all Handler'Class) In what way is the type safety of Data compromised here? > In every instance where I've had to do some sort of event handling, I've > written a separate specification for each handler. That's OK. These are defined as interfaces in my system. > You have already decided on a solution, and are trying to shoehorn that into > Ada. No. I have reviewed the available language features and I have chosen the one that allows me to solve the problem in the best way. You still have not proven it otherwise. :-) > But you are losing safety: both type safety and program safety (because > of the dangling pointer issues) by doing so. Type safety is not compromised. All operations are strongly types. Some of them might be lately bound (dispatching), but all are strongly typed. If you think otherwise, please explain. Program safety is not compromised due to the fact that all reasonable use cases guarantee that handlers outlive the registries. > Every solution has tradeoffs, > and you are picking the one that has the worst attributes from an Ada > perspective I'm still waiting for you to show it. And I still don't see it. > For instance, for Claw, we used OOP dispatching to deliver events. This > didn't require any Ada registration because Windows (and all other GUIs I've > looked at) include a window handle in the event message; GUI has the property that data sources and receivers are (typically) bound in the 1:1 way. This allowed you to cheat a bit by piggy-backing the object reference in the window handle. This is not applicable in general. > In the Web Server and Spam Filter, the events come from TCP/IP. In this > case, we don't want a callback per-se because we want to treat each event as > a "job", and schedule each "job" to be run by one of a pool of tasks. Yes, this is a possible approach, but tasks and jobs are bound in the 1:N way (that is, each job is delivered to exactly one task). Basic multitasking patterns involving queues or other buffers are OK for this. But it is not applicable in general. Think about how messed up it would become for N:M case (each job delivered to potentially many tasks). It is true that in my system it *would* be possible to do this, but I have chosen not to do it in order to avoid forcing the user (client for you) to create additional tasks on his side. It is perfectly possible to run the whole system in a single-tasking environment - that is, the one that does not even support multiple tasks. Since my system is targeted at embedded installations (among others), I consider this to be an important feature. The user/client can always add tasking if needed. > I don't think that there is a general pattern that is acceptable. I agree. But we are now discussing something different: you seem to state that *some* pattern is *universally UNacceptable*, no matter what - and this is something I don't agree with. -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-12 19:29 ` Maciej Sobczak @ 2010-10-12 20:19 ` Dmitry A. Kazakov 2010-10-13 2:09 ` Randy Brukardt 1 sibling, 0 replies; 54+ messages in thread From: Dmitry A. Kazakov @ 2010-10-12 20:19 UTC (permalink / raw) On Tue, 12 Oct 2010 12:29:28 -0700 (PDT), Maciej Sobczak wrote: > On 11 Paďż˝, 08:23, "Randy Brukardt" <ra...@rrsoftware.com> wrote: > >> IMHO, Interfaces are worthless. > > I hope you understand that this is a very strong statement. It > basically stands in direct opposition to the work that has been done > to introduce them into the language. How could it be otherwise if that work damaged Ada? I am pleasantly surprised that a prominent ARG member considers interfaces [at least] worthless. It is a progress, I would say. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-12 19:29 ` Maciej Sobczak 2010-10-12 20:19 ` Dmitry A. Kazakov @ 2010-10-13 2:09 ` Randy Brukardt 2010-10-13 8:44 ` Georg Bauhaus 2010-10-13 9:43 ` Maciej Sobczak 1 sibling, 2 replies; 54+ messages in thread From: Randy Brukardt @ 2010-10-13 2:09 UTC (permalink / raw) "Maciej Sobczak" <see.my.homepage@gmail.com> wrote in message news:1f38791b-a579-49ed-839c-e6ff3867de76@30g2000yqm.googlegroups.com... On 11 Paz, 08:23, "Randy Brukardt" <ra...@rrsoftware.com> wrote: >> > There are three options: >> >> >1. index/registry contains the actual objects - this has a big impact >> >on how the objects are created; this might impose limitations that >> >users will not want to accept > >> In our case, we did this, because we needed the capability to make the >> object database persistent. > >This is not my goal and there is no such requirement in my system, so >I don't consider it a reason for not using access types. Persistence is a very common requirement, so for general facilities it *is* a reason to avoid access types. ... >> IMHO, Interfaces are worthless. > >I hope you understand that this is a very strong statement. It >basically stands in direct opposition to the work that has been done >to introduce them into the language. I DID a lot of that work. It isn't the only work that I've done on the language that I didn't think was worthwhile (don't get me started on coextensions). But there are a lot of people that would disagree with me on interfaces; we added it for them, not for me! In every instance that I have ever wanted to use an abstract type, I have wanted to provide some data and or default implementations with that abstract type. The classic example is controlled types (where the private data of the root types provide the links used to manage the finalization), but that has happened in several places in Claw as well. Having to implement the same operation in the same way in almost every extension of an interface is not likely to add anything to maintainability nor to readability. So what "ility" is it adding?? Classwide routines are great for doing operations that are always handled the same way. But if the operation is *usually* done one way but in some circumstances needs to be done differently, overriding is needed and the inability to have concrete operations requires lots of manual repetition. In addition, hardly anything is common enough to really benefit from interfaces. Every hierarchy that I have dealt with is very shallow, with only a handful of levels of derivation. There isn't that many operations that are shared between object types. >> The contortions >> that we've gone through trying to use them for the queue containers > >There is no obligation to use them where they are not adequate. >However, this says absolutely nothing about their use in those places >where they *are* useful. The queue containers are about the most useful item that I could actually imagine for interfaces. This is a case with multiple plasible implementations and a well-defined set of operations that work with each implementation unmodified. If it doesn't work well on the queues, I'm skeptical that it can work well anywhere. ... >> If you use a single interface, you break type safety with accessing >> the data > >No, not at all. The type safety is not compromised in any way. >Consider: > > procedure Handle_Event (H : Handler_Access; Data : in Integer); > >(where type Handler_Access is access all Handler'Class) > >In what way is the type safety of Data compromised here? It's not. But it is highly unlikely that different events need the same data. So you have to have separate event handlers for each event. Once you have done that, there no longer is a common mechanism for registering/triggering events -- you have to recode it separately for each event. If that's the case, the concerns about coupling are pretty much eliminated, as both the object and the event have to know about each other anyway. >> In every instance where I've had to do some sort of event handling, I've >> written a separate specification for each handler. > >That's OK. These are defined as interfaces in my system. And how does this help? You can't make a call to some routine with an unknown parameter list, just because it is defined in an interface. So you still end up with a separate call to each handler, and all the interface has bought you is additional complication and lots of runtime overhead. ... >> But you are losing safety: both type safety and program safety (because >> of the dangling pointer issues) by doing so. > >Type safety is not compromised. All operations are strongly types. >Some of them might be lately bound (dispatching), but all are strongly >typed. If you think otherwise, please explain. You and I must be thinking of different solutions, because I can't see any way for that to work with a single handler registry type. And if you have multiple such types, I don't see why you'd want to introduce the complication: just deliver the events directly to the appropriate objects. >Program safety is not compromised due to the fact that all reasonable >use cases guarantee that handlers outlive the registries. Again, you are saying that no one that will program your system will make a mistake. It must be nice to have such perfect programmers -- I'd like to meet one someday. ;-) Work on the Ada standard and on Ada compilers has repeately reminded me that even if some use is an obvious pathology, it is pretty common that someone will try to use it. And it's not unusual that they've found a plasible use for it. (Programmers as a group are more clever than language designers or compiler writers, even if the latter individuals are brilliant.) So I always want to prevent misuse up-front, by the design if possible or by compile-time rules checks. >> Every solution has tradeoffs, >> and you are picking the one that has the worst attributes from an Ada >> perspective > >I'm still waiting for you to show it. And I still don't see it. Ada is all about tasking and about nested items. You are adopting a solution that will work poorly for both. And I've show the problems repeatedly. You don't believe they are problems because you are certain that no one will every try to use your code differently that you intended. In the unlikely case that you are right, I agree with you: any old design will work well -- use any spaggetti code that you want and it would *still* work. But I don't think you are being realistic: for instance, I want to avoid allocating objects when possible, because there are a lot less memory leaks when you let the compiler do the cleanup. So I declare a lot of objects in subprograms and in blocks. That doesn't mean that I wouldn't need one of them to handle an event! Anyway, I think we have to agree to disagree on this one, because we don't even have the same worldview. ... >> In the Web Server and Spam Filter, the events come from TCP/IP. In this >> case, we don't want a callback per-se because we want to treat each event >> as >> a "job", and schedule each "job" to be run by one of a pool of tasks. > >Yes, this is a possible approach, but tasks and jobs are bound in the >1:N way (that is, each job is delivered to exactly one task). Basic >multitasking patterns involving queues or other buffers are OK for this. > >But it is not applicable in general. Think about how messed up it >would become for N:M case (each job delivered to potentially many >tasks). N:M would be what I called chaos at the start of this thread! There has to be some structure to the events or you really do have chaos. So you have to partition that into a set of events that all go to a single object/task, and broadcast events that each go to a set of objects/tasks. That is, the program is a bunch of 1:N and N:1 events, and they don't (directly) interact. >It is true that in my system it *would* be possible to do this, but I >have chosen not to do it in order to avoid forcing the user (client >for you) to create additional tasks on his side. It is perfectly >possible to run the whole system in a single-tasking environment - >that is, the one that does not even support multiple tasks. Since my >system is targeted at embedded installations (among others), I >consider this to be an important feature. The user/client can always >add tasking if needed. It's too late at that point, as you now have a single event handler task as a bottleneck. If you need tasks, you have to add them early to get even a semblance of parallel execution. Even so, I understand your concern. Not all systems can use tasking effectively (although this is something that is changing rapidly, multicore chips are becoming the norm). I think it is necessary to use different patterns in the two cases ("jobs" vs. "events"), and I don't think it is a good idea to try to unify them. >> I don't think that there is a general pattern that is acceptable. > >I agree. But we are now discussing something different: you seem to >state that *some* pattern is *universally UNacceptable*, no matter >what - and this is something I don't agree with. There is nothing that is ever that black-and-white. This is a pattern that I think is best avoided if possible. But it clearly isn't always possible to avoid it. Registration is, for instance, the best way to build an object factory in Ada. But it is interesting that you're usually not storing pointers into the registration map in that case. Randy. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-13 2:09 ` Randy Brukardt @ 2010-10-13 8:44 ` Georg Bauhaus 2010-10-15 0:59 ` Randy Brukardt 2010-10-13 9:43 ` Maciej Sobczak 1 sibling, 1 reply; 54+ messages in thread From: Georg Bauhaus @ 2010-10-13 8:44 UTC (permalink / raw) On 10/13/10 4:09 AM, Randy Brukardt wrote: > "Maciej Sobczak"<see.my.homepage@gmail.com> wrote in message >> But it is not applicable in general. Think about how messed up it >> would become for N:M case (each job delivered to potentially many >> tasks). > > N:M would be what I called chaos at the start of this thread! There has to > be some structure to the events or you really do have chaos. So you have to > partition that into a set of events that all go to a single object/task, and > broadcast events that each go to a set of objects/tasks. That is, the > program is a bunch of 1:N and N:1 events, and they don't (directly) > interact. The design seems to assume one program of unchanging object structure once it has been output by the compiler. After that, it is known to be organized such and such, known to the one instance of program integration. Some organizational unit is aware of its total design. Assume instead a program composed of parts such that organizational boundaries will prevent knowing the total design, in every detail. For example, it is not known what exactly N and M will be (for N:M or even N:1). The system has a changing number of superior objects (agents, triggering events) and inferior objects (handling events). Software companies write components, other software companies compose programs from the components. Engineers add and remove components, during system operation if possible. But no one knows beforehand the set of events, or the set of handlers. Typical Superiors might be called The_Mechanical_Seargant, Task_Queue, Central_Services, Boss_Miller, The_President, Radar, or Klaxon. Inferiors are identified by Worker, Mr_Hill, Valve, Sink, or Magic_Apparatus. There may one or many of each of those, their number can change, and they may be turned on or off. That is N(t):M(t), t in Time. For example, Worker(13) might be busy and thus is unavailable for handing any event. Boss_Miller might be off. The_President might visit the site and trigger an unforeseen event, no matter what. Broadcast then really means broadcast: just any event handler can pick an event and react as programmed. Or not. But the programmers who wrote the handlers had not known about the totality (there you have another "ility" ;-) of circumstances of events, or their origin, or their specific type. Neither did the system makers foresee all circumstances triggering an event, or handlers available. Neither party may be able to predict all additions of events or handlers to some partition, or their removal, or malfunctioning. Is it realistic to assume that such a large, multi-organization, changing system can be modeled with "known specific handler" in mind? Without pointers, but scope based? (And isn't something like Linda tuple spaces, IIRC, directly opposed to scope based and deterministic solutions?) Georg ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-13 8:44 ` Georg Bauhaus @ 2010-10-15 0:59 ` Randy Brukardt 0 siblings, 0 replies; 54+ messages in thread From: Randy Brukardt @ 2010-10-15 0:59 UTC (permalink / raw) "Georg Bauhaus" <rm-host.bauhaus@maps.futureapps.de> wrote in message news:4cb57156$0$7654$9b4e6d93@newsspool1.arcor-online.net... > On 10/13/10 4:09 AM, Randy Brukardt wrote: ... > Is it realistic to assume that such a large, multi-organization, > changing system can be modeled with "known specific handler" > in mind? Without pointers, but scope based? Ada would be a lousy choice for such a system. Most of Ada's advantages are predicated on compile-time checking of correctness, and that is impossible by definition for a dynamically changing system without a defined structure. You could use Ada if you have very strong interfaces (and I don't mean interfaces the language feature but simply the totality of the type definitions, subprogram profiles and the like) for the plug-ins. But that still requires a lot more organization than you are postulating here. I wouldn't argue that there are such systems -- any widely-used OS would be similar. (Although even there the interfaces and interactions could be much better defined than in your thought experiment). Just because they exist doesn't mean that Ada is well-suited to program them. Randy. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-13 2:09 ` Randy Brukardt 2010-10-13 8:44 ` Georg Bauhaus @ 2010-10-13 9:43 ` Maciej Sobczak 1 sibling, 0 replies; 54+ messages in thread From: Maciej Sobczak @ 2010-10-13 9:43 UTC (permalink / raw) On 13 Paź, 04:09, "Randy Brukardt" <ra...@rrsoftware.com> wrote: > >> IMHO, Interfaces are worthless. > > >I hope you understand that this is a very strong statement. It > >basically stands in direct opposition to the work that has been done > >to introduce them into the language. > > I DID a lot of that work. [...] > we added it for them, not for me! This shows that there are very interesting forces in action in ARG. While we're at it, I believe that real multiple inheritance would be much better solution, but I accept interfaces as something that is still useful. Not having both of them would be very limiting. > >No, not at all. The type safety is not compromised in any way. > >Consider: > > > procedure Handle_Event (H : Handler_Access; Data : in Integer); > > >(where type Handler_Access is access all Handler'Class) > > >In what way is the type safety of Data compromised here? > > It's not. But it is highly unlikely that different events need the same > data. So you have to have separate event handlers for each event. Once you > have done that, there no longer is a common mechanism for > registering/triggering events -- you have to recode it separately for each > event. Yes. > If that's the case, the concerns about coupling are pretty much > eliminated, as both the object and the event have to know about each other > anyway. The decoupling allows to have N similar event sources and M similar event handlers. I find it frequently the case in control or monitoring systems, where many measurement systems produce data that can be processed or displayed by many clients. The decoupling is needed here so that data sources don't have to *statically* know their receivers. The "wiring" of the whole is driven by configuration, which is loaded at run-time. Georg already described the pattern in more detail, so I will not repeat it. > You and I must be thinking of different solutions, because I can't see any > way for that to work with a single handler registry type. And if you have > multiple such types, I don't see why you'd want to introduce the > complication: just deliver the events directly to the appropriate objects. And here's the catch: event sources don't know their receivers until the program is run[*], so they cannot deliver the events *directly* to appropropriate objects. Some level of indirection is needed here and access values provide the required capability with very little (negligible) overhead. I think that this is the detail that we have missed in the discussion up to now. [*] Actually, the wiring of the components is not necessarily done at run-time, but certainly later than the event producer is compiled. In other words, the event producer is compiled as a module and delivered to the client that adds his handlers. There is no way for the producer to deliver events *directly* to handlers, as handlers are not known a priori. And generics are too complex to handle that cleanly. > >Program safety is not compromised due to the fact that all reasonable > >use cases guarantee that handlers outlive the registries. > > Again, you are saying that no one that will program your system will make a > mistake. I'm saying that they will make a lot less mistakes when given a clear and readable API. The strategy seems to be working so far. > But I don't > think you are being realistic: for instance, I want to avoid allocating > objects when possible, because there are a lot less memory leaks Again: handlers are not allocated dynamically, they are typically static entities. Their associations with registries can be dynamic, though, but there is no danger in it. > Anyway, I think we have to agree to disagree on this one, because we don't > even have the same worldview. I think so. -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 12:59 ` Maciej Sobczak 2010-09-28 13:45 ` Dmitry A. Kazakov @ 2010-09-28 15:15 ` Ludovic Brenta 2010-09-28 22:04 ` Maciej Sobczak 1 sibling, 1 reply; 54+ messages in thread From: Ludovic Brenta @ 2010-09-28 15:15 UTC (permalink / raw) Maciej Sobczak wrote on comp.lang.ada: > So let's say that I already did the thinking and the most prevalent > use case for access types is callback registration. > > Think about AWS dispatchers for the closest analogy. > In fact, the AWS.Server.Start procedure has a version that takes the > callback by access. It is an access to function, but I need it to be > object-oriented. OK, functors then (I believe this word is C++ jargon). > Hiding the use of access values behind the scenes (by virtue of tagged > types being always passed by reference) would obstruct the code > without clear benefit. This is what AWS does in its other versions of > Start, but I don't like it. Do you mean you don't like this one: procedure Start (Web_Server : in out HTTP; Dispatcher : Dispatchers.Handler'Class; Config : AWS.Config.Object); Personally it is the one I prefer. Why do you not like it? > I want to express this: > > 1. Object is an interface type for the callback that will be > implemented by user. > 2. Object_Access is a type that will be used for declaring and passing > around callback references. I don't want everybody to define their own > types for what is a common functionality. I don't think this functionality is that common; ideally the only things you have to do with such references is record them in a registry and then dereference them to call the callbacks. You wouldn't "pass around" such references very often, I think. Especially if your callbacks are allocated at elaboration and not dynamically on the heap. > I totally agree that in Ada the pressure for using access values is > much smaller than in C++, but object registration (in a map, perhaps) > is not addressed by any other language feature. I would probably use an instance of Ada.Containers.Indefinite_Vectors or Indefinite_Doubly_Linked_Lists to hold an ordered list of class- wide callback objects. Is that the language feature you were looking for? Granted, such a container would duplicate the callback objects ("functors") in memory; using access values would avoid that. > Should I drop the Object_Access type altogether and mess with locally- > defined access types in other parts of the code? I would define the access type in the package that defines the registry of callback objects, e.g. limited with Objects; package Registry is type Callback is access all Objects.Object'Class; procedure Register (C : in Callback); procedure Call_All_Callbacks_In_Registration_Order; end Registry; -- Ludovic Brenta. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 15:15 ` Ludovic Brenta @ 2010-09-28 22:04 ` Maciej Sobczak 0 siblings, 0 replies; 54+ messages in thread From: Maciej Sobczak @ 2010-09-28 22:04 UTC (permalink / raw) On 28 Wrz, 17:15, Ludovic Brenta <ludo...@ludovic-brenta.org> wrote: > Do you mean you don't like this one: > > procedure Start > (Web_Server : in out HTTP; > Dispatcher : Dispatchers.Handler'Class; > Config : AWS.Config.Object); > > Personally it is the one I prefer. Why do you not like it? See my answer to Dmitry for code example. The above construct, by implicitly leaking references out of its execution, obstructs the scoping of object lifetime. > > 1. Object is an interface type for the callback that will be > > implemented by user. > > 2. Object_Access is a type that will be used for declaring and passing > > around callback references. I don't want everybody to define their own > > types for what is a common functionality. > > I don't think this functionality is that common; ideally the only > things you have to do with such references is record them in a > registry and then dereference them to call the callbacks. You wouldn't > "pass around" such references very often, I think. Especially if your > callbacks are allocated at elaboration and not dynamically on the > heap. That's right. Still, I need to express it somehow and I think that having an explicitly defined access type is a good practice. Unfortunately it does not seem to be compatible with the "limited with" feature. > I would probably use an instance of Ada.Containers.Indefinite_Vectors > or Indefinite_Doubly_Linked_Lists to hold an ordered list of class- > wide callback objects. Is that the language feature you were looking > for? Granted, such a container would duplicate the callback objects > ("functors") in memory; using access values would avoid that. Exactly, but copying is not the only problem. There are also dependencies. Note that my construct is not dependent on Ada.Containers and it allows the user to write programs without any use of dynamic memory. It is perfectly possible to create a callback object at the package level or local in a subprogram. > I would define the access type in the package that defines the > registry of callback objects, e.g. > > limited with Objects; > package Registry is > type Callback is access all Objects.Object'Class; > procedure Register (C : in Callback); > procedure Call_All_Callbacks_In_Registration_Order; > end Registry; Interesting. I will have to meditate on this possibility. -- Maciej Sobczak * http://www.inspirel.com ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 9:18 ` Ludovic Brenta 2010-09-28 12:59 ` Maciej Sobczak @ 2010-09-28 15:54 ` Robert A Duff 2010-09-30 7:27 ` Stephen Leake 2 siblings, 0 replies; 54+ messages in thread From: Robert A Duff @ 2010-09-28 15:54 UTC (permalink / raw) Ludovic Brenta <ludovic@ludovic-brenta.org> writes: > Taking this approach further, I can declare the access type in a > nested scope. When the access type goes out of scope, all objects > allocated through it are deallocated. This is a form of automatic > garbage collection. Those objects are finalized, but they are not deallocated in most implementations, unless you use Storage_Pool or Storage_Size. I think that's a good thing. - Bob ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 9:18 ` Ludovic Brenta 2010-09-28 12:59 ` Maciej Sobczak 2010-09-28 15:54 ` Robert A Duff @ 2010-09-30 7:27 ` Stephen Leake 2010-09-30 7:33 ` Ludovic Brenta 2010-09-30 16:03 ` Adam Beneschan 2 siblings, 2 replies; 54+ messages in thread From: Stephen Leake @ 2010-09-30 7:27 UTC (permalink / raw) Ludovic Brenta <ludovic@ludovic-brenta.org> writes: > I generally think twice or three times before declaring an access type > in the same package as the object type. In fact, I think twice before > declaring any access type at all :) To me, an access type makes the > package unclean. > > Since, in Ada, all objects of tagged types are passed by reference and > since Ada has class-wide types, you do not need any access type to > achieve pass-by-reference semantics or dynamic dispatching. This > leaves dynamic memory allocation as the only remaining justification > for access types. You left out accessibility checks, which are an important reason for named access types. -- -- Stephe ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-30 7:27 ` Stephen Leake @ 2010-09-30 7:33 ` Ludovic Brenta 2010-09-30 16:03 ` Adam Beneschan 1 sibling, 0 replies; 54+ messages in thread From: Ludovic Brenta @ 2010-09-30 7:33 UTC (permalink / raw) Stephen Leake wrote on comp.lang.ada: > Ludovic Brenta <ludo...@ludovic-brenta.org> writes: >> I generally think twice or three times before declaring an access type >> in the same package as the object type. In fact, I think twice before >> declaring any access type at all :) To me, an access type makes the >> package unclean. >> >> Since, in Ada, all objects of tagged types are passed by reference and >> since Ada has class-wide types, you do not need any access type to >> achieve pass-by-reference semantics or dynamic dispatching. This >> leaves dynamic memory allocation as the only remaining justification >> for access types. > > You left out accessibility checks, which are an important reason for > named access types. Yes, that's why I generally prefer named access types over anonymous access types, but you don't use access values just because of accessibility checks, do you? -- Ludovic Brenta. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-30 7:27 ` Stephen Leake 2010-09-30 7:33 ` Ludovic Brenta @ 2010-09-30 16:03 ` Adam Beneschan 2010-10-07 11:55 ` Stephen Leake 1 sibling, 1 reply; 54+ messages in thread From: Adam Beneschan @ 2010-09-30 16:03 UTC (permalink / raw) On Sep 30, 12:27 am, Stephen Leake <stephen_le...@stephe-leake.org> wrote: > Ludovic Brenta <ludo...@ludovic-brenta.org> writes: > > I generally think twice or three times before declaring an access type > > in the same package as the object type. In fact, I think twice before > > declaring any access type at all :) To me, an access type makes the > > package unclean. > > > Since, in Ada, all objects of tagged types are passed by reference and > > since Ada has class-wide types, you do not need any access type to > > achieve pass-by-reference semantics or dynamic dispatching. This > > leaves dynamic memory allocation as the only remaining justification > > for access types. > > You left out accessibility checks, which are an important reason for > named access types. Really? Most uses of anonymous access types still involve accessibility checks. The main case where they don't is access parameters; and in that case, you usually don't want accessibility checks unless you're going to store the parameter somewhere where it could live after the subprogram is completed, and doing so is going to involve an accessibility check at the point where it's stored, whether or not the type of the stored data is named or anonymous. This should all be crystal clear from reading 3.10.2. (I'm kidding about that last. Nothing about 3.10.2 is clear. The only reason I recommend reading 3.10.2 is because I own stock in Advil.) -- Adam ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-30 16:03 ` Adam Beneschan @ 2010-10-07 11:55 ` Stephen Leake 2010-10-07 18:27 ` Martin Krischik 0 siblings, 1 reply; 54+ messages in thread From: Stephen Leake @ 2010-10-07 11:55 UTC (permalink / raw) Adam Beneschan <adam@irvine.com> writes: > On Sep 30, 12:27 am, Stephen Leake <stephen_le...@stephe-leake.org> > wrote: >> Ludovic Brenta <ludo...@ludovic-brenta.org> writes: >> > I generally think twice or three times before declaring an access type >> > in the same package as the object type. In fact, I think twice before >> > declaring any access type at all :) To me, an access type makes the >> > package unclean. >> >> > Since, in Ada, all objects of tagged types are passed by reference and >> > since Ada has class-wide types, you do not need any access type to >> > achieve pass-by-reference semantics or dynamic dispatching. This >> > leaves dynamic memory allocation as the only remaining justification >> > for access types. >> >> You left out accessibility checks, which are an important reason for >> named access types. > > Really? Most uses of anonymous access types still involve > accessibility checks. Yes, and that's the point. > The main case where they don't is access parameters; and in that case, > you usually don't want accessibility checks unless you're going to > store the parameter somewhere where it could live after the subprogram > is completed, and doing so is going to involve an accessibility check > at the point where it's stored, whether or not the type of the stored > data is named or anonymous. Exactly. Suppose we have the following: type My_Type is record ... end record; type My_Access_Type is access all My_Type; procedure Foo_One (A : access My_Type); procedure Foo_Two (A : My_Access_Type); Inside Foo_Two, we know that the accessibility level of A is the same as the declaration of My_Access_Type, so you can save it to any value of that type; the accessibility check will pass (and should be removed by an optimizing compiler). Inside Foo_One, the accessibility level of A is likely the point of the call; it is not safe to save it, since you have no control over how the caller has declared the actual for A. So if you need to save copies of pointers, you need named access types, so you can know ahead of time that the accessibility check will not fail. In Ada 2012, it may be possible to use preconditions to enforce accessibility requirements; that would allow some more uses of anonymous access parameters. -- -- Stephe ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-07 11:55 ` Stephen Leake @ 2010-10-07 18:27 ` Martin Krischik 2010-10-07 21:30 ` Adam Beneschan 2010-10-08 0:07 ` Randy Brukardt 0 siblings, 2 replies; 54+ messages in thread From: Martin Krischik @ 2010-10-07 18:27 UTC (permalink / raw) Am 07.10.2010, 13:55 Uhr, schrieb Stephen Leake <stephen_leake@stephe-leake.org>: > Exactly. Suppose we have the following: > > type My_Type is record ... end record; > type My_Access_Type is access all My_Type; > > procedure Foo_One (A : access My_Type); > > procedure Foo_Two (A : My_Access_Type); > > > Inside Foo_Two, we know that the accessibility level of A is the same as > the declaration of My_Access_Type, Are you realy sure about that? My_Access_Type is an access *all* and could point to an aliased value on the stack. Martin -- Martin Krischik mailto://krischik@users.sourceforge.net https://sourceforge.net/users/krischik ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-07 18:27 ` Martin Krischik @ 2010-10-07 21:30 ` Adam Beneschan 2010-10-09 6:29 ` Martin Krischik 2010-10-08 0:07 ` Randy Brukardt 1 sibling, 1 reply; 54+ messages in thread From: Adam Beneschan @ 2010-10-07 21:30 UTC (permalink / raw) On Oct 7, 11:27 am, "Martin Krischik" <krisc...@users.sourceforge.net> wrote: > Am 07.10.2010, 13:55 Uhr, schrieb Stephen Leake > <stephen_le...@stephe-leake.org>: > > > Exactly. Suppose we have the following: > > > type My_Type is record ... end record; > > type My_Access_Type is access all My_Type; > > > procedure Foo_One (A : access My_Type); > > > procedure Foo_Two (A : My_Access_Type); > > > Inside Foo_Two, we know that the accessibility level of A is the same as > > the declaration of My_Access_Type, > > Are you realy sure about that? My_Access_Type is an access *all* and could > point to an aliased value on the stack. Only if you use 'Unchecked_Access (or some other trickery). Since we're talking about accessibility level checks, we have to assume that we aren't using 'Unchecked_Access, which would defeat the whole purpose of the checks and make the discussion pointless. -- Adam ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-07 21:30 ` Adam Beneschan @ 2010-10-09 6:29 ` Martin Krischik 2010-10-09 18:35 ` Robert A Duff 0 siblings, 1 reply; 54+ messages in thread From: Martin Krischik @ 2010-10-09 6:29 UTC (permalink / raw) Am 07.10.2010, 23:30 Uhr, schrieb Adam Beneschan <adam@irvine.com>: > Since > we're talking about accessibility level checks, we have to assume that > we aren't using 'Unchecked_Access, I thought Stephen were talking static vs. dynamic checks. He was arguing that anonymous access would need an dynamic check unlike a named access. But then he used an access all in his example - which AFAIK might needs a dynamic check as well. I would argue that if you use an access all you can just as well use an anonymous access. And I feel that many Ada programmes these day type »access all« out of habit without thinking of the implications. Regards Martin -- Martin Krischik mailto://krischik@users.sourceforge.net https://sourceforge.net/users/krischik ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-09 6:29 ` Martin Krischik @ 2010-10-09 18:35 ` Robert A Duff 0 siblings, 0 replies; 54+ messages in thread From: Robert A Duff @ 2010-10-09 18:35 UTC (permalink / raw) "Martin Krischik" <krischik@users.sourceforge.net> writes: > But then he used an access all in his example - which AFAIK might needs > a dynamic check as well. All accessibility checks on named access types with "all" are done at compile time. According to the RM, they are done at run time in some generic-related cases, but most compilers (the ones that do macro-expanded generics) will check those at compile time, too (and give a warning). Only anonymous access types have run-time accessibility checking (which I think was a mistake, by the way). But if you use 'Unchecked_Access, you will defeat both the compile-time and the run-time checks. - Bob ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-07 18:27 ` Martin Krischik 2010-10-07 21:30 ` Adam Beneschan @ 2010-10-08 0:07 ` Randy Brukardt 2010-10-09 6:21 ` Martin Krischik 1 sibling, 1 reply; 54+ messages in thread From: Randy Brukardt @ 2010-10-08 0:07 UTC (permalink / raw) "Martin Krischik" <krischik@users.sourceforge.net> wrote in message news:op.vj7xjle3z25lew@macpro-eth1.krischik.com... ... >> Inside Foo_Two, we know that the accessibility level of A is the same as >> the declaration of My_Access_Type, > > Are you realy sure about that? My_Access_Type is an access *all* and could > point to an aliased value on the stack. He is. If you try to store an access to an aliased value on the stack into My_Access_Type, you'll get a compile-time error that the accessibility fails. (Or, in a few obscure cases, Program_Error will be raised.) The rules that guarentee this are a constant source of headaches for the ARG and implementers, but Ada users don't need to worry about that. Randy. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-10-08 0:07 ` Randy Brukardt @ 2010-10-09 6:21 ` Martin Krischik 0 siblings, 0 replies; 54+ messages in thread From: Martin Krischik @ 2010-10-09 6:21 UTC (permalink / raw) Am 08.10.2010, 02:07 Uhr, schrieb Randy Brukardt <randy@rrsoftware.com>: > "Martin Krischik" <krischik@users.sourceforge.net> wrote in message > news:op.vj7xjle3z25lew@macpro-eth1.krischik.com... > ... >>> Inside Foo_Two, we know that the accessibility level of A is the same >>> as >>> the declaration of My_Access_Type, >> >> Are you realy sure about that? My_Access_Type is an access *all* and >> could >> point to an aliased value on the stack. > > He is. If you try to store an access to an aliased value on the stack > into > My_Access_Type, you'll get a compile-time error that the accessibility > fails. (Or, in a few obscure cases, Program_Error will be raised.) The > rules > that guarentee this are a constant source of headaches for the ARG and > implementers, but Ada users don't need to worry about that. But if you mention Program_Error then the accessibility can not be checked at compile time in all circumstances. In which case an access all is not at all better then an anonymous access. Only a plain access will guarantee that accessibility can be checked statically in all circumstances. Regards. Martin -- Martin Krischik mailto://krischik@users.sourceforge.net https://sourceforge.net/users/krischik ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 7:37 Limited use for limited with? Maciej Sobczak 2010-09-28 9:04 ` Alex R. Mosteo 2010-09-28 9:18 ` Ludovic Brenta @ 2010-09-28 9:32 ` Vadim Godunko 2010-09-28 11:34 ` stefan-lucks 2010-09-28 16:55 ` Adam Beneschan 4 siblings, 0 replies; 54+ messages in thread From: Vadim Godunko @ 2010-09-28 9:32 UTC (permalink / raw) On Sep 28, 11:37 am, Maciej Sobczak <see.my.homep...@gmail.com> wrote: > > In such cases, where the design intent is pretty clear (pass around > references to Objects) I find that limited with does not really bring > the functionality that it is supposed to provide. > It is how limited with is defined. Ada2012 should relax rules to make limited with more useful. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 7:37 Limited use for limited with? Maciej Sobczak ` (2 preceding siblings ...) 2010-09-28 9:32 ` Vadim Godunko @ 2010-09-28 11:34 ` stefan-lucks 2010-09-28 13:15 ` stefan-lucks 2010-09-28 16:55 ` Adam Beneschan 4 siblings, 1 reply; 54+ messages in thread From: stefan-lucks @ 2010-09-28 11:34 UTC (permalink / raw) On Tue, 28 Sep 2010, Maciej Sobczak wrote: > package Objects is > > type Object is interface; > type Object_Access is access all Object'Class; > > procedure Do_Something (X : in out Object) is abstract; > > end Objects; [...] Compiler doesn't like Objects.Object_Access: > procedure Use_Object (X : Objects.Object_Access); [...] Anymous access works, but is ugly: > procedure Use_Object (X : access Objects.Object'Class); [...] > Any thoughts on this? Not an answer to your question -- but what requirements force you to use an access type at all? To me, the most natural would be to write "X: in out Objects.Object": limited with Objects; package Use_Objects is procedure Use_1(X: access Objects.Object); -- compiles fine procedure Use_2(Y: in out Objects.Object); -- compiles fine procedure Use_3(Z: Objects.Object_Access); -- invalid use of incomplete type end Use_Objects; -- ------ 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] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 11:34 ` stefan-lucks @ 2010-09-28 13:15 ` stefan-lucks 0 siblings, 0 replies; 54+ messages in thread From: stefan-lucks @ 2010-09-28 13:15 UTC (permalink / raw) On Tue, 28 Sep 2010, stefan-lucks@see-the.signature wrote: > procedure Use_2(Y: in out Objects.Object); -- compiles fine Sorry, what I wanted to write is procedure Use_2b(Y: in out Objects.Object'Class); this also compiles fine. -- ------ 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] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 7:37 Limited use for limited with? Maciej Sobczak ` (3 preceding siblings ...) 2010-09-28 11:34 ` stefan-lucks @ 2010-09-28 16:55 ` Adam Beneschan 2010-09-28 17:31 ` Robert A Duff 4 siblings, 1 reply; 54+ messages in thread From: Adam Beneschan @ 2010-09-28 16:55 UTC (permalink / raw) On Sep 28, 12:37 am, Maciej Sobczak <see.my.homep...@gmail.com> wrote: > I have found a problem with the intended application of limited with. > > Consider a package specifying some object-oriented construct: > > package Objects is > > type Object is interface; > type Object_Access is access all Object'Class; > > procedure Do_Something (X : in out Object) is abstract; > > end Objects; > > In such cases I routinely define the XYZ_Access type and later use it > wherever pointers to class-wide XYZ are needed. > > Now consider a package that uses the above in a limited way (pun > intended), where only pointers to class-wide type are needed: > > with Objects; > package Object_Users is > > procedure Use_Object (X : Objects.Object_Access); > > end Object_Users; > > The problem is that is some cases it would be more convenient (or just > more self-documenting from the design perspective) to do limited with > instead, but unfortunately this makes Object_Access unavailable. It is > OK to use anonymous access type, at least in some cases like here: > > procedure Use_Object (X : access Objects.Object'Class); > > but I find that uncomfortable - after all, the proper access type is > already defined for exactly this purpose. > > In such cases, where the design intent is pretty clear (pass around > references to Objects) I find that limited with does not really bring > the functionality that it is supposed to provide. I'd recommend that you look at the example in the AI (AI95-217-6): http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ais/ai-50217.txt and in particular the !example section. The example there shows two packages that depend on each other, using "limited with", but neither package declares an access type that is an access to a type in the same package. Instead, the Employees package declares a named access type whose designated type is in the Departments package; and the anonymous access types in both packages all refer to types in the other package. So the code that you find "uncomfortable" really *was* the way it was supposed to work, I think. I can understand that this solution may not have given you a way to express things the way you might have preferred. But doing things "perfectly" was, I believe, unfeasible. If you look at the ! discussion part of this AI, you'll realize how much effort the ARG put into trying to come up with a solution that would provide a reasonable way to solve a real problem while doing so in a way that could be implemented feasibly by compilers, and in a way so that precise language rules could be written and that problems that could arise from using different combinations of features were addressed. (Note: I am not an ARG member and was not part of this effort.) And then you have to multiply all that effort by seven, because "limited with" was just one of *seven* different proposed solutions to the mutual dependence problem, and all of them received extensive consideration. So it really rubs me the wrong way to see comments---not from you, but from someone else---such as "Ada2012 should relax rules to make limited with more useful." Perhaps it's because I'm reaching the Grumpy-Old-Fart-In-Training phase of my life. But given the amount of work the ARG put into this over a number of years, can't we at least assume that they did the best they could to make it as useful as they could, and that whatever rules are there that you don't like are there for a very good reason? Jeesh, now I sound like I'm talking to my teenager. Definitely headed for G.O.F. at full speed. I'm certainly not opposed to people wanting changes in the language (although it's too late for Ada 2012); but in this case, I'd recommend that if anyone wants to see this change, they should read the discussions in all the AI95-217 options (AI95-217-1 through 7) to make sure they understand all the issues, and then propose something specific. OK, I'm done ranting. For now. -- Adam ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 16:55 ` Adam Beneschan @ 2010-09-28 17:31 ` Robert A Duff 2010-09-28 19:24 ` Adam Beneschan 0 siblings, 1 reply; 54+ messages in thread From: Robert A Duff @ 2010-09-28 17:31 UTC (permalink / raw) Adam Beneschan <adam@irvine.com> writes: > So it really rubs me the wrong way to see comments---not from you, but > from someone else---such as "Ada2012 should relax rules to make > limited with more useful." Well, ahem, the ARG actually IS considering some changes to make 'limited with' more useful. Sorry, I don't remember the AI number(s). >...Perhaps it's because I'm reaching the > Grumpy-Old-Fart-In-Training phase of my life. But given the amount of > work the ARG put into this over a number of years, can't we at least > assume that they did the best they could to make it as useful as they > could, and that whatever rules are there that you don't like are there > for a very good reason? As a member of ARG, I'd like to point out that "lots of hard work" does not always produce good solutions. Maybe we all were confused, and a fresh look would produce something better. As always, the primary difficulty is remaining compatible with early versions of the language. And the second one is making it implementable by existing compilers, which were of course designed without knowing about whatever new rules the ARG is cooking up. Without those two concerns, fixing the "cyclic imports" problem is easy. >...Jeesh, now I sound like I'm talking to my > teenager. Definitely headed for G.O.F. at full speed. ;-) ;-) > I'm certainly not opposed to people wanting changes in the language > (although it's too late for Ada 2012); but in this case, I'd recommend > that if anyone wants to see this change, they should read the > discussions in all the AI95-217 options (AI95-217-1 through 7) to make > sure they understand all the issues, and then propose something > specific. Sure, it's a good idea to look at previous work in the area. Don't take it as The Ultimate Truth, though. - Bob ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 17:31 ` Robert A Duff @ 2010-09-28 19:24 ` Adam Beneschan 2010-09-28 20:32 ` Vadim Godunko 0 siblings, 1 reply; 54+ messages in thread From: Adam Beneschan @ 2010-09-28 19:24 UTC (permalink / raw) On Sep 28, 10:31 am, Robert A Duff <bobd...@shell01.TheWorld.com> wrote: > Adam Beneschan <a...@irvine.com> writes: > > So it really rubs me the wrong way to see comments---not from you, but > > from someone else---such as "Ada2012 should relax rules to make > > limited with more useful." > > Well, ahem, the ARG actually IS considering some changes to > make 'limited with' more useful. Sorry, I don't remember > the AI number(s). Well, I see some Binding Interpretations related to "limited with". Most of them seem to be adding missing rules. I didn't see any AI's that would relax any rules, and nothing that would make any changes to the design or have any effect on the feature's usefulness. Unless, of course, there are some AI's that have been assigned but haven't yet made it onto the website. (There aren't any AI12's yet, I hope??) I suppose my rant was somewhat over the top. Sure, ARG makes mistakes, and it's entirely possible that they started off in a wrong direction and kept going that way. Still, there just seems to be something wrong when one party does a lot of work trying to figure out what all the issues are and come up with the best solution they can, and someone else who doesn't understand all the issues says too facilely, "There's a flaw. You should change it." (I'm not talking about minor and easily fixable flaws like wording errors, some of which I reported, but "flaws" that necessarily arise because the designers had to make some compromises and trade-offs.) I'm not sure why I'm so sensitive to this, particularly since I didn't do any of the work on this feature, but I am. -- Adam ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 19:24 ` Adam Beneschan @ 2010-09-28 20:32 ` Vadim Godunko 2010-09-28 21:32 ` Adam Beneschan 0 siblings, 1 reply; 54+ messages in thread From: Vadim Godunko @ 2010-09-28 20:32 UTC (permalink / raw) On Sep 28, 11:24 pm, Adam Beneschan <a...@irvine.com> wrote: > On Sep 28, 10:31 am, Robert A Duff <bobd...@shell01.TheWorld.com> > wrote: > > > Adam Beneschan <a...@irvine.com> writes: > > > So it really rubs me the wrong way to see comments---not from you, but > > > from someone else---such as "Ada2012 should relax rules to make > > > limited with more useful." > May be "should" is wrong word, I am sorry for confusion, but this extension is in Amendment 2012 right now, see http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai05s/ai05-0151-1.txt which allows to use incomplete type as type of parameter of subprogram, so it covers original issue exactly. ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: Limited use for limited with? 2010-09-28 20:32 ` Vadim Godunko @ 2010-09-28 21:32 ` Adam Beneschan 0 siblings, 0 replies; 54+ messages in thread From: Adam Beneschan @ 2010-09-28 21:32 UTC (permalink / raw) On Sep 28, 1:32 pm, Vadim Godunko <vgodu...@gmail.com> wrote: > May be "should" is wrong word, I am sorry for confusion, but this > extension is in Amendment 2012 right now, see > > http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai05s/ai05-0151-1.txt > > which allows to use incomplete type as type of parameter of > subprogram, so it covers original issue exactly. My apologies. I guess I misunderstood you. -- Adam ^ permalink raw reply [flat|nested] 54+ messages in thread
end of thread, other threads:[~2010-10-15 0:59 UTC | newest] Thread overview: 54+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2010-09-28 7:37 Limited use for limited with? Maciej Sobczak 2010-09-28 9:04 ` Alex R. Mosteo 2010-09-30 7:24 ` Stephen Leake 2010-09-30 9:21 ` Alex R. Mosteo 2010-09-28 9:18 ` Ludovic Brenta 2010-09-28 12:59 ` Maciej Sobczak 2010-09-28 13:45 ` Dmitry A. Kazakov 2010-09-28 21:57 ` Maciej Sobczak 2010-09-29 6:03 ` Ludovic Brenta 2010-09-29 8:25 ` Maciej Sobczak 2010-09-29 7:51 ` Dmitry A. Kazakov 2010-09-29 8:38 ` Maciej Sobczak 2010-09-29 9:16 ` Dmitry A. Kazakov 2010-09-29 12:22 ` Maciej Sobczak 2010-09-29 13:41 ` Dmitry A. Kazakov 2010-09-29 15:07 ` Georg Bauhaus 2010-09-29 19:22 ` Dmitry A. Kazakov 2010-09-29 20:51 ` Maciej Sobczak 2010-09-29 21:18 ` Dmitry A. Kazakov 2010-10-05 7:35 ` Randy Brukardt 2010-10-08 8:05 ` Maciej Sobczak 2010-10-09 6:29 ` Randy Brukardt 2010-10-05 7:25 ` Randy Brukardt 2010-10-08 8:23 ` Maciej Sobczak 2010-10-09 6:13 ` Randy Brukardt 2010-10-10 14:13 ` Maciej Sobczak 2010-10-11 6:23 ` Randy Brukardt 2010-10-12 19:29 ` Maciej Sobczak 2010-10-12 20:19 ` Dmitry A. Kazakov 2010-10-13 2:09 ` Randy Brukardt 2010-10-13 8:44 ` Georg Bauhaus 2010-10-15 0:59 ` Randy Brukardt 2010-10-13 9:43 ` Maciej Sobczak 2010-09-28 15:15 ` Ludovic Brenta 2010-09-28 22:04 ` Maciej Sobczak 2010-09-28 15:54 ` Robert A Duff 2010-09-30 7:27 ` Stephen Leake 2010-09-30 7:33 ` Ludovic Brenta 2010-09-30 16:03 ` Adam Beneschan 2010-10-07 11:55 ` Stephen Leake 2010-10-07 18:27 ` Martin Krischik 2010-10-07 21:30 ` Adam Beneschan 2010-10-09 6:29 ` Martin Krischik 2010-10-09 18:35 ` Robert A Duff 2010-10-08 0:07 ` Randy Brukardt 2010-10-09 6:21 ` Martin Krischik 2010-09-28 9:32 ` Vadim Godunko 2010-09-28 11:34 ` stefan-lucks 2010-09-28 13:15 ` stefan-lucks 2010-09-28 16:55 ` Adam Beneschan 2010-09-28 17:31 ` Robert A Duff 2010-09-28 19:24 ` Adam Beneschan 2010-09-28 20:32 ` Vadim Godunko 2010-09-28 21:32 ` Adam Beneschan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox