Version 1.2 of ai12s/ai12-0268-1.txt

Unformatted version of ai12s/ai12-0268-1.txt version 1.2
Other versions for file ai12s/ai12-0268-1.txt

!standard 5.5.2(2/3)          18-03-29 AI12-0268-1/01
!standard 5.5.2(5/4)
!standard 5.5.2(7/3)
!class Amendment 18-03-29
!status work item 18-03-29
!status received 18-03-22
!priority Medium
!difficulty Medium
!subject Automatic instantiation for generic formal parameters
!summary
Automatic instantiation is added to generic formal parameters.
!problem
Every Ada generic instantiation needs to be named and declared. When an instantiation is created solely to be passed to another generic instantiation, this requires picking a name for an instantiation that isn't intended to be visible to any other code.
!proposal
We can have default generic-parameters which are automatically instantiated.
The formal syntax for the above method would essentially combine the instantiation syntax into the generic formal parameter syntax after USE, thereby altering:
12.6 (2/2) — formal_subprogram_declaration ::=
formal_concrete_subprogram_declaration | formal_abstract_subprogram_declaration | formal_default_subprogram_declaration
and adding
formal_default_subprogram_declaration ::= use [overriding_indicator] procedure defining_program_unit_name is new generic_procedure_name [generic_actual_part]
[aspect_specification]; |
use [overriding_indicator] function defining_designator is
new generic_function_name [generic_actual_part] [aspect_specification];
Because, as a default itself, the inclusion of “is null”, “is <>”, and “is default_name” are meaningless. (Note: syntax taken from 12.3 (2/3), it could probably be cleaned up.)
12.7 (2/3) — formal_package_declaration ::=
with package defining_identifier is new generic_package_name
formal_package_actual_part [aspect_specification]; |
use package defining_identifier is new generic_package_name
formal_package_actual_part [aspect_specification];
Which I think would work syntactically.
!wording
** TBD.
!discussion
** TBD.
!examples
For example, given our current abilities with Ada’s generics and defaults we could say:
generic type Some_Type is private; procedure Generic_Swap( A, B : in out Some_Type );
generic type Element is private; type Index is (<>); type Array_Type is array(Index range <>) of Element; with procedure Swap(A, B : in out Element) is <>; procedure Generic_Sort( Input : in out Array_Type );
However, this requires an instantiation of Generic_Swap and results in something similar to the following:
-- Types. Type Integer_Vector is Array(Integer range <>) of Integer;
-- Instantiations. Procedure Swap is new Generic_Swap( Integer ); Procedure Sort is new Generic_Sort(
Swap => Swap, -- This line could be deleted. Element => Integer, Index => Integer, Array_Type => Integer_Vector
);
With our current defaulting methods, we could eliminate the Swap parameter of the instantiation Generic_Sort, but we have enough information in the definition of Generic_Sort that we could eliminate both it and the currently required instantiation of Generic_Swap. So, with proper syntax, we could say something like:
generic type Element is private; type Index is (<>); type Array_Type is array(Index range <>) of Element; use procedure Swap is new Generic_Swap(Some_Type => Element); procedure Generic_Sort( Input : in out Array_Type );
This also works with Packages and given the following —
generic type Element(<>) is private; package Generic_Stack is -- ... end Generic_Stack;
generic type PostScript_Object(<>) is private; type PostScript_Float is private; type PostScript_Context is private; with package Object_Stack is new Generic_Stack(PostScript_Object); with package Float_Stack is new Generic_Stack(PostScript_Float); with package Context_Stack is new Generic_Stack(PostScript_Context); package Generic_PostScript_VM is -- ... end Generic_PostScript_VM;
-- Type Stubs type PS_Object is tagged null record; type Real is new Interfaces.IEEE_Float_64; type Context is null record;
we could reduce the required instantiations of —
-- Instantiations. Package Object_Stack is new Generic_Stack(PS_Object'Class); Package Real_Stack is new Generic_Stack(Real); Package Context_Stack is new Generic_Stack(Context); Package PostScript_VM is new Generic_PostScript_VM(
PostScript_Object => PS_Object'Class, PostScript_Float => Real, PostScript_Context => Context, Object_Stack => Object_Stack, Float_Stack => Real_Stack, Context_Stack => Context_Stack
);
to: -- Instantiations. package PostScript_VM is new Generic_PostScript_VM( PostScript_Object => PS_Object'Class, PostScript_Float => Real, PostScript_Context => Context ); simply by replacing the with by use in the definition.
!ASIS
[Not sure. It seems like some new capabilities might be needed, but I didn't check - Editor.]
!ACATS test
ACATS B- and C-Tests are needed to check that the new capabilities are supported.
!appendix

From: Edward Fish
Sent: Thursday, March 22, 2018  1:48 PM

While this AI (AI12-0205-1 - ED) seems concerned with defaults of
object-parameters (particularly IN OUT), there are other advantages to 
default-parameters that can be had. In particular, we can have default 
generic-parameters which are automatically instantiated — thereby eliminating
the need for one-off instantiations which, in turn, would simplify the code of
current Ada projects (particularly at the library level*).

For example, given our current abilities with Ada’s generics and defaults we
could say:

     Generic
         Type Some_Type is private;
     Procedure Generic_Swap( A, B : in out Some_Type );

     Generic
         Type Element is private;
         Type Index is (<>);
         Type Array_Type is Array(Index Range <>) of Element;
         With Procedure Swap(A, B : in out Element) is <>;
     Procedure Generic_Sort( Input : in out Array_Type );

However, this requires an instantiation of Generic_Swap and results in
something similar to the following:

     -- Types.
     Type Integer_Vector is Array(Integer range <>) of Integer;

     -- Instantiations.
     Procedure Swap is new Generic_Swap( Integer );
     Procedure Sort is new Generic_Sort(
         Swap       => Swap, -- This line could be deleted.
         Element    => Integer,
         Index      => Integer,
         Array_Type => Integer_Vector
     );

With our current defaulting methods, we could eliminate the Swap parameter of
the instantiation Generic_Sort, but we have enough information in the
definition of Generic_Sort that we could eliminate both it and the currently
required instantiation of Generic_Swap. So, with proper syntax, we could say
something like:

     Generic
         Type Element is private;
         Type Index is (<>);
         Type Array_Type is Array(Index Range <>) of Element;
         Use Procedure Swap is new Generic_Swap(Some_Type => Element);
     Procedure Generic_Sort( Input : in out Array_Type );

The above could be said to move the instantiation into the formal-parameter
list, and is more inline with how generic packages may currently be 
linked/tied together (in eg signature-packages), and the instantiation of
Generic_Swap can now be elided into the instantiation of Generic_Sort,
cleaning out the previous namespace a bit, as well as [possibly] offering
public access to the instantiation via Generic_Sort.Swap.

This also works with Packages and given the following —

     Generic
         Type Element(<>) is private;
     Package Generic_Stack is
     -- ...
     End Generic_Stack;

     Generic
         Type PostScript_Object(<>) is private;
         Type PostScript_Float      is private;
         Type PostScript_Context    is private;
         with Package Object_Stack  is new Generic_Stack(PostScript_Object);
         with Package Float_Stack   is new Generic_Stack(PostScript_Float);
         with Package Context_Stack is new Generic_Stack(PostScript_Context);
     Package Generic_PostScript_VM is
     -- ...
     end Generic_PostScript_VM;

     -- Type Stubs
     Type PS_Object is tagged null record;
     Type Real      is new Interfaces.IEEE_Float_64;
     Type Context   is null record;

we could reduce the required instantiations of —

     -- Instantiations.
     Package Object_Stack  is new Generic_Stack(PS_Object'Class);
     Package Real_Stack    is new Generic_Stack(Real);
     Package Context_Stack is new Generic_Stack(Context);
     Package PostScript_VM is new Generic_PostScript_VM(
         PostScript_Object  => PS_Object'Class,
         PostScript_Float   => Real,
         PostScript_Context => Context,
         Object_Stack       => Object_Stack,
         Float_Stack        => Real_Stack,
         Context_Stack      => Context_Stack
     );

to:
     -- Instantiations.
     Package PostScript_VM is new Generic_PostScript_VM(
         PostScript_Object  => PS_Object'Class,
         PostScript_Float   => Real,
         PostScript_Context => Context
     );
simply by replacing the WITH by USE in the definition.

Additionally, this defaulting should obviously be able to be overridden, so 
even in the USE form the more verbose instantiations above would still be
valid.

The formal syntax for the above method would essentially combine the 
instantiation syntax into the generic formal parameter syntax after USE,
thereby altering:

12.6 (2/2) —    formal_subprogram_declaration ::=
             formal_concrete_subprogram_declaration    |
             formal_abstract_subprogram_declaration    |
             formal_default_subprogram_declaration
and adding
         formal_default_subprogram_declaration ::=
              use [overriding_indicator] procedure defining_program_unit_name is
                 new generic_procedure_name [generic_actual_part] 
[aspect_specification];    |
             use [overriding_indicator] function defining_designator is
                 new generic_function_name [generic_actual_part]
                 [aspect_specification];

Because, as a default itself, the inclusion of “is null”, “is <>”, and
“is default_name” are meaningless. (Note: syntax taken from 12.3 (2/3),
it could probably be cleaned up.)

12.7 (2/3) —    formal_package_declaration ::=
             with package defining_identifier is new generic_package_name
                  formal_package_actual_part [aspect_specification];    |
             use package defining_identifier is new generic_package_name
                  formal_package_actual_part [aspect_specification];

Which I think would work syntactically.

* For Ada projects using GNAT in particular, which requires separate files for
  every library-level instantiation, the complexity-savings in the number of
  files that have to be maintained could be substantial for a project that
  heavily uses generics.

****************************************************************

From: Randy Brukardt
Sent: Thursday, March 29, 2018  9:25 PM

> While this AI seems concerned with defaults of object-parameters 
> (particularly IN OUT), there are other advantages to 
> default-parameters that can be had.

We decided to give this it's own AI.

> ... In
> particular, we can have default generic-parameters which are 
> automatically instantiated — thereby eliminating the need for one-off 
> instantiations which, in turn, would simplify the code of current Ada 
> projects (particularly at the library level*).
> 
> For example, given our current abilities with Ada’s generics and 
> defaults we could say:
> 
>      Generic
>          Type Some_Type is private;
>      Procedure Generic_Swap( A, B : in out Some_Type );
> 
>      Generic
>          Type Element is private;
>          Type Index is (<>);
>          Type Array_Type is Array(Index Range <>) of Element;
>          With Procedure Swap(A, B : in out Element) is <>;
>      Procedure Generic_Sort( Input : in out Array_Type );

For me personally, there's little chance of any useful generics lying around.
All of the one's I've used are heavily tailored for a particular purpose. I
can imagine it happens to others.

However, there is a related problem that hits me all the time, and this
generic sort shows it well. And that is the need to declare one-off
subprograms to use with generics. I'd end up with something like:

       procedure Int_Swap (Left, Right : in out Integer) is
           Temp : Integer;
       begin
           Temp := Left;
           Left := Right;
           Right := Temp;
       end Int_Swap;

       procedure Sort is new Generic_Sort(
           Swap       => Int_Swap,
           Element    => Integer,
           Index      => Integer,
           Array_Type => Integer_Vector);

But if this is a specification, the body can't even be here, so now you have 
to declare a body somewhere, too.

This is probably one of the few cases where anonymous anything would be
helpful, but none of the proposals actually allow that.

A similar thought seems to hold for this idea; it seems more likely to be
valuable in the instance rather than as a default.

Perhaps that's just my usage patterns.

****************************************************************

From: Edward Fish
Sent: Friday, March 30, 2018  12:35 PM

>> While this AI seems concerned with defaults of object-parameters 
>> (particularly IN OUT), there are other advantages to 
>> default-parameters that can be had.
> We decided to give this it's own AI.
Interesting; is that good?

Though I did think it was somewhat pertinent to the formal OUT & IN OUT 
parameter-defaults; that is, the same idea could be applied to put the actual 
parameter into the scope that the actual instance uses unless overridden by
the parameter-association of the instantiation. This is to say that:

Generic
   Param : out Integer := 21; -- Or whatever IN OUT /OUT mode-defaulting syntax is chosen.
Package Example_Pkg is
   Procedure Operation;
End Example_Pkg;

Package Body Example_Pkg is
   Procedure Operation is
   Begin
     Param := 5;
   End Operation;
End Example_Pkg;
-- ...

ACTUAL_EXAMPLE:
Declare
   Lights : Integer := 4; -- THERE. ARE. FOUR. LIGHTS!
   Package Ex_Init is new Example( Lights ); Begin
   Ex_Init.Operation;
   -- Now there are five lights.
End ACTUAL_EXAMPLE;

-- ACTUAL_EXAMPLE could be replaced by:

REDUCED_EXAMPLE:

Declare
   Package Ex_Init is new Example; -- This creates Ex_Init.Param, and sets its value to 21.
Begin
   Ex_Init.Operation;
   -- Now Ex_Init.Param is 5.
End REDUCED_EXAMPLE;

The biggest problem is that this creates aliasing where ACTUAL_EXAMPLE's Lights
and Ex_init.Param refer to the same thing. Though I think that this could be 
handled by treating Ex_Init.Param as a RENAME of Lights for analysis purposes. 
(Though perhaps the mode-restriction would invalidate that 
interpretation/notion; I don't have the static analysis expertise to say.)

>> ... In
>> particular, we can have default generic-parameters which are 
>> automatically instantiated - thereby eliminating the need for one-off 
>> instantiations which, in turn, would simplify the code of current Ada 
>> projects (particularly at the library level*).
>>
>> For example, given our current abilities with Ada's generics and 
>> defaults we could say:
>>
>>       Generic
>>           Type Some_Type is private;
>>       Procedure Generic_Swap( A, B : in out Some_Type );
>>
>>       Generic
>>           Type Element is private;
>>           Type Index is (<>);
>>           Type Array_Type is Array(Index Range <>) of Element;
>>           With Procedure Swap(A, B : in out Element) is <>;
>>       Procedure Generic_Sort( Input : in out Array_Type );
> For me personally, there's little chance of any useful generics lying 
> around. All of the one's I've used are heavily tailored for a 
> particular purpose. I cam imagine it happens to others.
>
> However, there is a related problem that hits me all the time, and 
> this generic sort shows it well. And that is the need to declare 
> one-off subprograms to use with generics. I'd end up with something like:
>
>         procedure Int_Swap (Left, Right : in out Integer) is
>             Temp : Integer;
>         begin
>             Temp := Left;
>             Left := Right;
>             Right := Temp;
>         end Int_Swap;
>
>         procedure Sort is new Generic_Sort(
>             Swap       => Int_Swap,
>             Element    => Integer,
>             Index      => Integer,
>             Array_Type => Integer_Vector);
>
> But if this is a specification, the body can't even be here, so now 
> you have to declare a body somewhere.
>
> This is probably one of the few cases where anonymous anything would 
> be helpful, but none of the proposals actually allow that.

What anonymous thing do you want here?

This proposal, so far, doesn't use anything anonymously and instead makes
use of the actual instantiation to automatically chain instantiation of 
non-overriding/non-specified defaults into its own namespace... so there's
nothing anonymous that I can see there. / Or am I misunderstanding what you're
meaning?

> A similar thought seems to hold for this idea; it seems more likely to 
> be valuable in the instance rather than as a default.
>
> Perhaps that's just my usage patterns.

Are the two (default vs. instance) necessarily opposed?

It seems obvious to me they're related, just as a call to a 
Function Image( X : Integer; Alignment : Justification := Right ) return String 
{given TYPE Justification IS (Left, Right, Center);} is related to the 
default by its usage/non-usage: 
"Text : String := Image(23, Left);" vs "Text : String := Image(23);".

It's also worth noting that there's a bit of a difference between subprograms 
and packages as formal parameters for generics; as packages the parameter 
needs to be a generic. as a subprogram they cannot be. While I typically like 
Ada's generic faculties, and they tend to be much better than the now-typical 
types-as-parameters generics, this always seemed a bit odd to me.

****************************************************************

From: Randy Brukardt
Sent: Friday, March 30, 2018  4:36 PM

> Interesting; is that good?

It's related to the existing suggestion but sufficiently different and complex 
that it seemed worth considering separately. AI12-0205-1 is in the "simple" 
category, I'd like to keep it there, and anything to do with instantiations is
not simple.

...
> > However, there is a related problem that hits me all the time, and 
> > this generic sort shows it well. And that is the need to declare 
> > one-off subprograms to use with generics. I'd end up with something like:
> >
> >         procedure Int_Swap (Left, Right : in out Integer) is
> >             Temp : Integer;
> >         begin
> >             Temp := Left;
> >             Left := Right;
> >             Right := Temp;
> >         end Int_Swap;
> >
> >         procedure Sort is new Generic_Sort(
> >             Swap       => Int_Swap,
> >             Element    => Integer,
> >             Index      => Integer,
> >             Array_Type => Integer_Vector);
> >
> > But if this is a specification, the body can't even be here, so now 
> > you have to declare a body somewhere.
> >
> > This is probably one of the few cases where anonymous anything would 
> > be helpful, but none of the proposals actually allow that.

> What anonymous thing do you want here?

Anonymous procedures and functions to pass to the instance for formal 
subprograms. Using the language-defined sort as an example:

     function Order (Left, Right : Float) return Boolean is (Func(Left) < Func(Right));

     procedure My_Sort is new Ada.Containers.Generic_Array_Sort (Natural, Float, My_Array, Order);

Here Order is a one-off function used only by My_Sort. It would be nice for it
to be anonymous somehow:

     procedure My_Sort is new Ada.Containers.Generic_Array_Sort (Natural, 
        Float, My_Array, function (Func(Left) < Func(Right)));

> This proposal, so far, doesn't use anything anonymously and instead 
> makes use of the actual instantiation to automatically chain 
> instantiation of non-overriding/non-specified defaults into its own 
> namespace... so there's nothing anonymous that I can see there. / Or 
> am I misunderstanding what you're meaning?

The automatic instance itself is anonymous to users of the enclosing instance 
- it does not have a name that its parts can be referenced through.
For a subprogram, that probably doesn't matter (if you wanted to call it, 
you'd need a name, but then the instance would not have been a "1-off" in the
first place). For a formal package though, you might want to access the 
contents when using the instance.

For instance (ugh - way too many "instances" here!), imagine that you use your 
feature on a generic I/O package for a bounded string:

      generic
         Max : Positive;
         use package Bounded is
             new Ada.Strings.Bounded.Generic_Bounded_Length (Max);
      package Bounded_IO is
         procedure Put_Line
            (Item : in Bounded.Bounded_String);
         ...
      end Bounded_IO;

So an instance could be:
    package Message_IO is new Bounded_IO (80);

but here you don't have any way to name the type of the Bounded string (since 
formal packages are not visible outside of the instance, and the actual 
package was automatically instantiated without a name).

You could try to give this package a name, but now the entire idea has become
a lot more complicated because it is dragging in visibility issues - you are 
addingd a new kind of scope (it gets inserted inside of the instance 
implicitly by the instance, but only conditionally - its not there if the 
default isn't used).

****************************************************************

Questions? Ask the ACAA Technical Agent