Skip to content

[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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

raph-amiard
Copy link
Member

@raph-amiard raph-amiard commented Mar 3, 2023

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.

@raph-amiard raph-amiard force-pushed the topic/generic_instantiations branch 2 times, most recently from 4e8e48b to 3be3f9d Compare March 3, 2023 14:35
Comment on lines +32 to +33
instance of `Ada.Containers.Vectors (Positive, Positive)`, and all people
refering to it will refer to the same instance, which solves a long standing
Copy link
Contributor

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.)

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely! Thank you 😄

@briot
Copy link

briot commented Mar 6, 2023 via email

@raph-amiard
Copy link
Member Author

raph-amiard commented Mar 8, 2023

Hi @briot ! Thanks for the thoughtful feedback.

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)

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 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))

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:

  1. Readable and explicit (like your proposal)
  2. Non-ambiguous (unlike both your proposal and mine, in which you cannot determine by syntax alone that it is a structural instantiation)
  3. Uses existing Ada symbols.

I was hoping to leverage brackets that were added recently, but Reduce [Fn => "+"] (X) is still potentially ambiguous (or at least Reduce ["+"] (X) is. I imagine we could imagine a syntax similar to type qualification but with brackets instead, and use that for generic instantiation: Reduce'[Fn => "+"] (X)

In this proposal, I am less convinced by the "Generic types" part. It
seems to me that standard subtypes would work here:

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.

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).

It is indeed true that implicit instantiations will make error messages harder to decipher. However,

  1. We do have a name to reference things, it's just a tad longer :) But there is only one Vector (Positive, Positive), and as with structural things in programming languages, the name of this instantiation is Vector (Positive, Positive).

  2. Unlike C++ (at least pre-concepts) we can still do most of the error checking before the instantiation, thanks to Ada generics model. Errors that happen at the instantiation site in Ada are pretty rare.

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.

@raph-amiard raph-amiard force-pushed the topic/generic_instantiations branch 3 times, most recently from 61c2134 to d2c4553 Compare March 9, 2023 11:44
@raph-amiard
Copy link
Member Author

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);

@sttaft
Copy link
Contributor

sttaft commented Apr 17, 2023

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?

Copy link
Contributor

@QuentinOchem QuentinOchem left a 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.

Comment on lines 52 to 71
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);
Copy link
Contributor

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);
Copy link
Contributor

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);
Copy link
Contributor

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
Copy link
Contributor

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;
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

@Roldak Roldak Oct 25, 2023

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.

Copy link
Contributor

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;

Copy link
Member Author

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

@raph-amiard raph-amiard force-pushed the topic/generic_instantiations branch from 2c756d2 to fe098da Compare September 16, 2024 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Need design&discussion
Development

Successfully merging this pull request may close these issues.

6 participants