Version 1.2 of 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 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:
--
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