comp.lang.ada
 help / color / mirror / Atom feed
From: Stephen Leake <stephen_leake@stephe-leake.org>
Subject: Re: Access types as parameters
Date: Tue, 11 Aug 2009 22:22:27 -0400
Date: 2009-08-11T22:22:27-04:00	[thread overview]
Message-ID: <uprb1epp8.fsf@stephe-leake.org> (raw)
In-Reply-To: h5svl3$k4p$1@munin.nbi.dk

"Randy Brukardt" <randy@rrsoftware.com> writes:

> I know this is old, but it is so misguided that it needs a 
> reply...especially as it is from someone who usually knowledgable on this 
> forum. - RLB

Thanks :).

> "Stephen Leake" <stephen_leake@stephe-leake.org> wrote in message 
> news:u63dkb12m.fsf@stephe-leake.org...
> ...
>> Most GTK subprograms do need access values; the same is true of any
>> system that deals with derived types at multiple levels. The only
>> library-level object that can hold any type in the hierarchy is a
>> class-wide pointer. Lists of objects must hold pointers, etc.
>
> All of the indefinite containers can hold class-wide objects. No (explicit) 
> pointers are needed.

You mean the language defined containers in Ada.Containers.*. Yes, the
pointers are not explicit, but they are needed in the bodies. That's
how the language works.

Gtk has its own container hierarchy. So it needs pointers.

> One of the goals for the next revision of Ada is that the vast majority of 
> reasonable data structures can be reasonably written with the 
> Ada.Containers. 

I agree this is a reasonable approach for many applications, but
trying to use Ada.Containers to implement the Gtk container hierarchy
would be a nightmare.

> In particular, the multiway tree container can be used to model
> almost all forms for tree structure (and many graphs as well). That
> should mean that the need for explicit access types will be greatly
> reduced when class-wide programming.

This is true, but only if starting from scratch in a new system. And
it's not always the best solution anyway; each application has
particular needs. 

>> A reason to use 'access Record_Type' instead of 'in Access_Type' is to
>> avoid explicit type conversions.
>
> This is a terrible reason: explicit conversions show when you are converting 
> accesses from one "domain of use" to another. 

Not in this case. The domain is always "some Gtk object". Or "some
OpenToken object".

> (I'm thinking of each named access type as a "domain-of-use". It
> only makes sense to have multiple named access types for a
> particular designated type if there are different "domains-of-use"
> [such as different storage pools or disjoint data structures].) 

GtkAda and OpenToken don't have different named access types for one
type; they have different named access types for types in a hierarchy.

That allows the user to have collections of "any Gtk object", and
collections of "Gtk windows" or "my dialog objects". All of these
occur in a real Gtk application, and all need their own access type.

> The type conversions make it clear when you are converting from one
> "domain-of-use" to another, something that should be rare.

"converting" from one level in the type hierarchy to another is not
rare. That's part of the point of a type hierarchy. Most of the time,
it's just a view conversion, not a "real" conversion.

To be convincing, I'd have to post an example from Gtk or OpenToken
written both ways; maybe later I'll find time for that.

> Anonymous access types hide that, and make it essentially impossible to 
> determine what the storage pool of an access is -- meaning that most values 
> that pass through anonymous access cannot be deallocated even if converted 
> to a named type (because there is no way to know if it is the *right* named 
> type). [Technically, they can be deallocated, but the program is erroneous 
> and may do anything.]

That's true. But it just forces better application design.

Deallocating should be done by the same chunk of code that allocates,
so this shouldn't be an issue.

That is how OpenToken works internally.

> Finally, the implicit conversions are only allow to the anonymous type. To 
> get back to a named type, you have to go back to explicit conversions, so 
> you don't really save anything. In your example:
>
>> Given:
>>
>> package Widget is
>>    type Gtk_Widget_Record is ...;
>>    type Gtk_Widget is access all Gtk_Widget_Record'class;
>>
>>    procedure Show (Widget : in Gtk_Widget);
>> end Widget;
>>
>> package Window is
>>    type Gtk_Window_Record is new Gtk_Widget_Record with ...;
>>    type Gtk_Window is access all Gtk_Window_Record'class;
>> end Window;
>>
>>    Window : Gtk_Window := ...;
>>
>> then this does not work:
>>
>>   Widget.Show (Window);
>>
>> but this does:
>>
>>   Widget.Show (Gtk_Widget (Window));
>>
>> This is just annoying!
>>
>> However, if Show is declared:
>>
>> procedure Show (Widget : access constant Gtk_Widget_Record'class);
>>
>> Then Show matches any access type in the class hierarchy;
>>
>>    Widget.Show (Window);
>>
>> works.
>
> Of course, Show in this case surely should be dispatching (even if the 
> interface itself doesn't use that property) so that extensions can extend 
> the operation, so the declaration should be:
>     procedure Show (Widget : in Gtk_Widget);

You meant Gtk_Widget_Record here.

> and the call should be
>     Show (Window.all);

You can redesign the requirements (decide that show "should be
dispatching"), but that's just avoiding the issue. Surely you can
accept that some subprograms should not be dispatching!

> Probably the real reason the designers of GTK made this an anonymous access 
> parameter is to get that dispatching property. 

no, because the type is 'class. Some other subprograms are
dispatching; that's not the critical issue here.

> It surely has little to do with implicit conversions.

This requires mindreading. In _my_ mind, implicit conversions are a
major reason for this design. 

It would be interesting to hear from the GtkAda designers. I suspect
they would say "that was the only way we could get it to work in Ada
95" or something similar.

> [The fact that you can dispatch on access types in Ada, but only with this 
> bizarre syntax is a highly misguided feature of the language IMHO. It was 
> added only because of the IN OUT parameter restrictions and the insistence 
> of some that that not being able to copy lousy C++ and Java designs directly 
> (rather than better Ada designs not using explicit access types) would harm 
> the language's use in some way.]

It would be interesting to go back and design Ada to accomodate these
issues in a different way.

But I'm trying to work with the language we have.

> Anyway, the real issue that these implicit conversions don't work in many 
> cases that you would expect them to. For instance, inside of Show, if you 
> need to treat the parameter as a value of GTK_Widget:
>
>     Widget_List : GTK_Widget;
>
>     Widget_List := Widget; -- Illegal. Anon to Named conversions not 
> implicit!
>     Widget_List := GTK_Widget(Widget);

Which is as it should be, for accessiblity reasons. If you do this,
you have a potential accessibility error, which is bad. So it
certainly should not be implicit.

> Indeed, the only reason (in Ada 95) why the anonymous access conversions are 
> implicit is the obvious problem that they don't have a name! Else I think 
> they would have in fact required a conversion.
>
> It's possible that we'd going to fix this conversion problem in the next 
> version of Ada, but we will have to do more study to ensure that we don't 
> introduce a show-stopping incompatibility.

Or introduce lots of "unnecessary" explicit conversions. I hope that
discussion will be on the ada-comment list, where I can monitor and/or
participate.

>> I've been struggling with this issue in OpenToken, and settled on
>> using 'access Record_Type' as the best compromise. Now I just need to add
>> 'constant' in all the right places; that wasn't allowed in Ada 95,
>> when OpenToken was first written.
>
> That may have made sense in Ada 95, but now it is a terrible choice, as it 
> forces the user to do all memory management on the objects. 

Yes. But in this case, the intent is to declare a few objects on the
heap, and leave them there forever. Some of them can be on the stack
in some simple situations. 

> If you force passing access-to-objects, you cannot store the objects
> in a container and allow the container to do the storage management.

yes, but it's not a problem in practice. For other applications, it
might be more of a problem.

> (It also requires adding a huge amount of noise to stack-allocated
> objects -- the need to declare everything "aliased" and 'access on
> every call -- and the use of stack-allocated objects should always
> be a primary goal. Why OpenToken cannot be designed to usually use
> stack-allocated objects escapes me...)

Here's one example. We have a grammar:

L -> E  EOF
E -> T {+ T}
T -> F {* F}
F -> ( E )  
F -> integer

This has recursion in one place; E refers to itself via T and F.

Recursion in a data structure requires pointers. 

More complex grammars have more recursion, and require more pointers. 

OpenToken allows for the most complex grammars, so it assumes pointers
for all grammars.

If you use Ada.Containers, you can replace the pointers with
Cursor values, but that just complicates things; at this level, Cursor
values are just pointers with a different (and more cumbersome) syntax.

You will probably say "Cursor values are _safe_, pointers are not".
That's true. I just don't find the extra safety to be worth the
hassle.

But to be fair, I have not tried to implement something as complex as
OpenToken using only Ada.Containers. So maybe the hassle isn't as bad
as I think it will be; what I know how to do is _always_ easier than
something I haven't tried yet :).

I have used Ada.Containers in a small project. It was a mind-bending
experience, but the result is elegant.

>> In some cases, I have both class-wide and primitive operations:
>>
>>   type Instance is abstract tagged private;
>>   subtype Class is Instance'Class;
>>   type Handle is access all Class;
>>
>>   function Name (Token : in Instance) return String is abstract;
>>
>>   --  Dispatching calls to Name
>>   function Name_Dispatch (Token : in Class) return String;
>>   function Name_Dispatch (Token : access constant Instance'Class) return 
>> String;
>>
>> That gives the best of all cases, at the expense of writting more
>> code.
>
> Let me get this straight. You declare an extra routine with a much longer 
> name and a complex (and expensive at runtime call-sequence) in order that 
> you can avoid writing ".all" periodically??

That's not the only, or even the main, reason. There were many places
where I wrote "Name (foo)" that turned out to be not dispatching, when
I wanted it to be dispatching. That was confusing, and difficult to
track down. Hence the longer name.

I have Emacs programmed to put in (or take out) the .all where
necessary (in response to compiler errors), so that's not a big deal
for me.

> That is, if you have
>     type Any_Instance_Class is access all Instance'Class;
>     An_Instance : Any_Instance_Class;
>
> (and appropriate visibility),
>
> you would rather write:
>     Name_Dispatch (An_Instance);
>
> rather than
>     Name (An_Instance.all);

Yes, because then I can be _sure_ it is dispatching. In most cases,
the immediate argument is _not_ classwide, but is in fact a parent
type view of a descendant type object, so I want the call to dispatch,
to get the full descendant type object name.

Saving the .all is gravy.

> Moreover, if you are using Ada 2005, you probably should write:
>    An_Instance.Name
>
> And you don't even need the .all anymore! (Nor do you need "appropriate 
> visibility"!)

That feels like going over to the dark side of C++, so I've been
avoiding it. But I admit it is attractive sometimes. But it doesn't
address the issue of forcing dispatching.

>>> And if you're going to store the access parameter in a global
>>> structure, you'll get a runtime error if you try to store a dangling
>>> reference, so it's best to use a global access type so that
>>> accessibility level errors are caught at compile time.
>>
>> This is a problem; people keep stumbling across it. Still, you get
>> used to it after a while, and stop trying to pass 'access of a local
>> variable.
>
> One would hope that designers of Ada interfaces would talk more care for the 
> users than to say "they get used to it after a while". That sort of ticking 
> time bomb (that easily can be missed in unit testing!) is very adverse to 
> the goals of safety espoused by many in this group (including me!). It has 
> the potential of making Ada into just another programming language.

Yes. 

I did say using "access" was a compromise. I can't go back and
redesign Ada. Nor do I want to reimplement OpenToken to take full
advantage of Ada 2005 features.

I agree that new projects, that are not bound by Ada 95 heritage,
should strive harder to use Ada.Containers and avoid access
parameters. 

-- 
-- Stephe



  reply	other threads:[~2009-08-12  2:22 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-07-17  8:39 Access types as parameters Rick
2009-07-17 15:03 ` Adam Beneschan
2009-07-17 16:28   ` Hibou57 (Yannick Duchêne)
2009-07-17 23:25     ` rickduley
2009-07-18  1:03       ` Randy Brukardt
2009-07-19 22:57         ` rickduley
2009-07-20  0:10           ` John B. Matthews
2009-07-20  8:13           ` Dmitry A. Kazakov
2009-07-21  0:34           ` Randy Brukardt
2009-07-21 14:34           ` Adam Beneschan
2009-07-23  2:11             ` Stephen Leake
2009-08-11 23:41               ` Randy Brukardt
2009-08-12  2:22                 ` Stephen Leake [this message]
2009-08-13  1:06                   ` Randy Brukardt
2009-08-13  8:34                     ` Niklas Holsti
2009-08-13  9:15                       ` Dmitry A. Kazakov
2009-08-13 20:13                         ` Niklas Holsti
2009-08-13 21:07                           ` Dmitry A. Kazakov
2009-08-14  9:27                             ` Niklas Holsti
2009-08-14 10:36                               ` Dmitry A. Kazakov
2009-08-14 16:03                                 ` Niklas Holsti
2009-08-15  9:47                                   ` Dmitry A. Kazakov
2009-08-15 19:19                                     ` Niklas Holsti
2009-08-16  8:32                                       ` Dmitry A. Kazakov
2009-08-16  9:52                                         ` Niklas Holsti
2009-08-16 12:38                                           ` Dmitry A. Kazakov
2009-08-16 13:21                                             ` Niklas Holsti
2009-08-16 17:58                                               ` Dmitry A. Kazakov
2009-08-14  4:07                       ` Randy Brukardt
2009-08-14 10:22                         ` Niklas Holsti
2009-08-18 12:22                     ` Stephen Leake
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox