-
Notifications
You must be signed in to change notification settings - Fork 29
[RFC] improved generic instantiations #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
4e8e48b
to
3be3f9d
Compare
instance of `Ada.Containers.Vectors (Positive, Positive)`, and all people | ||
refering to it will refer to the same instance, which solves a long standing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, the ability to create a fresh instance may be important too, if the instantiation performs important side effects. (Things like creating a datastructure that holds a list of already hash-consed entities. You don't want to mix two separate instances.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing is, you already have an easy way to create a fresh instance in Ada, and it's regular explicit generic instantiations. I'm not sure implicit but fresh instantiations bring any value to the table honestly, and I would need to see a compelling real world example of a case where they're a better fit than regular explicit instantiations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed live with Boris: Agreed that with explicit instantiations the first point is moot. Boris also pointed out that we might want a mechanism to prevent implicit references to instantiations for certain generics.
An example that comes to mind is generics with global state such as GNAT's hash table.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely! Thank you 😄
On 2023-03-06 12:27, Raphaël AMIARD wrote:
As discussed live with Boris: Agreed that with explicit instantiations
the first point is moot. Boris also pointed out that we might want a
mechanism to prevent implicit references to instantiations for certain
generics.
An example that comes to mind is generics with global state such as
GNAT's hash table.
1 - disable implicit instantiations
-------------------------------------------
There is at least one case where the compiler should emit a warning:
- if we allow implicit instantiations, yet the generic package has a
global state (this needs to be defined precisely, but a rough approach
is whether there are global objects in the package, including a task)
I suppose a generic subprogram can always have implicit instantiations,
with the above rule, since it doesn't have a global state.
It would be easy for programmers to forget to disable implicit
instantiations, so the more warnings we have the better.
2 - use type qualification for instantiations
--------------------------------------------------------
Going back to an example in the proposal:
function Sum (X: Float_Array) return Float is (Reduce (Fn => "+")
(X))
I think it might be better to use syntax like type qualification here,
rather than subprogram call:
function Sum (X: Float_Array) return Float is (Reduce'(Fn => "+") (X))
to better differentiate which part is the instantiation and which part
is the call
3 - need to implicit instantiations
--------------------------------------------
Although I can see the benefits for implicit instantiations, I can't say
this is something I have actually missed in Ada (I do have a number of
gripes with generics, but not this particular one). One place where I
wished I had them was as defaults for formal parameters in other
generics. For instance:
generic
type T is private;
with package T_Vectors is new Ada.Containers.Vectors (Element_Type
=> T, others => <>)
is Ada.Containers.Vectors (Positive, T); -- Missing
syntax for defaults, but you get the idea hopefully
package Foo is
end Foo;
The part I prefer in this proposal is the "inference of dependent
types". That could ease instantiations (even explicit ones), and seems
like a good step forward. A similar change in recent versions of the
language is where we no longer need to specify the type when using
"renames". I like when the user doesn't have to state the "obvious".
4 - generic types
----------------------
In this proposal, I am less convinced by the "Generic types" part. It
seems to me that standard subtypes would work here:
subtype Float_Vector is Ada.Containers.Vectors (Positive,
Float).Vector;
and we do not need to invent a new syntax like "generic type". Not that
I am a big fan of my proposal either, since we might as well use an
explicit instantiation here.
5 - error messages
------------------------
One issue with implicit instantiations is that the compiler is lacking a
name to reference things, and we might end up with pretty obscure error
messages as a result. Although this is orthogonal to the proposal, I
think the latter needs to ensure that it is possible to have readable
error messages (thinking of C++ templates errors here).
|
Hi @briot ! Thanks for the thoughtful feedback.
Yes, that's the point Boris raised live and that I recorded here: #103 (comment). I'll make sure to address this point in the specific RFC
I agree, in particular with the fact that the syntax is less than optimal. I really didn't think too much about it for the moment, because I know that it's going to be the most discussed topic about the RFC. Ideally I would like to find something that is simultaneously:
I was hoping to leverage brackets that were added recently, but
The generic types part is meant to acknowledge at the language level that often a generic package's only use is to use a specific type. It's also meant to simplify using those types in specific use cases, because typing the full package name to use a container type is verbose for no reason. With the subtype syntax, at least if this is just a regular subtype, then you cannot instantiate it, and you lose the shortcut AFAICT.
It is indeed true that implicit instantiations will make error messages harder to decipher. However,
So all in all I'm not too worried about this specific concern. The biggest concern is whether this all is actually implementable :) The compilation model for structural generics is still to be defined. |
61c2134
to
d2c4553
Compare
One example of something that would be made much easier with this is Option types: generic
type T is private;
package Options is
type Option(Exists : Boolean) is record
case Exists is
when True => Val : T;
when False => null;
end case;
end record;
end Options;
generic type Opt is Options.Option;
A : Opt [Integer] := (Val => 12); |
The aggregate (Val => 12) in your example seems to be presuming that the value of the discriminant "Exists" is being inferred. Is that part of these proposals, or just a typo? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going back to this. This would be a significant way forwards in terms of usability of the language IMO.
generic | ||
type Element_Type is private; | ||
type Index_Type is (<>); | ||
type Array_Type is array (Index_Type range <>) of Element_Type; | ||
package Array_Operations is | ||
... | ||
end Array_Operations; | ||
|
||
... | ||
|
||
type Int_Array is array (Positive range <>) of Integer; | ||
|
||
package Int_Array_Operations is new Array_Operations | ||
(Element_Type => Integer, | ||
Index_Type => Positive, | ||
Array_Type => Int_Array); | ||
``` | ||
|
||
This feature allows the programmer to not have to pass type parameters that | ||
could be deduced from other type parameters. In the example above, the language | ||
can automatically deduce the index and element type from the array type that is | ||
passed in: | ||
|
||
```ada | ||
package Int_Array_Operations is new Array_Operations (Array_Type => Int_Array); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's too bad that one still has to identify the name of the generic parameter to value here. It would be great to work the other way around, and allow something like:
package Int_Array_Operations is new Array_Operations (Int_Array);
For this we would need a way to either re-order the list of generic parameters so that the array is the first one, or to indicate that the other parameters are inferred and not explicitly provided, e.g.:
generic
type Element_Type is private with Inferred;
type Index_Type is (<>) with Inferred;
type Array_Type is array (Index_Type range <>) of Element_Type;
package Array_Operations is
end Array_Operations;
package Int_Array_Operations is new Array_Operations (Int_Array);
And perhaps as an extension, if really needed, allow optional named valuation of these parameters (but never positional).
|
||
A : Integer_Access := new Integer'(12); | ||
|
||
Ada.Unchecked_Deallocation [Integer, Integer_Access] (A); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The semantic of the square brackets is not clear in the proposal. Some other (not all) parts of the RFC instantiate through parenthesis. Maybe we want to keep round parenthesis for now? The syntax (something) (something else)
is arguably not ideal, but already present in some other constructions in Ada. At the very least, if we want to discuss a different syntax to list actual parameters, this should be a separate part of the RFC.
generic package Vecs | ||
is Ada.Containers.Vectors (Index_Type => Positive); | ||
|
||
package Float_Vectors is new Vecs (Float); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As stated in the introduction, that may be the least valuable part of the proposal. At this stage, the use cases for such notation might be limited. Probably not worth putting on the forefront.
> accesses/array declarations. | ||
|
||
|
||
### Fourth step: Generic types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this still be part of this RFC? There's no detailed RFC on this, perhaps that's a separate idea that can be worked on in a different thread.
|
||
```ada | ||
F : Ada.Containers.Vectors (Positive, Positive).Vector; | ||
F2 : Ada.Containers.Vectors (Positive, Positive).Vector; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that specific example, you could just do:
subtype Vector is Ada.Containers.Vectors (Positive, Positive).Vector;
F : Vector;
V2 : Vector;
This seems similar in size as the next version proposed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I guess the snippet from Raph should be changed to something like:
generic type Vector is Ada.Containers.Vectors (Index_Type => Positive).Vector;
F : Vector (Positive);
F2 : Vector (Float);
To make the point more evident.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH For now I'm not really convinced by generic types. If we had partial package instantiations as in #41, this could already be simplified to:
generic
type Element_Type is private;
package Vectors is new Ada.Containers.Vectors (Positive, Element_Type);
F : Vectors (Positive).Vector;
F2 : Vectors (Float).Vector;
Which is a bit longer but doesn't require introducing a whole new concept.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ada 2022 added defaults for formal types, so if we provided a default of Positive for the Index_Type this would already be a little better:
F2 : Vectors (Element_Type => Float).Vector;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sttaft Good point, that would be a worthy (and backwards compatible) addition to containers IMO
2c756d2
to
fe098da
Compare
This is the first draft of the high level RFC summarizing the work we have to do to improve generic instantiations in Ada, and of the low level corresponding RFCs
For the moment some of the sub-referenced RFCs are yet to write, I plan to work on them in the coming days/weeks.