[comp.lang.ada] Procedure types and dynamic binding

sommar@enea.se (Erland Sommarskog) (12/31/88)

Bob Hathaway (rjh@cs.purdue.edu) writes:
>For yet another Ada 9X extension, I propose procedural variables.   
>...
>Procedural variables can
>avoid the redundant use of case statements by allowing an operation to be 
>stored within an Adt.     

I have stated my view before: a langauge revision should not introduce 
major changes, but stick to an overview of details that needs refining,
like character representation. There is one exception, though: if someone
comes up with a fully-fledged extention to give Ada dynamic binding to
make it a truly object-oriented langauge, I would whole-heartedily
support that proposal.

From this it is clear I think Bob Hathaway's idea should be rejected.
There are some gains with it, but not suffciently many. We will move
some tiny step against the O-O paradigm, but we will not be there.

Just for discussion: If we should make Ada an object-oriented langauge,
what should we add? Quite obvious seems package types to get classes.
If we could create several instances of a package, you have what Bob 
wanted and more. You have data *and* operations stored together. 
  But this doesn't suffice. This far we have just constructed task
types doesn't cost us a context change. We must have inheritance in
some way. How tell which other package types we inherit from? The 
WITH clause is not useful; it merely tells us which types we intend
to declare objects of. USE? Maybe. Doing USE on a package type to 
get all exported operations directly visible is of no interest. We 
should USE the package variable instead. But for inheritance we want 
all attributes, also those who do not appear in the specification 
part (or?), and particulary we probably want to have access to 
private types internal structure. So best would be a particular 
INHERIT clause.
  Multiple inheritance? That doesn't seem to be any problem. Ada
already has RENAMES which here could serve to remove clashes.
  Dynamic binding? We must be able to redefine a inherited operation.
There is no current construct for that in Ada so a REDEFINES clause
has to be added. (Rather useful is also deferred procedures that
inhereting packages *must* define, Ada more or less has this concept.) 
  We are about satisfied by now. Probably we need delaration a la
Eiffel, but if we could make all the rest, this would be no problem.
  Procedure types could be added for completeness, but doesn't feel
that necessary with all we added above.

Now, it is not as easy as this of course. Many detail rules has to
be thought out. And we would get an unnecessary big language. Would
we really need the sophisticated system with subtypes and derived
types? Well, we can't throw it away. 

Oh, one we need one more thing if go this way. Yes, you guessed it:
Garbage collection.

>It also allows individually parameterizable and 
>reprogrammable Adts since operations can be provided to alter the Adts actions
>or structure.  Generic subprogram parameters can only allow Adts to be set 
>once for any instantiation.  I use procedural variables and function pointers
>in Adts frequently when programming in languages other than Ada and am 
>convinced they are an elegant way to model Adt actions.

With the risk of saying something completely obvious: if you want variable
user-provided operations the following example with a tree-traversing
illustrates:

   Generic
      Type Data_type is limited private;
      With procedure Assign(A : in out Data_type; B : in Data_type);
      With function "<"(A, B : Data_type) return boolean is <>;
      With function ">"(A, B : Data_type) return boolean is <>;
   Package Binary_trees is
      Type Tree_type is private;        -- A tree with sorted data.
      Type Node_type is private;        -- A node in such a tree.
      ...
      Generic
         With Procedure Treat(Node : in     Node_type;  
                              Data : in out Data_type); 
      Procedure Traverse_forward(Tree : Tree_type);

(By the way, an example like the one above should be added to the validation
suite if it's not already there. A PC compiler I played with choked on the 
code above, and I believe it is/was validated.)
-- 
Erland Sommarskog
ENEA Data, Stockholm              This signature is not to be quoted.
sommar@enea.se

rjh@cs.purdue.edu (Bob Hathaway) (01/01/89)

Newsgroups: comp.lang.ada
Subject: Procedure types and dynamic binding
Message-ID: <4204@enea.se>
Date: 30 Dec 88 21:42:42 GMT
Organization: ENEA DATA AB, Sweden
Lines: 84

From Erland Sommarskog:
>Bob Hathaway (rjh@cs.purdue.edu) writes:
>>For yet another Ada 9X extension, I propose procedural variables.   
>>...
>>Procedural variables can
>>avoid the redundant use of case statements by allowing an operation to be 
>>stored within an Adt.     

>I have stated my view before: a language revision should not introduce 
>major changes, but stick to an overview of details that needs refining,
>like character representation. There is one exception, though: if someone

I disagree.  While it's not difficult to design new languages with powerful 
constructs and methodologies it is difficult to get them standardized 
and accepted.  Validated Ada compilers are available on almost every machine
and extending Ada with more modern constructs will at least provide one 
commonly available modern programming language, even if only for designing 
better ones ;-)

>comes up with a fully-fledged extension to give Ada dynamic binding to
>make it a truly object-oriented language, I would whole-heartedily
>support that proposal.

In "Concurrency and Programming Languages" by David Harland, as sited by
Bill, dynamic and static binding are provided as a module option.  I think
this is an excellent idea since most of the literature gives a mind set of 
one type of binding or the other, with the obvious general solution being to
provide both.  Another issue is the level of encapsulation in Ada dynamic
binding should be provided.  Dynamic binding of procedures and functions 
within a package is possible, but so is dynamic binding of packages.  Which
did you mean?

>From this it is clear I think Bob Hathaway's idea should be rejected.
>There are some gains with it, but not suffciently many. We will move
>some tiny step against the O-O paradigm, but we will not be there.

Procedural variables are orthogonal to "O-O" programming, they can
be used for a number of purposes.  Some typical uses are tables of procedural 
variables with generator functions and configuring device driver tables.
Array aggregates are easier to modify than in-line code and initializing
tables of procedural variables with aggregates using named associations would
make programs easy to read.  While tables do not always provide the best 
interface to programmers, I think some circumstances justify their use.

>Just for discussion: If we should make Ada an object-oriented langauge,
>what should we add? Quite obvious seems package types to get classes.
>If we could create several instances of a package, you have what Bob 
>wanted and more. You have data *and* operations stored together. 

Yes, this is the "Completion of the Adt paradigm" Bill mentioned earlier.
In a compilers class at Purdue, I implemented a class type structure which
contained its data and operations including initialization code, and
termination code would have been easy to provide as well.  But these
operations were hardwired into the declaration and a general facility to 
change Adt operations is desirable.  For example, I might want an instance 
to output data in one way but under other circumstances output data in 
another.  If an output operation is implemented as a procedural variable,
I could provide a change_output operation which changes the output operation 
in the Adt. Thereafter, the new print routine would be in effect whenever 
the output operation is invoked.

[ More about the virtues of inheritance... ]

>Now, it is not as easy as this of course. Many detail rules has to
>be thought out. And we would get an unnecessary big language. Would
>we really need the sophisticated system with subtypes and derived
>types? Well, we can't throw it away. 

I don't think procedural variables or class structures will make Ada 
much bigger.  Procedural variables are not complicated in Modula and
from my experience class type structures are not difficult to implement and 
do not complicate the language.  They would, however, provide an incremental 
improvement in data abstraction.

>With the risk of saying something completely obvious: if you want variable
>user-provided operations the following example with a tree-traversing
>illustrates:
>
>   Generic
>      Type Data_type is limited private;
>      With procedure Assign(A : in out Data_type; B : in Data_type);
>      With function "<"(A, B : Data_type) return boolean is <>;
>      With function ">"(A, B : Data_type) return boolean is <>;
>   Package Binary_trees is
>      Type Tree_type is private;        -- A tree with sorted data.
>      Type Node_type is private;        -- A node in such a tree.
>      ...
>      Generic
>         With Procedure Treat(Node : in     Node_type;  
>                              Data : in out Data_type); 
>      Procedure Traverse_forward(Tree : Tree_type);
>

This isn't what I had in mind.  What if variables of type Tree_Type should
be traversed in different ways depending on context?  As above, a case
statement is needed to choose the routine to parameterize Traverse_forward
whenever traversal is invoked.  A scheme is needed to allow Tree_Type Adts to
be programmed with different traversal routines after its declaration has been 
elaborated and without the need for case statements.  If Traverse_forward 
invoked a procedural variable stored within Tree_Type then instances of a
Tree_Type Adt could be reprogrammed to traverse the tree however desired and
a simple call to Traverse_forward would produce the desired result.  I think
this is in harmony with 'O-O' programming, which I usually refer to as Adt
programming, since the operation is stored in the Adt as a procedural variable.

                                           Bob Hathaway
					   rjh@purdue.edu

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/05/89)

From article <4204@enea.se>, by sommar@enea.se (Erland Sommarskog):
> Just for discussion: If we should make Ada an object-oriented langauge,
> what should we add? Quite obvious seems package types to get classes.

    I'd think classes would be defined a bit differently!  A package
    could contain declarations of variables, and we certainly don't want
    variables included in a class definition (where class is defined as
    an abstraction over types, e.g., FRUIT as an abstraction over APPLEs,
    ORANGEs, etc.).  A class of types would consist of the name of the class,
    in conjunction with a set of operations which must be available over
    a given type in order for it to be considered a member of the class.

    Then the use of generics would be reduced substantially; we could 
    simply write

       function MAKE_JUICE (OUT_OF : in out FRUIT) return JUICE;
 
    and this would operate on any type of object (APPLEs, ORANGEs, etc.)
    which satisfied the definition of a FRUIT.  There would still be a
    need for generics in situations where values or objects are required
    as generic parameters.

    (BTW, the above example is also an example of why the stupid rule
    about functions having only "in" parameters needs to be repealed...) 
           
> But for inheritance we want all attributes, also those who do not 
> appear in the specification part (or?), and particulary we probably 
> want to have access to private types internal structure. 
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    One of the most fundamental aspects of the ADT paradigm is that
    an object is known to the outside world only as a type definition
    and a set of predefined operations.  This provides the independence
    which allows us to compile specifications and implementations separately.
    Access to the internal structure of a private type violates the entire
    concept of a "private" type.  

    To achieve this effect, one can simply declare the type in question 
    as a visible type rather than a private type.  In other words, simply 
    avoid using private types.  Be warned, however, that terrible compilation 
    dependencies will result, and you will lose the freedom to replace one 
    implementation with another; in short, the method is NOT recommended 
    for serious use, and is presented only as a theoretical means of achieving 
    what you seek (in my view, misguidedly).
 
> Probably we need delaration a la Eiffel, [...]

    Gee, why don't we just make "Ada" an alias for "Eiffel"?   :-)  :-(

    Seriously though, there *is* a comp.lang.eiffel.  Perhaps you 
    would feel more at home there rather than in comp.lang.ada.

> With the risk of saying something completely obvious: if you want variable
> user-provided operations the following example with a tree-traversing
> illustrates:
> 
>    Generic
>       Type Data_type is limited private;
>       With procedure Assign(A : in out Data_type; B : in Data_type);
>       With function "<"(A, B : Data_type) return boolean is <>;
>       With function ">"(A, B : Data_type) return boolean is <>;
>    Package Binary_trees is
>       Type Tree_type is private;        -- A tree with sorted data.
>       Type Node_type is private;        -- A node in such a tree.
>       ...
>       Generic
>          With Procedure Treat(Node : in     Node_type;  
>                               Data : in out Data_type); 
>       Procedure Traverse_forward(Tree : Tree_type);
> 
> (By the way, an example like the one above should be added to the validation
> suite if it's not already there. A PC compiler I played with choked on the 
> code above, and I believe it is/was validated.)

     Why does Node need to be a parameter of Treat?  All you need is
     something that will treat Data once Traverse_Forward has extracted
     it from the Node.  Once you've removed the unnecessary parameter,
     your package should compile perfectly.



                                            Bill Wolfe

                                    wtwolfe@hubcap.clemson.edu

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/05/89)

From article <5753@medusa.cs.purdue.edu>, by rjh@cs.purdue.edu (Bob Hathaway):
> Erland writes:
>>I have stated my view before: a language revision should not introduce 
>>major changes, but stick to an overview of details that needs refining,
>>like character representation. There is one exception, though: if someone
> 
> I disagree.  While it's not difficult to design new languages with powerful 
> constructs and methodologies it is difficult to get them standardized 
> and accepted.  Validated Ada compilers are available on almost every machine
> and extending Ada with more modern constructs will at least provide one 
> commonly available modern programming language, even if only for designing 
> better ones ;-)

   I have to agree with Bob.  Since old Ada and new Ada would be fairly
   similar, automatic translators would probably be highly effective
   should the need arise.  

>>comes up with a fully-fledged extension to give Ada dynamic binding to
>>make it a truly object-oriented language, I would whole-heartedily
>>support that proposal.
> 
> In "Concurrency and Programming Languages" by David Harland, as sited by
> Bill, dynamic and static binding are provided as a module option.  I think
> this is an excellent idea since most of the literature gives a mind set of 
> one type of binding or the other, with the obvious general solution being to
> provide both.  

    Um, hold on there.  I cited Harland primarily as an example of extreme
    steps which would provide maximum support for abstraction, and I support
    Harland's view that departures from the state of maximum expressiveness
    must bear the burden of proof, but I also noted that there are certain 
    situations in which I think a convincing case can be made in this
    direction.  Most languages, though, go much too far in their restriction
    of expressiveness, including Ada.  (Still, Ada is the best language 
    available, all things considered.) 

    As far as dynamic binding goes, I think there is a need to provide
    the ability to have a program edit (or generate) a piece of source
    code, compile that code (including any necessary recompilations),
    dump any necessary local knowledge for its successor to load in, 
    initiate its successor, and finally kill itself off, but I see no 
    need to go any farther than that.  Harland gives a rather eloquent
    statement of why systems must be self-modifying, and this argument
    is convincing.  But dynamic binding (as I understand the term) is
    "too changeable", like the uncontrolled change which occurs in some
    inheritance-oriented languages.  Ada's compilation control facilities
    enable controlled change; they provide firewalls against modification
    which serve to require that all aspects of the propagating change(s)
    are considered throughout the system, and this is a very good thing
    which we should not dispense with lightly.  Even the scheme I've
    described should be applied only with extreme caution.
 
> [...] a general facility to change Adt operations is desirable.  For 
> example, I might want an instance to output data in one way but under 
> other circumstances output data in another.  If an output operation is 
> implemented as a procedural variable, I could provide a change_output 
> operation which changes the output operation in the Adt. Thereafter, 
> the new print routine would be in effect whenever the output operation 
> is invoked.

    Other than doing things as I've described above, another way would
    be (assuming that New Ada expands the idea of a specification to 
    include any accesses to externally defined objects/definitions) to
    have the output procedure examine its environment to decide what to do.

> I don't think procedural variables or class structures will make Ada 
> much bigger.  Procedural variables are not complicated in Modula [...] 

    But Modula doesn't have generics.  Using the combination of generics
    and the class abstraction outlined in my earlier article, I think
    we'll get more power, with better readability.

> This isn't what I had in mind.  What if variables of type Tree_Type should
> be traversed in different ways depending on context?  

    Just make basic traversal operations a part of your abstraction.



                                             Bill Wolfe

                                      wtwolfe@hubcap.clemson.edu

sommar@enea.se (Erland Sommarskog) (01/07/89)

Bill Wolfe writes: 
>    I'd think classes would be defined a bit differently!  A package
>    could contain declarations of variables, and we certainly don't want
>    variables included in a class definition (where class is defined as

This is half-true. Thus, we don't want them to be modifyable, but we 
want them to be readable. As far as I am concerned, writeable variable
in packages should be prohibited today, but, alas, wouldn't be a change
to rhyme with backwards compability. It should be avoided anyway.

>> But for inheritance we want all attributes, also those who do not 
>> appear in the specification part (or?), and particulary we probably 
>> want to have access to private types internal structure. 
>  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>
>    One of the most fundamental aspects of the ADT paradigm is that
>    an object is known to the outside world only as a type definition
>    and a set of predefined operations.  This provides the independence
>    which allows us to compile specifications and implementations separately.
>    Access to the internal structure of a private type violates the entire
>    concept of a "private" type.  

But can you as ADT designer always find out exactly what I want? Say 
you hand me a linked-list package. I discover that I need to reverse
a list, but does not have such an operation. With Ada today I have
three possibilies:
   1) Write Reverse_list using the existing primitives and place it 
      in my own package.
   2) Write my own extended list package, which calls yours and adds my own 
      Reverse_list.
   3) Re-hack your package, which gives me the possiblility to write a more
      efficient version.
1) has the disadvantge that my code contains something that does not belong
there. 2) Causes redundancy and duplicated declarations. 3) is maybe not 
available for various reasons. It resides in a library I cannot write to, 
and you are holiday. Besides other users would have a lot of recompilation 
triggered just before deliverance and get mad.
  
With inheritance there is a fourth possibility. I can write my extended  
list package to solely inherit all from yours and then add Reverse_list.
Depending if I want to be saved from changes, or need efficiency I
use your primitives, or the implementation.

For more info on this, the "open-closed principle", I once again refer
to Bertrand Meyer's "Object-Oriented Software Construction" where he
discusses these ideas.

>    Seriously though, there *is* a comp.lang.eiffel.  Perhaps you 
>    would feel more at home there rather than in comp.lang.ada.

I feel at home both here and there. Wouldn't say I found that comment
very serious, :-)

>>    Generic
>>...
>>    Package Binary_trees is
>>       ...
>>       Generic
>>          With Procedure Treat(Node : in     Node_type;  
>>                               Data : in out Data_type); 
>>       Procedure Traverse_forward(Tree : Tree_type);
>> 
>> (By the way, an example like the one above should be added to the validation
>> suite if it's not already there. A PC compiler I played with choked on the 
>> code above, and I believe it is/was validated.)
>
>     Why does Node need to be a parameter of Treat?  All you need is
>     something that will treat Data once Traverse_Forward has extracted
>     it from the Node.  Once you've removed the unnecessary parameter,
>     your package should compile perfectly.

You shouldn't work as a compiler. Whether Node is anything useful or
not could be discussed, but it's certainly perfectly legal Ada. If
it is not, tell me why and I write an error report about our Unix
compiler that accepts this. (The use for Node is that the user
may want a quick reference to some data, which he saves somewhere
else.)
-- 
Erland Sommarskog
ENEA Data, Stockholm              This signature is not to be quoted.
sommar@enea.se

rjh@cs.purdue.EDU (Bob Hathaway) (01/08/89)

>> I don't think procedural variables or class structures will make Ada
>> much bigger.  Procedural variables are not complicated in Modula [...]
>
[Bill Wolfe responds]
>    But Modula doesn't have generics.  Using the combination of generics
>    and the class abstraction outlined in my earlier article, I think
>    we'll get more power, with better readability.
>

Yes, class structures with generics would provide good data abstraction.
This would allow classes to be statically parameterized by types, variables, 
constants, and subprograms.  Generics are good for setting an Adt once, such as
a stack which is parameterized by its element type and a print subprogram.  
But what about dynamic parameterization?  For dynamic parameterization of data,
polymorphic parameters to Adt operations are needed to allow the greatest 
expressiveness.  An example is a stack of any element type which could be
provided by polymorphic parameters to an insert subprogram.  For dynamic 
parameterization of operations, procedural variables are needed.  An example
is a stack which can change its print operation as described previously.

Now, adding polymorphic parameters to Ada would be a sizable extension but 
adding procedural variables would be almost trivial.  Admittedly, Adt 
designers can provide a plethora of operations and programmers can use case 
statements to choose among them but this puts greater burden on the programmer
since they must now select the correct operation throughout their code.  Also,
static generic parameterization is desirable in certain cases such as in the
stack example above when the element type is known in advance and the type
system can statically perform strong typechecking for correctness.  But the
flexibility of dynamic parameterization allows maximum expressiveness 
and greater power, and so "the burden of proof" has to be made against
dynamically parameterized operations.  Since static generics and dynamic
parameterization are not mutually exclusive, both could be provided.

>> This isn't what I had in mind.  What if variables of type Tree_Type should
>> be traversed in different ways depending on context?
>
>    Just make basic traversal operations a part of your abstraction.
>

But this calls for case statements throughout the program to determine which 
operation to call, as mentioned above.  For maximum expressiveness, I might
want to set instances with particular traversal operations and then use a
single operation invocation at a later point in the program which will invoke
the correct operation for all instances, reducing redundant code and allowing 
maximum expressiveness.


					Bob Hathaway
                                        rjh@purdue.edu

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/08/89)

From article <5790@medusa.cs.purdue.edu>, by rjh@cs.purdue.EDU (Bob Hathaway):
> 
>>> I don't think procedural variables or class structures will make Ada
>>> much bigger.  Procedural variables are not complicated in Modula [...]
>>
> [Bill Wolfe responds]
>>    But Modula doesn't have generics.  Using the combination of generics
>>    and the class abstraction outlined in my earlier article, I think
>>    we'll get more power, with better readability.
>>
> Yes, class structures with generics would provide good data abstraction.
> This would allow classes to be statically parameterized by types, variables, 
> constants, and subprograms.  Generics are good for setting an Adt once, such as
> a stack which is parameterized by its element type and a print subprogram.  
> But what about dynamic parameterization?  For dynamic parameterization of data,
> polymorphic parameters to Adt operations are needed to allow the greatest 
> expressiveness.  An example is a stack of any element type which could be
> provided by polymorphic parameters to an insert subprogram.  For dynamic 
> parameterization of operations, procedural variables are needed.  

     No, all we need is the ability to have pointers which are not bound
     to any specific type.  Most of the time we want to say "pointer to
     SPECIFIC_TYPE", but sometimes we just want to say "pointer".  Given
     the ability to specify an unrestricted pointer, we can implement
     the desired stack (or whatever) of any element type. 
 
> Now, adding polymorphic parameters to Ada would be a sizable extension

     Not true; all we need to do is write procedures which require an
     arbitrary pointer as a parameter, and we're done.

> static generic parameterization is desirable in certain cases such as in the
> stack example above when the element type is known in advance and the type
> system can statically perform strong typechecking for correctness.  

     Quite true, and pointers should be restricted wherever it is reasonable
     to do so, just as ranges should be specified whenever appropriate.

# But the
# flexibility of dynamic parameterization allows maximum expressiveness 
# and greater power, and so "the burden of proof" has to be made against
# dynamically parameterized operations.  Since static generics and dynamic
# parameterization are not mutually exclusive, both could be provided.

     Unless, as I have shown above, a simpler mechanism will suffice.

#>> What if variables of type Tree_Type should
#>> be traversed in different ways depending on context?
#>
#>    Just make basic traversal operations a part of your abstraction.
#>
# 
# But this calls for case statements throughout the program to determine which 
# operation to call, as mentioned above.  For maximum expressiveness, I might
# want to set instances with particular traversal operations and then use a
# single operation invocation at a later point in the program which will invoke
# the correct operation for all instances, reducing redundant code and allowing 
# maximum expressiveness.

   Using the above mechanism, we've eliminated the case statements.

   Now set up your abstraction to provide a "current node" concept,
   along with operations like DESCEND_LEFT, DESCEND_RIGHT, and ASCEND.
   This can be implemented either via parent pointers or via a secret
   stack which holds the path of descent used to reach the current node.



                                           Bill Wolfe

                                    wtwolfe@hubcap.clemson.edu

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/08/89)

From article <4223@enea.se>, by sommar@enea.se (Erland Sommarskog):
> But can you as ADT designer always find out exactly what I want? Say 
> you hand me a linked-list package. I discover that I need to reverse
> a list, but does not have such an operation. With Ada today I have
> three possibilies:
>    1) Write Reverse_list using the existing primitives and place it 
>       in my own package.
>    2) Write my own extended list package, which calls yours and adds my own 
>       Reverse_list.
>    3) Re-hack your package, which gives me the possiblility to write a more
>       efficient version.
> 1) has the disadvantge that my code contains something that does not belong
> there. 2) Causes redundancy and duplicated declarations. 3) is maybe not 
> available for various reasons. It resides in a library I cannot write to, 
> and you are holiday. Besides other users would have a lot of recompilation 
> triggered just before deliverance and get mad.

     Proceed with either 2) or 3), whichever you prefer, and use it until
     procedures can be invoked whereby the library is upgraded to satisfy
     this requirement.  Probably there is a mechanism for submitting new
     requirements, and you can go ahead and submit your workaround along
     with your request; the workaround's spec will probably be placed into
     immediate service, perhaps with minor revisions, and the implementation
     will be updated with an optimized version as time permits.  Other users
     will not have recompilation triggered because they are depending on 
     the version which does not provide list reversal, which remains in
     the library.  If no increase in efficiency is possible as a result of
     omitting list reversal, then probably a note will be placed in the
     library saying that programmers should use the new version instead,
     and eventually all software depending on the old version will be 
     rewritten (shifting to the new version in the process), or abandoned,
     and the old version will then be removed from the library.   If your
     local installation prefers, they can instead purchase a more powerful
     version of the ADT, and then apply the same phasing-out procedure
     if there is no efficiency bonus in the less comprehensive ADT.

> With inheritance there is a fourth possibility. I can write my extended  
> list package to solely inherit all from yours and then add Reverse_list.
> Depending if I want to be saved from changes, or need efficiency I
> use your primitives, or the implementation.
> 
> For more info on this, the "open-closed principle", I once again refer
> to Bertrand Meyer's "Object-Oriented Software Construction" where he
> discusses these ideas.

     I have read about this in Meyer's book, and do not agree with it.

     We do not first create the abstract concept of FRUIT and then 
     specialize into APPLEs and ORANGEs; it is the other way around!!

     Additionally, Meyer's approach creates all manner of compilation
     dependencies and inefficiencies, and is in general a very messy
     way to go about doing things.  Instead, let us proceed along the
     lines of a synthesis-oriented approach, as I have described.  

     We should describe FRUIT as an abstraction, and the characteristics
     of FRUIT permit us to decide whether a given real object falls 
     into this class or not.  We can then construct packages which operate
     over the abstraction FRUIT, knowing that our APPLEs and ORANGEs
     will satisfy the abstraction and therefore be compatible with the
     package.  We can even discover PLUMs at a later date, design the
     implementing specification to be compatible with the FRUIT abstraction,
     and then freely insert PLUMs into the stream of FRUIT being handled
     by our packages.  This is a far more natural paradigm; it corresponds
     much more closely to the natural processes of human thought.  

>>>    Generic
>>>...
>>>    Package Binary_trees is
>>>       ...
>>>       Generic
>>>          With Procedure Treat(Node : in     Node_type;  
>>>                               Data : in out Data_type); 
>>>       Procedure Traverse_forward(Tree : Tree_type);
>>> 
>>> (By the way, an example like the one above should be added to the validation
>>> suite if it's not already there. A PC compiler I played with choked on the 
>>> code above, and I believe it is/was validated.)
>>
>>     Why does Node need to be a parameter of Treat?  All you need is
>>     something that will treat Data once Traverse_Forward has extracted
>>     it from the Node.  Once you've removed the unnecessary parameter,
>>     your package should compile perfectly.
> 
> You shouldn't work as a compiler. Whether Node is anything useful or
> not could be discussed, but it's certainly perfectly legal Ada. If
> it is not, tell me why and I write an error report about our Unix
> compiler that accepts this. (The use for Node is that the user
> may want a quick reference to some data, which he saves somewhere
> else.)

    But the problem is that Node is not the user's property; it is the
    property of the generic package.  As such, the user is not free to
    stick arbitrary data in some secret place within the Node.  If the
    user wants to store "a quick reference to some data", then the user
    can define Data as a record with two fields: one will provide the
    REAL data, and the other will provide the user's secret cache.  Then
    the procedure Treat which the user writes can access the user's cache
    freely, and now we see that Node really is a useless parameter.

    (I'm not concerned with the deficiencies of one compiler or another
    as much as I am with the goodness of the ADT; I see no reason why
    the PC should have rejected the code, but I saw a more important
    question which needed to be taken care of first.)



                                         Bill Wolfe

                                  wtwolfe@hubcap.clemson.edu
 

rjh@cs.purdue.EDU (Bob Hathaway) (01/08/89)

>> Yes, class structures with generics would provide good data abstraction.
>> This would allow classes to be statically parameterized by types, variables,
>> constants, and subprograms.  Generics are good for setting an Adt once, 
>> such as
>> a stack which is parameterized by its element type and a print subprogram.  
>> But what about dynamic parameterization?  For dynamic parameterization of
>> data,
>> polymorphic parameters to Adt operations are needed to allow the greatest 
>> expressiveness.  An example is a stack of any element type which could be
>> provided by polymorphic parameters to an insert subprogram.  For dynamic 
>> parameterization of operations, procedural variables are needed.  
>
>     No, all we need is the ability to have pointers which are not bound
>     to any specific type.  Most of the time we want to say "pointer to
>     SPECIFIC_TYPE", but sometimes we just want to say "pointer".  Given
>     the ability to specify an unrestricted pointer, we can implement
>     the desired stack (or whatever) of any element type. 

But this assumes no type or error checking at all.  By dynamic 
parameterization I meant with runtime type and error checking.  For static
typechecking of data we can use a pointer to a tagged variant record to
distinguish types.  This assumes foreknowledge of the types in the variant
but at least allows subprograms to distinguish objects.   With unbound
pointers, there is no built-in type or error checking mechanism.  This
covers the case of "pseudo" polymorphic parameters but what about
dynamically parameterized operations?  Ada has an address type applicable
to function and task type addresses but this is implementation dependent
and there is no defined typechecking mechanism for the subprogram parameter
structure as there is with procedural variables.  Unbound pointers to
functions have the same consequences.  While tasks can allow procedural
abstraction, better is a higher order construct such as procedural
variables.

>> 
>> Now, adding polymorphic parameters to Ada would be a sizable extension
>
>     Not true; all we need to do is write procedures which require an
>     arbitrary pointer as a parameter, and we're done.

I meant with runtime typechecking and how this should be done is another 
point.  Milner's type system uses static typechecking for polymorphic 
parameters but doesn't seem to provide the necessary power for arbitrary 
polymorphism.  More powerful is a scheme which invokes operations on 
arbitrary types at runtime with dynamic type and error checking mechanisms
and not just simple statically checked invocation.  This requires a more
elaborate mechanism but adding polymorphism to Ada's static type system
would allow programmers to fully specify types for static checking and 
efficiency or choose a more powerful parameter passing mechanism for
greater expressiveness however polymorphic parameters and variables would 
entail major changes to the LRM.  On dynamically parameterized operations, 
if incremental improvement is the goal of the Ada-9X standard then 
simple statically checked dynamically parameterized operations can be
provided with procedural variables.  Dynamic typechecking of procedural
variables is also possible but I was advocating statically checked
procedural variables as an extension.

># But the
># flexibility of dynamic parameterization allows maximum expressiveness 
># and greater power, and so "the burden of proof" has to be made against
># dynamically parameterized operations.  Since static generics and dynamic
># parameterization are not mutually exclusive, both could be provided.
>
>     Unless, as I have shown above, a simpler mechanism will suffice.

But this "proof" doesn't apply to dynamically parameterized operations
with type and error checking as defined above, could you comment on that
also?


>#>> What if variables of type Tree_Type should
>#>> be traversed in different ways depending on context?
>#>
>#>    Just make basic traversal operations a part of your abstraction.
>#>
> 
> But this calls for case statements throughout the program to determine which 
># operation to call, as mentioned above.  For maximum expressiveness, I might
># want to set instances with particular traversal operations and then use a
># single operation invocation at a later point in the program which will invoke
># the correct operation for all instances, reducing redundant code and allowing
># maximum expressiveness.
>
>  Using the above mechanism, we've eliminated the case statements.
>

I'm not sure what this means.  With pointers to functions there should
be typechecking of parameter structures and this is the essence of
procedural variables.


>   Now set up your abstraction to provide a "current node" concept,
>  along with operations like DESCEND_LEFT, DESCEND_RIGHT, and ASCEND.
>  This can be implemented either via parent pointers or via a secret
>  stack which holds the path of descent used to reach the current node.
>

Yes this would work, but leaves programmers to build their own traversal 
routines.  My idea was to provide a mechanism allowing programmers to specify
a procedural invocation in a single, simple statement and have the Adt
select and carry out the operation implicitly.


                                           Bob Hathaway
                                           rjh@purdue.edu

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/09/89)

From article <5793@medusa.cs.purdue.edu>, by rjh@cs.purdue.EDU (Bob Hathaway):
>>     all we need is the ability to have pointers which are not bound
>>     to any specific type.  Most of the time we want to say "pointer to
>>     SPECIFIC_TYPE", but sometimes we just want to say "pointer".  Given
>>     the ability to specify an unrestricted pointer, we can implement
>>     the desired stack (or whatever) of any element type. 
> 
> But this assumes no type or error checking at all.  By dynamic 
> parameterization I meant with runtime type and error checking.  

    The same runtime type and error checking will occur with
    the unrestricted pointers.  When an operation is applied
    to the object pointed to, the type of that object will
    be used to determine which code to invoke or to prove
    the existence of a run-time error.  Now I see no reason
    why we can't say "pointer to TYPE_1 or TYPE_2 (etc.)",
    thus introducing a new form of restricted pointer, but
    what we are dealing with here is the need to store 
    any type of object whatsoever.  Clearly, unrestricted 
    pointers, with their associated run-time costs, should 
    only be used when their power is absolutely necessary. 

> For static typechecking of data we can use a pointer to a tagged variant 
> record to distinguish types.  This assumes foreknowledge of the types in 
> the variant but at least allows subprograms to distinguish objects.   

    What you describe is a hack which would provide (less cleanly) the
    power provided by the form of restricted pointer described above.

    This is inappropriate to the problem under discussion, which 
    assumes the need to program a polymorphic stack without foreknowledge
    of the types of objects to be stacked.

> but what about dynamically parameterized operations?  Ada has an address 
> type applicable to function and task type addresses but this is 
> implementation dependent and there is no defined typechecking mechanism 
> for the subprogram parameter structure as there is with procedural 
> variables.  Unbound pointers to functions have the same consequences.  
> While tasks can allow procedural abstraction, better is a higher order 
> construct such as procedural variables.

     I have described in earlier articles mechanisms by which the
     desired effects can be achieved in a more intuitive manner.

     Please provide a concrete example in which the provided
     mechanisms would prove inadequate.

>>  Now set up your abstraction to provide a "current node" concept,
>>  along with operations like DESCEND_LEFT, DESCEND_RIGHT, and ASCEND.
>>  This can be implemented either via parent pointers or via a secret
>>  stack which holds the path of descent used to reach the current node.
> 
> Yes this would work, but leaves programmers to build their own traversal 
> routines.  My idea was to provide a mechanism allowing programmers to specify
> a procedural invocation in a single, simple statement and have the Adt
> select and carry out the operation implicitly.

   But wasn't the basis of your argument the claim that the usual
   preorder, postorder, and inorder traversal schemes may not be
   enough?  The solution to this question is clearly to proceed
   as I have described.  Perhaps you could clarify the problem
   under consideration and the precise nature of your solution...



                                    Bill Wolfe

                             wtwolfe@hubcap.clemson.edu
 

rjh@cs.purdue.EDU (Bob Hathaway) (01/09/89)

> 
> But this assumes no type or error checking at all.  By dynamic 
> parameterization I meant with runtime type and error checking.  
>
>    The same runtime type and error checking will occur with
>    the unrestricted pointers.  When an operation is applied
>    to the object pointed to, the type of that object will
>    be used to determine which code to invoke or to prove
>    the existence of a run-time error.  Now I see no reason
>    why we can't say "pointer to TYPE_1 or TYPE_2 (etc.)",
>    thus introducing a new form of restricted pointer, but
>    what we are dealing with here is the need to store 
>    any type of object whatsoever.  Clearly, unrestricted 
>    pointers, with their associated run-time costs, should 
>    only be used when their power is absolutely necessary. 

I'm still not sure what this means.  Do you mean passing 
unrestricted pointers to procedures and then selecting the 
procedure via overloading (ad-hoc polymorphism) at runtime?
I meant allowing polymorphic parameters to a particular subprogram, 
such as
        procedure test (parameter : polymorphic; ...) is ...
which defeats static typechecking of parameter and uses a 
type attribute in the body of test.  This also brings up the issue of 
name vs. structural equivalence of runtime typechecking in both cases,
but the usual Ada rules could apply.   Either approach allows 
considerable power but adding runtime typechecking is not a trivial
extension, as discussed earlier.  But I agree a more powerful parameter
passing mechanism is highly desirable.

>> For static typechecking of data we can use a pointer to a tagged variant 
>> record to distinguish types.  This assumes foreknowledge of the types in 
>> the variant but at least allows subprograms to distinguish objects.   
>
>    What you describe is a hack which would provide (less cleanly) the
>    power provided by the form of restricted pointer described above.
>
>    This is inappropriate to the problem under discussion, which 
>    assumes the need to program a polymorphic stack without foreknowledge
>    of the types of objects to be stacked.

I agree, I thought you were talking about unbound pointers; insufficient
semantics of unrestricted pointers was given.

>> but what about dynamically parameterized operations?  Ada has an address 
>> type applicable to function and task type addresses but this is 
>> implementation dependent and there is no defined typechecking mechanism 
>> for the subprogram parameter structure as there is with procedural 
>> variables.  Unbound pointers to functions have the same consequences.  
>> While tasks can allow procedural abstraction, better is a higher order 
>> construct such as procedural variables.
>
>     I have described in earlier articles mechanisms by which the
>     desired effects can be achieved in a more intuitive manner.
>
>     Please provide a concrete example in which the provided
>     mechanisms would prove inadequate.

Ok.  Here is an (uncommented fast prototype) example which could easily 
apply to Ada Adts as well.  This is similar to a project I once worked 
on here and similar constructs have appeared throughout the literature.
This does not however, give away any trade secrets.  There are many 
possibilities such as type inference of the generic parameters (ML), 
full static declarations of generics (current Ada), or dynamic checking 
via arbitrary polymorphism but these are superfluous to procedural 
variables.  For the Ada-9X extension, generic declarations would be used.
Now, ignoring interface boundaries below, SHORT_PRINT and LONG_PRINT can 
be statically typechecked with the declaration of PRINT_ELEMENT, a 
procedural variable.  For an Ada example, parameterize the visible 
operations with a stack type and add the usual interfaces and declarations.

--=========================================================================
-- class STACK
--
--    Provides a simple example of procedural variables.
--=========================================================================
class STACK (ELEMENT_TYPE, FCFS, LCLS, SHORT_PRINT, LONG_PRINT) is

  procedure INSERT (ELEMENT : ELEMENT_TYPE); -- INSERT (ELEMENT : polymorphic);
  procedure POP () return ELEMENT_TYPE;
  procedure MAKE_SHORT_PRINT ();
  procedure MAKE_LONG_PRINT ();
  procedure PRINT ();
  procedure MAKE_fCFS ()...
    ...

on entry  ...   -- called at block entry.
on exit   ...   -- called at block exit.
exception ...
private   ...
end STACK;

class body STACK is
    --
    -- PRINT_PROCEDURE_TYPE
    --
    --    A procedural type used to implement the print operation.
    --    These could be in the private part of the class.
    --
    type  PRINT_PROCEDURE_TYPE is procedure (ELEMENT : ELEMENT_TYPE) 
                                          := SHORT_PRINT;
    -- 
    -- PRINT_ELEMENT
    --
    --  A procedural variable invoked by the PRINT operation.
    --
    PRINT_ELEMENT            : PRINT_PROCEDURE_TYPE;

    type INTERNAL_STACK_TYPE is
                record
                        BODY : array ... of ELEMENT_TYPE;
                        TOP  : NATURAL;
                end record;

    INTERNAL_STACK           : INTERNAL_STACK_TYPE;

procedure PRINT is
    begin
        -- 
        -- For each element in the stack
        --      print element
        -- 
        for  INDEX in INTERNAL_STACK .BODY'FIRST .. INTERNAL_STACK .TOP  loop
            PRINT_ELEMENT (INTERNAL_STACK .BODY (INDEX));
        end loop;
    end insert;

--
-- MAKE_SHORT_PRINT
--
--    Set procedural instance variable PRINT_ELEMENT to parameter 
--    SHORT_PRINT so subsequent calls to PRINT by this instance 
--    will invoke SHORT_PRINT.   Could also use a generic 
--    subprogram parameter to supply a new print routine, or ...
--    Parameters SHORT_PRINT and LONG_PRINT will have to be assignment
--    compatible with type PRINT_PROCEDURE_TYPE;
--
procedure MAKE_SHORT_PRINT is
    begin
        PRINT_ELEMENT := SHORT_PRINT;
    end MAKE_SHORT_PRINT;

procedure MAKE_LONG_PRINT is
    begin
        PRINT_ELEMENT := LONG_PRINT;
    end MAKE_SHORT_PRINT;

...
end STACK;


Now, a call to print an instance of this class will at first result in 
invocation of the SHORT_PRINT parameter.  But programmers can make
subsequent calls to PRINT for this instance use long_print
through a call to MAKE_LONG_PRINT.  Each instance can parameterize
its print subprogram to output its data as desired.   Other tricks 
could be used but this method avoids the use of case statements and 
is a simple use of procedural variables.  It also provides static 
typechecking of the print operation since only procedures with the 
same parameter structure as PRINT_ELEMENT will match.

In more complicated examples parameterizable operations for each Adt 
instance could simplify the program considerably, such as by avoiding
nested case statements.

>>  Now set up your abstraction to provide a "current node" concept,
>>  along with operations like DESCEND_LEFT, DESCEND_RIGHT, and ASCEND.
>>  This can be implemented either via parent pointers or via a secret
>>  stack which holds the path of descent used to reach the current node.
> 
>> Yes this would work, but leaves programmers to build their own traversal 
>> routines.  My idea was to provide a mechanism allowing programmers to specify
>> a procedural invocation in a single, simple statement and have the Adt
>> select and carry out the operation implicitly.
>
>   But wasn't the basis of your argument the claim that the usual
>   preorder, postorder, and inorder traversal schemes may not be
>   enough?  The solution to this question is clearly to proceed
>   as I have described.  Perhaps you could clarify the problem
>   under consideration and the precise nature of your solution...
>

I hope the example above clarified the idea.  On the subject of
classes, another construct to change operations could be invented but
procedural variables provide a simple mechanism.


                                    Bob Hathaway
                                    rjh@purdue.edu 

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/09/89)

From article <5796@medusa.cs.purdue.edu>, by rjh@cs.purdue.EDU (Bob Hathaway):
>> [Discussion of unrestricted pointers]
> 
> Do you mean passing unrestricted pointers to procedures and then 
> selecting the procedure via overloading (ad-hoc polymorphism) at runtime?
> I meant allowing polymorphic parameters to a particular subprogram, 
> such as
>         procedure test (parameter : polymorphic; ...) is ...

    Yes, that's what I had in mind, although I would also provide
    
          procedure INSERT_ITEM (TARGETED_STACK : in out STACK;
                                 NEW_ITEM       : in     OBJECT);

    to avoid making the user set up a pointer just to get a parameter passed.

    The resulting situation would be the same as if an unrestricted pointer
    had been used and POINTER.all had been made to serve the same function.
 
>  [An extended example of toggling between two print procedures]
>
>   procedure MAKE_SHORT_PRINT ();    -- sets PRINT up to call SHORT_PRINT,
>                                     --   a user-supplied generic parameter 
>
>   procedure MAKE_LONG_PRINT ();     -- sets PRINT up to call LONG_PRINT,
>                                     --   a user-supplied generic parameter 
>
>   procedure PRINT ();               -- a service provided to the user  

> Now, a call to print an instance of this class will at first result in 
> invocation of the SHORT_PRINT parameter.  But programmers can make
> subsequent calls to PRINT for this instance use long_print
> through a call to MAKE_LONG_PRINT.  Each instance can parameterize
> its print subprogram to output its data as desired.   Other tricks 
> could be used but this method avoids the use of case statements and 
> is a simple use of procedural variables.  

> In more complicated examples parameterizable operations for each Adt 
> instance could simplify the program considerably, such as by avoiding
> nested case statements.

     But you've designed your ADT to handle only two types of printing
     situations: SHORT_PRINT and LONG_PRINT (or at least this would seem
     to be the case to anyone who takes your generic parameter structure
     at face value).  What we REALLY want is for ANY type of printing
     operation to occur, depending on the user's needs, which we cannot
     forecast in advance.  But now look at the trap we've walked into:
     we must provide MAKE_*_PRINT, where * represents an indefinite set
     of cases.  We now see that our old friend, the case statement, is
     not gone; it has simply taken on a new form. 

     Verdict: a bad ADT design idea.

     Now let us step back for a moment and consider our situation.  We
     wish to accept a PRINT procedure from our user, which we will use 
     in the course of printing out the contents of the stack.  But how
     do we know that the method we choose for encapsulating the stacked
     objects in the printout will make our user happy?  Perhaps our idea
     is to print each object, separated by a blank line, but our user
     wants to see each object surrounded by a rectangular box of :-)s...

     We now see that our entire approach to the problem is wrong.  We must
     provide a method whereby our user can access his/her stacked objects,
     and allow the user to implement any arbitrary print procedure.  But,
     you object, the user must be given some sort of standard print procedure
     which can be used as a default!  And so he/she must; we achieve this
     by supplying in a utilities package the following:

         with TEXT_IO;

         generic  
 
            type DATA_STRUCTURE is limited private;

            type STORED_OBJECT is limited private;

            with procedure NEXT_POSITION (STRUCTURE : in out DATA_STRUCTURE);

            with function GET_CURRENT_ITEM 
                                  (STRUCTURE : in DATA_STRUCTURE)
                                                       return STORED_OBJECT;

            with procedure PRINT (TO_FILE  : in TEXT_IO.FILE_TYPE;
                                  THE_ITEM : in STORED_OBJECT    );

         procedure STANDARD_PRINT_ROUTINE (INTO_FILE : in TEXT_IO.FILE_TYPE;
                                           STRUCTURE : in DATA_STRUCTURE);

     Our user is now provided with maximal power and convenience.

     Note that the above utility applies to any class of structures
     for which the user can construct a procedure to sequentially 
     access every stored object in some user-defined order.

     Still waiting for a situation in which procedural variables should 
     be used in order to formulate an intuitively natural solution...



                                            Bill Wolfe

                                     wtwolfe@hubcap.clemson.edu
 

rjh@cs.purdue.EDU (Bob Hathaway) (01/10/89)

> In more complicated examples parameterizable operations for each Adt 
> instance could simplify the program considerably, such as by avoiding
> nested case statements.
>
Bill Wolfe responds:
>     But you've designed your ADT to handle only two types of printing
>     situations: SHORT_PRINT and LONG_PRINT (or at least this would seem
>     to be the case to anyone who takes your generic parameter structure
>     at face value).  What we REALLY want is for ANY type of printing
>     operation to occur, depending on the user's needs, which we cannot
>     forecast in advance.  But now look at the trap we've walked into:
>     we must provide MAKE_*_PRINT, where * represents an indefinite set
>     of cases.  We now see that our old friend, the case statement, is
>     not gone; it has simply taken on a new form. 
>

This is not entirely correct, since I commented another more general solution.
It is possible to parameterize the Adt with a generic MAKE_PRINT_PROCEDURE
as described in the comment which I'll repeat below.  This is accomplished
with a nested generic subprogram declaration in the visible part of the 
package specification allowing any visible type compatible procedure
to be passed to a "MAKE_PRINT_PROCEDURE".  This satisfies the constraint 
above by dynamically assigning the generic parameter to the internal 
procedural variable, if desired.
--
-- MAKE_SHORT_PRINT
--
--    Set procedural instance variable PRINT_ELEMENT to parameter
--    SHORT_PRINT so subsequent calls to PRINT by this instance
** Please Note ***
--    will invoke SHORT_PRINT.   Could also use a generic
--    subprogram parameter to supply a new print routine, or ...
***           ***
--    Parameters SHORT_PRINT and LONG_PRINT will have to be assignment
--    compatible with type PRINT_PROCEDURE_TYPE;
--

Also, it appears unrestricted pointers to subprograms, if thats what
you propose, would provide dynamically checked operations at runtime.
This mechanism is more powerful than statically checked procedural
variables.  Analogous to the use of generics and arbitrary polymorphism
where generics should be used whenever applicable and arbitrary polymorphism
with care, procedural variables should be used whenever applicable and
unrestricted pointers with care.  Since the previous example used generics,
corresponding procedural variables are applicable and provide an
elegant general solution.  I'll let Harland, if we're lucky enough to
have him read this, provide an example with arbitrary polymorphism
and unrestricted pointers:-)

Mats Weber writes:
>I think there is one good reason why procedure types should not be included in
>the Ada language: A secure implementation of procedure types must restrict
>their use to global procedures (that is, procedures that are not nested in
>other procedures or tasks), as is the case in Modula-2.
>
>Such a rule would violate the uniformity of the language (in Ada 83, anything
>can be declared in any declarative region, no matter how deeply nested the
>region is).

I'm aware of this argument but after several years of Modula-2 programming
this simply doesn't cause concern.   This is like the "else" construct
argument where disambiguating rules must be applied to allow if-then-else
constructs.  It is not consequential that rules must be applied to 
grammars, as with the else construct, or that semantic rules must be applied
to objects, as with procedural variables.  As a programmer, the only 
procedures I would assign to a procedural variable are top-level procedures
and since the nested case doesn't occur, I don't see a problem.  I agree 
constraints restricting orthogonality should only be applied when necessary 
but procedural variables present such a case in a stack based-model since
assignment could allow access to inactive environments global to a procedure
and the assignment restriction to top-level procedures is safe, appropriate,
and general.

Also, the rule for disallowing procedural variable assignment to nested 
subprograms which are passed as generic subprogram parameters must still 
be statically checked.

Bill Wolfe writes:
>     We now see that our entire approach to the problem is wrong.  We must
>     provide a method whereby our user can access his/her stacked objects,
>     and allow the user to implement any arbitrary print procedure.  But,
>     you object, the user must be given some sort of standard print procedure
>     which can be used as a default!  And so he/she must; we achieve this
>     by supplying in a utilities package the following:
>

But this is the old scheme again since the Adt cannot be parameterized at
any point in the program and allow a single subsequent operation to invoke 
the desired subprogram.  This sheme requires the work of writing
an output procedure inline.  Yes, the user can call MY_PRINT (STACK .POP ...)
for maximum flexibility as they can with the presented example but
this was not desired.  The output example was kept simple to abstract the 
desired method and real applications are easy to construct, as shown 
below.


>     Still waiting for a situation in which procedural variables should 
>     be used in order to formulate an intuitively natural solution...

Graphical environments provide one example where dynamically parameterizable
objects can be of great assistance.  One could parameterize graphical objects 
with operations for displaying output, windows, etc., with each instance using
the correct subprogram to implement its display operations.

                                            Bob Hathaway
                                            rjh@purdue.edu 

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/10/89)

From article <5804@medusa.cs.purdue.edu>, by rjh@cs.purdue.EDU (Bob Hathaway):
>> In more complicated examples parameterizable operations for each Adt 
>> instance could simplify the program considerably, such as by avoiding
>> nested case statements.
>>
>>> [...] we must provide MAKE_*_PRINT, where * represents an indefinite set
>>> of cases.  We now see that our old friend, the case statement, is
>>> not gone; it has simply taken on a new form. 
> 
> This is not entirely correct, since I commented another more general solution.

     Then let's have a concrete example of this other, more general solution.

     Be generous with the comments.

% Also, it appears unrestricted pointers to subprograms, if thats what
% you propose, would provide dynamically checked operations at runtime.
% This mechanism is more powerful than statically checked procedural
% variables.  

     No, I haven't yet been convinced that subprograms should constitute
     a separate type; my current position is that subprograms should only
     be a means by which ADTs and tasks (think of a "main program" as an
     isolated task...) can be specified.  If you can convince us that there
     is a legitimate need for a subprogram type, then variables of the type
     and pointers to objects of the type will follow automatically.  

> Mats Weber writes:
>>I think there is one good reason why procedure types should not be included in
>>the Ada language: A secure implementation of procedure types must restrict
>>their use to global procedures (that is, procedures that are not nested in
>>other procedures or tasks), as is the case in Modula-2.

     Quick question: assuming the idea of a specification is expanded to
     include all externally accessible objects, what is the source of
     the insecurity?

% Graphical environments provide one example where dynamically parameterizable
% objects can be of great assistance.  One could parameterize graphical objects 
% with operations for displaying output, windows, etc., with each instance using
% the correct subprogram to implement its display operations.

     Specific examples, please...



                                           Bill Wolfe

                                    wtwolfe@hubcap.clemson.edu

rjh@cs.purdue.EDU (Bob Hathaway) (01/10/89)

Another more direct way to parameterize a change operation is
to use a procedural variable as a parameter.  Such as:

generic
    type ELEMENT_TYPE is private;
package ...
    type PRINT_PROCEDURE is procedure (ELEMENT : in ELEMENT_TYPE);
    procedure CHANGE_PRINT_OPERATION (PRINT_OPERATION : in PRINT_PROCEDURE);

This allows dynamic specification of statically checked print procedures per 
instance while the previous example assumed the user knew all allowable 
procedures during instantiation.

					 Bob Hathaway
                                         rjh@purdue.edu

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/11/89)

From article <5806@medusa.cs.purdue.edu>, by rjh@cs.purdue.EDU (Bob Hathaway):
> 
> Another more direct way to parameterize a change operation is
> to use a procedural variable as a parameter.  Such as:
> 
> generic
>     type ELEMENT_TYPE is private;
> package ...
>     type PRINT_PROCEDURE is procedure (ELEMENT : in ELEMENT_TYPE);
>     procedure CHANGE_PRINT_OPERATION (PRINT_OPERATION : in PRINT_PROCEDURE);
> 
> This allows dynamic specification of statically checked print procedures per 
> instance while the previous example assumed the user knew all allowable 
> procedures during instantiation.

    But now we have a highly changeable system.  What I am asking is,
    "Is there any reason we need this extremely high level of changeability?"

    We are essentially getting into the realm of programs which modify
    themselves, and I tend to think that this power requires extraordinary
    regulation due to the ease with which such a program could modify itself
    to the point that nobody can figure out how it got there in less than
    omega (Ackermann's function) time, much less how to fix it.

    I've suggested earlier that programs should theoretically be able to
    edit source files, cause a recompilation, dump their local knowledge
    into a transition file, invoke the successor on the local knowledge,
    and finally commit suicide, but that this should be done with extreme
    caution. 

    Is there any reason why we need procedural variables in addition to this?



                                           Bill Wolfe

                                    wtwolfe@hubcap.clemson.edu

rjh@cs.purdue.EDU (Bob Hathaway) (01/11/89)

> Another more direct way to parameterize a change operation is
> to use a procedural variable as a parameter.  Such as:
> 
> generic
>     type ELEMENT_TYPE is private;
> package ...
>     type PRINT_PROCEDURE is procedure (ELEMENT : in ELEMENT_TYPE);
>     procedure CHANGE_PRINT_OPERATION (PRINT_OPERATION : in PRINT_PROCEDURE);
> 
> This allows dynamic specification of statically checked print procedures per 
> instance while the previous example assumed the user knew all allowable 
> procedures during instantiation.

>    But now we have a highly changeable system.  What I am asking is,
>    "Is there any reason we need this extremely high level of changeability?"
>

I think the answer is yes, and I'll provide a summary of the method under
discussion instead of another example.  As I mentioned to Bill about a month
ago, I have been working on a generic class structure for about a year.
Other simpler languages have emerged and provided classes but I am not aware
of any extending the idea with generics.  If Ada is to continue remaining
viable as a state-of-the-art programming language then incremental extensions
such as procedural variables and generic classes are necessary.  Ada is often
considered a general purpose programming language, a systems programming 
language, and an object-oriented programming language and its use has far
surpassed its original intention.  Grady Booch has presented an elegant
object-oriented design strategy for Ada and since most programmers are
using this style of programming, Ada should be extended to provide first
class constructs supporting this methodology.  Classes provide an incremental
improvement in abstract data types and together with procedural variables
they provide an elegant solution to parameterized operations.  I owe some
credit to Chris Torek, who posted a method for "the right way to do object
oriented programming in C" (his words as I remember) by storing data and
operations together.  I extended his idea with dynamically parameterized
operations in Adts as the need arose and am sure one "right way to do this"
is in Ada with generics.  Again, the presented example of procedural 
variables allows abstract data types to dynamically (at runtime) alter 
thier operations so each instance can be individually parameterized with
an appropriate subprogram to perform that operation.  Previous postings
presented other uses as well.

The first posted example used type inference and provided a method
to specify all allowable subprogram invocations per instance during 
instantiation.  This method insures controlled operations during and
after instantiation and the Adt state will not be hard to trace.  

The second method allows Adt operations to be dynamically parameterized during
and after instantiation and can be used as the need arises.  A history can be
kept within the Adt for mission critical applications or complex debugging.
I will let the method stand on its own merits and it has been my experience
that parameterized operations are frequently an appropriate solution.  Here
is a simple example of parameterization via procedural variables using a 
single operation Adt:

generic 
   type ELEMENT_TYPE is limited private;
package STACK is
  type ADT is limited private;
  type OPERATION_TYPE is procedure (ELEMENT : in out ELEMENT_TYPE);
  procedure OPERATION (INSTANCE : in out ADT; ELEMENT : in out ELEMENT_TYPE);
  procedure CHANGE_OPERATION (INSTANCE      : in out ADT; 
                              NEW_OPERATION : in OPERATION_TYPE);
end STACK;

or

generic 
   type ELEMENT_TYPE is limited private;
package STACK is
  type ADT is limited private;
  procedure OPERATION (INSTANCE : in out ADT; ELEMENT : in out ELEMENT_TYPE);
  generic
	with procedure NEW_OPERATION (ELEMENT : in out ELEMENT_TYPE);
  procedure CHANGE_OPERATION (INSTANCE : in out ADT);
end STACK;

Here is a recommended class based approach:

generic 
    type ELEMENT_TYPE is limited private;
class
  type OPERATION_TYPE is procedure (ELEMENT : in out ELEMENT_TYPE);
  procedure OPERATION (ELEMENT : in out ELEMENT_TYPE);
  procedure CHANGE_OPERATION (NEW_OPERATION : in OPERATION_TYPE);
  procedure INITIALIZE;	-- These can be dynamically parameterized, if desired
  procedure FINISH;     -- or declared in the private part.
  -- exception declarations --
private
  pragma  NO_ENTRY;	-- If Bill wants to call these explicitly
  pragma  NO_EXIT;
  on entry  INITIALIZE;
  on exit   FINISH;
end STACK;

The reader is referred to previous articles for discussions on more powerful
parameter passing mechanisms which require a runtime type system.  Here is
an example within a class construct:

class STACK is
  type OPERATION_TYPE is procedure (ELEMENT : polymorphic);
  procedure OPERATION (ELEMENT : in polymorphic);
  procedure CHANGE_OPERATION (NEW_OPERATION : OPERATION_TYPE);
  function  GET_OBJECT return polymorphic;
end STACK;

class body STACK
	-- see previous class example.
end STACK;

And here is an example of Bill's proposal with unrestricted pointers
(*If* unrestricted pointers can to apply to subprograms):

class STACK is
  type OPERATION_TYPE is procedure;
  procedure OPERATION (ELEMENT : in polymorphic;...);
  procedure CHANGE_OPERATION (NEW_OPERATION : OPERATION_TYPE);
  function  GET_OBJECT return polymorphic;
end STACK;

But this last method requires considerable thought.  I'll assume any procedure
can be passed to CHANGE_OPERATION and calls to OPERATION will require
runtime elaboration.  All packages assign the new operation to an internal 
procedural variable.

As shown above, generics co-exist with procedural variables.  Another issue
is default parameters; what if the supplied procedure doesn't have the same
default parameters?  The simplist solution is to enforce a new rule, such
as during assignment of a subprogram to a procedural variable any default
parameters in the procedural variables parameter structure must be matched
by corresponding default parameters in the supplied subprogram.  But for now
I'll assume assignment is legal as long as subsequent invocations of the
procedural variable don't reference default parameters not supplied by the
subprogram.  Such cases should be avoided, however.

I will let the idea stand on its own merits for now since there is time for
consideration before the next extension.  Is there any way to get
feedback or proposal information for the Ada 9X extension?  I think there
have been several worthwhile ideas presented so far.


                                              Bob Hathaway
                                              rjh@apurdue.edu

rjh@cs.purdue.EDU (Bob Hathaway) (01/11/89)

>    We are essentially getting into the realm of programs which modify
>    themselves, and I tend to think that this power requires extraordinary
>    regulation due to the ease with which such a program could modify itself
>    to the point that nobody can figure out how it got there in less than
>    omega (Ackermann's function) time, much less how to fix it.
>
>    I've suggested earlier that programs should theoretically be able to
>    edit source files, cause a recompilation, dump their local knowledge
>    into a transition file, invoke the successor on the local knowledge,
>    and finally commit suicide, but that this should be done with extreme
>    caution. 
>
>    Is there any reason why we need procedural variables in addition to this?

This method is not unusual and can be accomplished by providing an operating 
system dependent package with the necessary operations.  Your suggestion 
would move this operation into the higher-order domain and could
be useful in AI and program generation applications.  Retargetable compilers 
are a case in point.

And, speaking of letting proposals stand on their own merits, could we please
put the garbage collection issue to rest.  Each party could state a final
summary of their position, and leave time for consideration before Ada 9X.

                                          Thanks,
                                              Bob Hathaway
                                              rjh@purdue.edu

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/11/89)

From article <5816@medusa.cs.purdue.edu>, by rjh@cs.purdue.EDU (Bob Hathaway):
> [much verbiage deleted...] 
> 
> class STACK is
>   type OPERATION_TYPE is procedure;
>   procedure OPERATION (ELEMENT : in polymorphic;...);
>   procedure CHANGE_OPERATION (NEW_OPERATION : OPERATION_TYPE);
>   function  GET_OBJECT return polymorphic;
> end STACK;

    First, let's clarify the terminology: a STACK is an example of a type.
    A class is a group of related types.  Thus, APPLE and ORANGE are types,
    and FRUIT is a class of types.  OBJECT is the class of all possible types.

    Despite the request that you be generous with the comments, no indication
    was given as to what OPERATION has to do with the stack.  Let's assume
    that it is some operation that one wishes to apply to every item in the
    stack.  Then we encounter problems in that OPERATION has only an "in"
    parameter, and therefore cannot modify its parameter.  But we'll overlook
    this too, and ask: If we're looking at objects which need to be modified,
    1) what are they doing hidden away inside a stack?  2) why can't we just
    have a stack of pointers, and write a procedure which will serially read
    the stacked objects and update what they point to? and 3) what if the user
    wants to keep N different operations which could be applied to all items
    in the stack, without the bother of continuously invoking CHANGE_OPERATION?

    The mechanism whereby a program could edit source files, cause a rec
    recompilation, transfer its local knowledge to its successor, and
    then destroy itself is appropriate to an environment where code
    modifications are infrequent; the system transitions from one well-defined
    state to another, and the source code always represents the current
    state of the system.  Additionally, optimized code can be generated.
    Procedural variables would encourage excessive rates of change, and
    would greatly complicate the problem of code optimization.

    The idea of modifying the display routine of a graphical object 
    through procedural variables has no merit.  What should be modified
    is a standardized data structure such as a bit-map or list of endpoints.
    This is analogous to the inappropriate use of recursion (vs. iteration).

> If Ada is to continue remaining viable as a state-of-the-art programming
> language then incremental extensions such as procedural variables [...]

    Ada is a state-of-the-art applications programming language, not
    a state-of-the-art research language.  Perhaps a more appropriate
    approach to procedural variables would be to incorporate them into
    a research language and look for a) efficient implementations,
    including code optimization, and b) realistic situations in which 
    the use of procedural variables is effectively not optional.   



                                            Bill Wolfe

                                     wtwolfe@hubcap.clemson.edu

barmar@think.COM (Barry Margolin) (01/12/89)

In article <4062@hubcap.UUCP> wtwolfe@hubcap.clemson.edu writes:
>    Ada is a state-of-the-art applications programming language, not
>    a state-of-the-art research language.  Perhaps a more appropriate
>    approach to procedural variables would be to incorporate them into
>    a research language and look for a) efficient implementations,
>    including code optimization, and b) realistic situations in which 
>    the use of procedural variables is effectively not optional.   

I'm really beginning to wonder what universe you're coming from. :-)

Procedure variables are hardly a research topic these days.  They've
been in production use for decades.  And I can't think of any reason
why any decent compiler implementor wouldn't be able to implement them
efficiently (the only inefficiency I know of is minor - procedures
assigned to variables must get their own stack frame, rather than
being able to share its parent block's stack frame - and does not
exist if procedure assignment is restricted to top-level procedures).

They've been in use in the Lisp programming world for years.  They are
great for AI applications in which objects must have highly variable
active properties.  Simple object-oriented systems can be implemented
using either "upward closures" (functions that remember the lexical
environment from which they were returned) or structures that contain
data and a procedure object.

They are used quite effectively on Multics, which has extended PL/I to
support procedure variables.  The most obvious use is in the
device-independent I/O library.  The structure that represents an I/O
stream mostly contains procedure objects that implement the
device-specific portion of each I/O operation.  When you call the
device-independent routines they dispatch through the appropriate
component of the stream structure.  The components that represent
operations that are not appropriate (either because of the type of
device or the state of the stream) can be filled in with procedures
that return appropriate error indications.  But all of the above can
be done with extra flags in the structure and CASE statements.  Where
it really comes in handy is when you allow new device drivers to be
linked in on the fly (Multics has been doing dynamic linking for over
twenty years, and there's a system call to explicitly invoke the
dynamic linking mechanism and return a procedure object).

I guess this doesn't provide any more fundamental capabilities than
the runtime recompilation mechanism you've mentioned.  But I sure
wouldn't want to use a system that implemented dynamic linking by
automatic editing, recompilation, linking and invocation of the
application.  True dynamic linking is often criticized as being too
slow, but it must be several orders of magnitude than the most
well-optimized recompilation scheme.

Algol-68 (or was it Algol-60?) had procedure variables, at least in
the form of call-by-name parameters.  And there are many good uses for
them in C, such as the parameter to the signal() subroutine, which
associates a procedure with a software interrupt.

I could probably go on and on.  Procedure variables are an elegant way
to reduce the complexity of an enormous number of programs.  When I
first saw Ada I noticed two things: 1) the syntax is excessively
verbose and contorted (I really hate the way the verb "IS" is used,
for some reason even I don't understand), and 2) no procedure
variables.


Barry Margolin
Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/12/89)

From article <35339@think.UUCP>, by barmar@think.COM (Barry Margolin):
> Procedure variables are hardly a research topic these days.  They've
> been in production use for decades.  

    The Rationale for the Design of Ada lists it as a research topic
    which was not sufficiently well-understood at the time (particularly
    in terms of efficient implementations) to be acceptable for inclusion.

    Additionally, Paul N. Hilfinger, one of the designers of Ada, discussed
    procedural variables in his ACM Distinguished Dissertation; last I
    heard, dissertations were considered research.

    I haven't done an extended investigation of the literature, but
    procedural variables in Algol-family languages would appear to
    be a research topic, or at least would seem to have recently
    been such a topic.


                                        Bill Wolfe

                                 wtwolfe@hubcap.clemson.edu
 

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/12/89)

From article <35339@think.UUCP>, by barmar@think.COM (Barry Margolin):
> 
> [Procedural variables] are great for AI applications in which objects 
> must have highly variable active properties.  

     Could you expand on these situations?  I'll be happy to support
     procedural variables if you can describe a general class of applications
     in which they are intuitively natural and necessary...



                                       Bill Wolfe

                                wtwolfe@hubcap.clemson.edu

barmar@think.COM (Barry Margolin) (01/12/89)

In article <4072@hubcap.UUCP> billwolf@hubcap.clemson.edu writes:
>From article <35339@think.UUCP>, by barmar@think.COM (Barry Margolin):
>> [Procedural variables] are great for AI applications in which objects 
>> must have highly variable active properties.  
>     Could you expand on these situations?  I'll be happy to support
>     procedural variables if you can describe a general class of applications
>     in which they are intuitively natural and necessary...

Unfortunately, I'm not an AI programmer, so I don't have concrete
examples.  I merely imagined that paradigms such as message-passing
and actors would be implemented using procedure objects.

Barry Margolin
Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

firth@sei.cmu.edu (Robert Firth) (01/12/89)

In article <4071@hubcap.UUCP> billwolf@hubcap.clemson.edu writes:

>    I haven't done an extended investigation of the literature, but
>    procedural variables in Algol-family languages would appear to
>    be a research topic

I hardly think so.  Procedure variables have been around for over
20 years, and the implementation issues are pretty well understood.
As for Algol, when I had to write an Algol-60 compiler in the late
'70s, the only "research" required was reading the literature from
the early '60s that told me how to do it.  My most valued source
was the early volumes of "Annual Reviews in Automatic Programming",
supplemented by Hopgood's old monograph "Compiler Construction".

There is just one problem with procxedure variables that has not
quite been completely solved.  It is illustrated by this:

	type proc_type is procedure;

	procvar : proc_type;

	procedure outer is
	  X : integer;

	  procedure inner;
	  begin
	    X := X+1;
	  end;

	begin
	  procvar := inner;
	end;

	outer;
	procvar;

The final statement invokes the value of procvar, which is, of course,
"inner".  The call of inner references the variable X declared in outer,
which no longer exists.  This is the equivalent of the "dangling reference"
problem when the address of a local variable is assigned to a global
pointer.

Many solutions have been proposed; the cleanest in my opinion is that of
Modula-2, which does not allow an "inner" procedure to be assigned to a
variable.  If that were combined with reasonable visibility rules (so
that a procedure could be unnested without perforce being made visible),
I think it would be a definitive solution.

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/13/89)

From article <8178@aw.sei.cmu.edu>, by firth@sei.cmu.edu (Robert Firth):
> There is just one problem with procxedure variables that has not
> quite been completely solved.  It is illustrated by this:
> 
> 	type proc_type is procedure;
> 
> 	procvar : proc_type;
> 
> 	procedure outer is
> 	  X : integer;
> 
> 	  procedure inner;
> 	  begin
> 	    X := X+1;
% 	  end;
% 
% 	begin
% 	  procvar := inner;
% 	end;
% 
% 	outer;
% 	procvar;
% 
% The final statement invokes the value of procvar, which is, of course,
% "inner".  The call of inner references the variable X declared in outer,
% which no longer exists.  This is the equivalent of the "dangling reference"
% problem when the address of a local variable is assigned to a global
% pointer.
% 
% Many solutions have been proposed; the cleanest in my opinion is that of
% Modula-2, which does not allow an "inner" procedure to be assigned to a
% variable.  If that were combined with reasonable visibility rules (so
% that a procedure could be unnested without perforce being made visible),
% I think it would be a definitive solution.

     Why not something along the lines of the "restricted" clause present
     in Preliminary Ada?  Then inner's specification would require that
     an integer named X be visible in the environment; the attempt to call
     procvar would then result in an error which would be similar to 
     a "missing parameters" situation.  This would have the added benefit
     of completing the idea of a specification such that ALL interactions
     between a procedure and its environment are fully documented (and
     compiler-enforced) in the specification, considerably simplifying
     program debugging and maintenance.

     Also, since Barry was unable to give an answer, for what situations
     would the use of procedural variables be intuitively natural and
     necessary?   Just some example of a realistic applications program
     for which current Ada would prove inadequate, some motivating reason
     for wanting to have procedural variables which would justify such
     a change.

     By the way, would this extend also to packages?  Package variables? 



                                        Bill Wolfe

                                 wtwolfe@hubcap.clemson.edu

scott@shuksan.UUCP (Scott Moody) (01/14/89)

> 
> >    I haven't done an extended investigation of the literature, but
> >    procedural variables in Algol-family languages would appear to
> >    be a research topic
>  <code not included> 
> 
> The final statement invokes the value of procvar, which is, of course,
> "inner".  The call of inner references the variable X declared in outer,
> which no longer exists.  This is the equivalent of the "dangling reference"
> problem when the address of a local variable is assigned to a global
> pointer.
> 

The C language solves this problem by not allowing 'inner' procedures at all.
Of course in C any address could be used and called, but it might not
point to anything meaningful.

I would thing the bigger problems with procedure parameters is in the 
parameters to those procedures; especially in Ada where there might be
overloaded procedures with very complex arguments. The syntax for 
specifying - and then checking at runtime that the user supplies
the arguments correctly can get very complex. 

I think Ada's  solution of requiring procedures to be supplied
to generics is a good compromise between allowing a users code to suddenly
jump to somewhere (hopefully not hitting any stars), and not having
them at all. I for one wouldn't mind procedure parameters and varaiables, 
ala C, but I can see that they go against the non goto rational behind Ada.

-- scott

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (01/16/89)

From article <1089@shuksan.UUCP>, by scott@shuksan.UUCP (Scott Moody):
> I for one wouldn't mind procedure parameters and varaiables, 
> ala C, but I can see that they go against the non goto rational behind Ada.

   Speaking of the non-goto rationale behind Ada, can anyone tell me
   why Ada has a goto statement??  (See LRM 5.9...)  The Rationale for 
   the Design of Ada conveniently fails to discuss it.


                                          Bill Wolfe

                                   wtwolfe@hubcap.clemson.edu

stachour@umn-cs.CS.UMN.EDU (Paul Stachour) (01/24/89)

In article <1089@shuksan.UUCP> scott@shuksan.UUCP (Scott Moody) writes:
>> 
>> >    I haven't done an extended investigation of the literature, but
>> >    procedural variables in Algol-family languages would appear to
>> >    be a research topic
>>  <code not included> 
>> 
>> The final statement invokes the value of procvar, which is, of course,
>> "inner".  The call of inner references the variable X declared in outer,
>> which no longer exists.  This is the equivalent of the "dangling reference"
>> problem when the address of a local variable is assigned to a global
>> pointer.
>> 
>
In the Multics Implementation of PL/I, an procedure variable was
(not unsuprisingly) two items.  One was a pointer to the code
which was the procedure.  The 2nd was the "environment" of the
procedure at the point of the assignment.

Note that this allows:
   1) The code will thus update the correct "X" at any time,
      even when called from a highly-nested point, as it can find
      its "stack" and thus the "right x" to update.
   2) The code given in its original form to "detect" that there
      is no-such "X" to update, since there is no longer the
      version of "inner" that is "procvar" on the stack.  Detection
      of this stack-mismatch is easy in the example given, it is
      merely the fact that the stack is now "below" what it was at
      the point when the assignment "had effect".

  Of course, in the general case, where there may have been many calls
      in between, such a simple check would not work.  However, the simple
      matter of "gravestones", as used to detect dangling references,
      could be used with procedure-variables just as with naturals,
      or strings, or ...    ...Paul