[comp.lang.ada] procedural variables

) (02/12/91)

	Since Ada does not provide for procedural variables, how
do you/your associates handle the need for having a procedure or
a task perform different duties depending on run-time circumstances?

	One scenario would be to pass an enumeration value to the
procedure and then to use "if-then-else-end if" or "case" statements to
determine which procedure should be run. This approach will work;
however, it seems inelegant.


	Does anyone one out there have a "nifty" solution or idea
that they would be willing to share?

Thanks,

JOSE D.

howard@esosun.css.gov (Howard Turner) (02/12/91)

	jduarte@ternes.ICS.UCI.EDU (J o s e D u a r t e ! ! !) writes:

		Since Ada does not provide for procedural variables, how
	do you/your associates handle the need for having a procedure or
	a task perform different duties depending on run-time circumstances?

Two methods come to mind immediately.  Since I am involved developing
an Ada/Xt and Ada/widget_set  I've had to solve this "problem".

The two methods are 

1) use tasks.  The prototype code for this is included at the end 
   of this posting.  This code has been compiled linked and run
   under several different hardware/compiler combinations and has
   always run correctly.

2) Call it from C.




howard
howard@esosun.css.gov

--------------------prototype code starts here

package subprogram_pointer is


  type func_ptr is private;
  procedure execute (the_obj : in func_ptr);

  generic
    type local_ptr is private;
    proc_id : in out local_ptr;
    with procedure user_specified;

  package proc_ptr is
  end proc_ptr;

private
  type func;
  type func_ptr is access func;

end subprogram_pointer;

--**************************
with text_io;
with unchecked_conversion;
package body Subprogram_Pointer is

    task type func is
       entry Handle;
    end func;

    task body func is
    begin
       loop
         select
           accept Handle do
             text_io.put_line ("in the wrong task");
           end Handle;
         or
           terminate;
         end select;
       end loop;
    end func;


    procedure execute (the_obj : in func_ptr) is

    begin
      the_obj.Handle;
    exception
      when others =>
         text_io.put_line("exception raised trying to execute a func_ptr");
         raise;
    end execute;



  package body proc_ptr is
    task type Local is
       entry Handle;
    end Local;

    type local_task_ptr is access Local;
    
    local_task : local_task_ptr;
 
    function to_func_ptr is new unchecked_conversion (
                 Source => local_task_ptr,
                 Target => Local_Ptr);

    task body Local is
    begin
       loop
         select
           accept Handle do
             text_io.put_line("in the Local task");
             User_Specified;
           end Handle;
         or
           terminate;
         end select;
       end loop;
    end Local;

  begin
    local_task := new Local;
    proc_id := to_func_ptr (local_task);

    exception
      when others =>
         text_io.put_line ("error starting the function pointer task");
         raise;

  end proc_ptr;

end Subprogram_Pointer;

--***************************

with text_io;
procedure the_callback is
begin
  text_io.put_line("it worked");
end the_callback;

--**************************

with the_callback;
with text_io;
with subprogram_pointer;

procedure Subprogram_Driver is

  cb1 : subprogram_pointer.func_ptr;
  package anypackage is new subprogram_pointer.proc_ptr
           (proc_id => cb1, user_specified => the_callback, 
            local_ptr => subprogram_pointer.func_ptr);


begin
  subprogram_pointer.execute(cb1);
exception
  when others =>
    text_io.put_line("exception caught in main routine, reraise");
    raise;
  
end Subprogram_Driver;

mneerach@iiic.ethz.ch (Matthias Ulrich Neeracher) (02/12/91)

In article <662@esosun.UUCP>, howard@esosun.css.gov (Howard Turner) writes:
<>	jduarte@ternes.ICS.UCI.EDU (J o s e D u a r t e ! ! !) writes:
<>
<>		Since Ada does not provide for procedural variables, how
<>	do you/your associates handle the need for having a procedure or
<>	a task perform different duties depending on run-time circumstances?

>Two methods come to mind immediately.  Since I am involved developing
>an Ada/Xt and Ada/widget_set  I've had to solve this "problem".
>
>The two methods are 
>
>1) use tasks.  The prototype code for this is included at the end 
>   of this posting.  This code has been compiled linked and run
>   under several different hardware/compiler combinations and has
>   always run correctly.
>
>2) Call it from C.

Could anybody explain what the rationale for not including procedural
variables in Ada is (I have heard it has been discussed and dismissed again
in the past few years) ? 
   The only reason against it that I have seen is that procedural variables
might put an additional cost to exception handling. Can anybody give a
better explanation ?

Matthias

-- 
Matthias Neeracher                                   mneerach@iiic.ethz.ch
   "These days, though, you have to be pretty technical before you can 
    even aspire to crudeness." -- William Gibson, _Johnny Mnemonic_

g_harrison@vger.nsu.edu ((George C. Harrison) Norfolk State University) (02/13/91)

In article <662@esosun.UUCP>, howard@esosun.css.gov (Howard Turner) writes:
> 	jduarte@ternes.ICS.UCI.EDU (J o s e D u a r t e ! ! !) writes:
> 
> 		Since Ada does not provide for procedural variables, how
> 	do you/your associates handle the need for having a procedure or
> 	a task perform different duties depending on run-time circumstances?
> 
> Two methods come to mind immediately.  Since I am involved developing
> an Ada/Xt and Ada/widget_set  I've had to solve this "problem".
> 
> The two methods are 
> 
> 1) use tasks.  The prototype code for this is included at the end 
>    of this posting.  This code has been compiled linked and run
>    under several different hardware/compiler combinations and has
>    always run correctly.
> 
> 2) Call it from C.
> [etc.]

I am of the impression that one reason procedure parameters were NOT placed
into the Ada standard is that generic subroutines can do the same trick:

Pasqual:

procedure DEMO ( procedure F ( X, Y : INTEGER; var R : REAL ); S : CHAR);

might be

Ada:

generic
  with procedure F ( X, Y : in INTEGER; R : in out FLOAT );
procedure DEMO ( S : in CHARACTER );
~~~~~~~~~~~~~~~::::::::::::
procedure DEMO ( S : in CHARACTER ) is
.....
end DEMO;
~~~~~~~~~~~~~~~::::::::::::
with DEMO;
procedure MAIN is
.................
  procedure QQQ ( LEG1, LEG2 : in INTEGER; REALSTUFF : in out FLOAT) is
  ....
  end QQQ;

  procedure MYDEMO is new procedure DEMO ( F => QQQ);
    use MYDEMO;

......
end MAIN;

-- George C. Harrison -------------- || -- My opinions and observations --
---|| Professor of Computer Science  || -- Only. -------------------------
---|| Norfolk State University, ---- || ----------- Pray for Peace -------
---|| 2401 Corprew Avenue, Norfolk, Virginia 23504 -----------------------
---|| INTERNET:  g_harrison@vger.nsu.edu ---------------------------------

stt@inmet.inmet.com (02/15/91)

Re: procedural variables

The Ada 9X Requirements document, dated December 1990,
includes a Study Topic and a Requirement dealing with
subprograms as variables/parameters.  This indicates
that there is now general consensus that such a capability
should be in the upcoming revision of Ada.

As for why there were left out originally, yes
it was probably that generics provide partial support, and also
I think there were some concerns about safety.

For Ada 9X, we believe we will be able to provide both a safe and
flexible capability for subprograms as variables/parameters.

Stay tuned...

S. Tucker Taft      stt@inmet.inmet.com
Ada 9X Mapping/Revision Team
Intermetrics, Inc.
Cambridge, MA  02138

sampson@cod.NOSC.MIL (Charles H. Sampson) (02/16/91)

In article <24590@neptune.inf.ethz.ch> mneerach@iiic.ethz.ch writes:
>
>Could anybody explain what the rationale for not including procedural
>variables in Ada is (I have heard it has been discussed and dismissed again
>in the past few years) ?

     I've been searching for some pertinent ancient documents, but without
success, so I'm going to have to rely on memory.  Probably everybody remem-
bers that Ada is the result of an effort by the U. S. Department of Defense.
This effort had a limited scope: to define a single computer language that
could be used for all embedded computer applications within the DoD.  (An
embedded computer is one that is a part of a larger system, such as an air-
craft or a smart torpedo.)  This was a well executed project.  It began with
a survey of many of the groups withing the DoD, to determine what capabili-
ties were required, followed by a set of successively refined requirements
documents.  As I remember it, one result of this survey was that there was
no need at all for procedural variables (in DoD embedded computer applica-
tions).  I also remember that the first requirements document (called Straw-
man; this is the one I can't find) explicitly stated that the language would
not have such a capability.  In the successors to Strawman the requirement
was stated positively in terms of the current three formal parameter modes.

     Incidentally, at one stage of this process the capability that ulti-
mately became exceptions was defined as a fourth formal parameter mode.

                                 Charlie

madmats@elcgl.epfl.ch (02/16/91)

In my opinion, procedure variables were not included in Ada because of the
restrictions that must be placed upon them, making the feature non-ortho-
gonal. Most languages that have procedure variables allow only top-level
(i.e. declared as library units or immediately within library packages)
procedures as their values. This is the case of Modula-2 for instance.
The reason for this restriction is obvious: otherwiswe, a local procedure
might be called in a situation where its context is not present, as in the
following example written in PV-Ada (Ada with proc. variables):

   ...                       

   type Proc is procedure;  -- parameterless

   X : Proc;

   procedure P is

      A : Integer;

      procedure Q is
      begin
         A := A + 1;
      end;
   
   begin
      X := Q;
   end P;

   ...

   P;
   X;  -- this should be a call to P.Q, which makes no sense.

Now one might argue that it is easy to check (at runtime) if X's context 
is present at the moment X is called, but this is not so true because a 
simple check that P is currently being called is not sufficient as
different invocations of P may create different contexts for Q, for 
instance by declaring subtypes with non-static bounds.
This issue of 'dangling subprograms' is treated in more detail in the
context of an object-oriented extension to Ada in Norman Cohen's
report "Ada subtypes as subclasses", which I recommend.

For Ada-9X, I would much prefer an object-oriented extension than support
for procedure variables, because this would greatly extend the expressive
power of the language and would simultaneoulsy provide support for
procedure variables for those who really want them: a procedure value 
would be an object with no data and only one method. To me, a procedure
value not tied to an object does not make much sense.

After all, the 'need' for procedure variables has only
become serious since people have been trying to interface x-toolkits
with Ada (callbacks), but does that mean that callbacks are the right
way of doing it ?

Mats Weber

jls@yoda.Rational.COM (Jim Showalter) (02/18/91)

>>Could anybody explain what the rationale for not including procedural
>>variables in Ada is (I have heard it has been discussed and dismissed again
>>in the past few years) ?

They were deemed unsafe, because it is possible to get all fouled up
with lifetime issues. Consider this example:

    procedure outer (...) is

        type some_procedure_type is ...

        foo : some_procedure_type;

        procedure middle (...) is
        
            procedure short_lifetime (...) is -- MATCHES some_procedure_type
            begin
                ...
            end short_lifetime;

        begin
            foo := short_lifetime;
        end middle;

    begin
        middle (...);
        foo; -- WHAT SHOULD HAPPEN HERE?
    end outer;

Anyway, 9x looks like it will have these puppies. How they get around
problems like the above I don't know--I suggest raising program_error
whenever it occurs.
--
***** DISCLAIMER: The opinions expressed herein are my own. Duh. Like you'd
ever be able to find a company (or, for that matter, very many people) with
opinions like mine. 
                   -- "When I want your opinion, I'll beat it out of you."

edwards@rutabaga.cis.ohio-state.edu (stephen edwards) (02/20/91)

In article <1991Feb16.141232.1@elcgl.epfl.ch> madmats@elcgl.epfl.ch writes:

>After all, the 'need' for procedure variables has only
>become serious since people have been trying to interface x-toolkits
>with Ada (callbacks), but does that mean that callbacks are the right
>way of doing it ?

This isn't quite correct.  Ironically, procedure _variables_ (in Ada)
aren't even needed for X callbacks, as virtually all Ada-to-C X interfaces
illustrate.  One only need pass an indirect reference to an Ada procedure
on to the C code.  For this purpose, there isn't need of Ada-manipulable
procedure variables.  (this would not be true when interfacing to an all
Ada implementation of X, of course.)

But this statement, and a few of the past articles on this thread which
point out that generics can be used in place of many forms of procedure
variables, misses what may be the heart of the 'need.'  Remember "table-
driven programming"?  Maybe you learned it by a different name, like
"data-driven computation", computing with "dispatch tables", or something
else.  But the point is that real, live procedure variables _can be
placed in data structures_.  These data structures can be manipulated in
arbitrary ways, and the final result can be passed to something that is,
at heart, an interpreter.

Now, that final "interpreting" routine has programmable semantics.  What
actions it performs can be controlled simply by placing procedure variables
pointing to new routines into the data structure.  How much the semantics
can be altered (and how easily) is determined by the actual structure of
the "table", how it is manipulated, the interpretation strategy, etc.

This is a fairly conventional, but very powerful technique.  It allows
one to completely separate the "driver"/interpreter/execution framework
from the "action routines"/"evaluation functions/etc, and pushes the time
at which the binding between these two distinct parts are made off until
run-time.  It's common in highly programmable/configurable software tools
which load a configuration file when they are run--this file is basically
a description of the interpreted data structure in some abstract form.

Without procedure variables, you can't completely separate the driver
from the action routines.  This makes the software somewhat less flexible
and also more difficult to maintain.

A few posters have hinted at such "alternative" uses for procedure variables,
but no one had quite  come right out and said it (if they did and I missed
it, I apologize for the redundancy).


				-- Steve

Stephen Edwards					U.S. mail: 372 Jones Tower
e-mail    : edwards@cis.ohio-state.edu	           	   101 Curl Drive
home phone: (614)-293-0842			 	   Columbus, OH  43210

jls@yoda.Rational.COM (Jim Showalter) (02/21/91)

Agreed: table-driven programming requires subprogram variables to
work smoothly. We encounter other cases where they would be quite
useful.

By the way, we implemented subprogram variables when we wrote our
Ada-based X bindings. Works quite well, and only requires the body
of one package to be rehosted on each platform. The rest is pure
Ada.
--
***** DISCLAIMER: The opinions expressed herein are my own. Duh. Like you'd
ever be able to find a company (or, for that matter, very many people) with
opinions like mine. 
                   -- "When I want your opinion, I'll beat it out of you."