From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,c08a7609345f4e5 X-Google-NewGroupId: yes X-Google-Attributes: gida07f3367d7,domainid0,public,usenet X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!news3.google.com!news4.google.com!proxad.net!feeder1-2.proxad.net!news.in2p3.fr!in2p3.fr!news.ecp.fr!news.jacob-sparre.dk!pnx.dk!not-for-mail From: "Randy Brukardt" Newsgroups: comp.lang.ada Subject: Re: Limited use for limited with? Date: Mon, 11 Oct 2010 01:23:13 -0500 Organization: Jacob Sparre Andersen Message-ID: References: <853314bc-0f79-435f-86a5-d7bcdd610731@c10g2000yqh.googlegroups.com> <36e886fa-b272-461f-bf86-a6b18366b64f@i5g2000yqe.googlegroups.com> <1eug9v5h5mf8d$.ud00hrz48lyr.dlg@40tude.net> <67044906-dacc-4526-b3f6-27e5323ab8fc@n3g2000yqb.googlegroups.com> <12chb4kbqt9ln$.zumsv1z9hqvk$.dlg@40tude.net> <292dd0bd-1fc4-4715-bb70-7655d0dc04eb@j24g2000yqa.googlegroups.com> <09c36bd6-edfa-42bf-8f33-e91b0a9b0737@26g2000yqv.googlegroups.com> NNTP-Posting-Host: static-69-95-181-76.mad.choiceone.net X-Trace: munin.nbi.dk 1286778196 6415 69.95.181.76 (11 Oct 2010 06:23:16 GMT) X-Complaints-To: news@jacob-sparre.dk NNTP-Posting-Date: Mon, 11 Oct 2010 06:23:16 +0000 (UTC) X-Priority: 3 X-MSMail-Priority: Normal X-Newsreader: Microsoft Outlook Express 6.00.2900.5931 X-RFC2646: Format=Flowed; Original X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.5931 Xref: g2news1.google.com comp.lang.ada:14478 Date: 2010-10-11T01:23:13-05:00 List-Id: "Maciej Sobczak" wrote in message news:c76f3559-7c89-4536-b92f-951da5a44170@m1g2000vbh.googlegroups.com... On 9 Paz, 08:13, "Randy Brukardt" 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.