55Summary
66=======
77
8+ This RFC introduces a shorthand notation to declare subprogram-valued aspects.
9+
810Motivation
911==========
1012
13+ Currently, to specify a subprogram-valued aspect for a type, one needs to first
14+ declare a subprogram, that most of the times is never referred to elsewhere,
15+ and then link it to the type via an aspect specification or attribute
16+ definition clause. This pattern creates unnecessary boilerplate code,
17+ disconnects the aspect specification from the denoted subprogram declaration,
18+ and introduces potential name resolution issues.
19+
1120Guide-level explanation
1221=======================
1322
14- Today, specifying a subprogram attribute of a type requires to declare a
15- subprogram and then to use an aspect or an attribute to link it to a type, e.g.
23+ Subprogram-valued Aspect Declarations
24+ -------------------------------------
25+
26+ Currently, specifying a subprogram-valued aspect for a type requires to first
27+ declare the subprogram and then apply the corresponding aspect specification or
28+ attribute definition clause. For example:
1629
1730.. code-block :: ada
1831
1932 type T is null record;
2033
2134 for T'Write use My_Write;
2235
23- procedure My_Write(
24- Stream : not null access Ada.Streams.Root_Stream_Type'Class;
36+ procedure My_Write
37+ (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
38+ Item : in T);
39+
40+ Or via an aspect specification:
41+
42+ .. code-block :: ada
43+
44+ type T is null record with Write => My_Write;
45+
46+ procedure My_Write
47+ (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
2548 Item : in T);
2649
27- This proposal allows to define directly the procedure as being an attribute of
28- the type:
50+ This proposal introduces **subprogram-valued aspect declarations ** that allow
51+ to specify a subprogram as an aspect value directly within the subprogram
52+ declaration.
53+ For example, the above can be rewritten as:
2954
3055.. code-block :: ada
3156
3257 type T is null record;
3358
34- procedure T'Write(
35- Stream : not null access Ada.Streams.Root_Stream_Type'Class;
59+ procedure T'Write
60+ ( Stream : not null access Ada.Streams.Root_Stream_Type'Class;
3661 Item : in T);
3762
38- In addition, this can be used as a scoped attribute, possibly with re-ordering
39- of parameters to ensure that the first parameter is conceptually the one
40- "owning" the primitive, e.g.:
63+ The code above both declares the subprogram ``T'Write `` and specifies it as the
64+ ``Write `` aspect of type ``T ``.
65+ Intuitively, this shorthand notation translates to the following aspect
66+ specification:
67+
68+ .. code-block :: ada
69+
70+ type T is null record with Write => @'Write;
71+
72+ Where "@" denotes the enclosing type name, cf. ``T `` in this case. When such
73+ subprogram is inherited, "@" will also denote the derived type name.
74+
75+ A Derived Map Example
76+ ^^^^^^^^^^^^^^^^^^^^^
77+
78+ With the subprogram-valued aspect declarations, we could write a hierarchy of a
79+ map as follows:
80+
81+ .. code-block :: ada
82+
83+ package P is
84+ type T is tagged null record;
85+
86+ -- T has exactly two keys: Natural and Boolean
87+ function T'Constant_Indexing (X : T; I : Natural) return Natural;
88+ function T'Constant_Indexing (X : T; B : Boolean) return Natural;
89+ end P;
90+
91+ .. code-block :: ada
92+
93+ with P;
94+
95+ package Q is
96+ type T2 is new P.T with null record;
97+
98+ -- The behavior of the Natural key is overridden
99+ overriding -- Optional
100+ function T2'Constant_Indexing (X : T2; I : Natural) return Natural;
101+
102+ -- The Boolean key is implicitly inherited from P.T
103+ -- function T2'Constant_Indexing (X : T2; B : Boolean) return Natural;
104+
105+ -- Additionally, T2 supports a Float key
106+ function T2'Constant_Indexing (X : T2; F : Float) return Float;
107+ end Q;
108+
109+ The aspect specification on ``T `` is ``... => @'Constant_Indexing ``, where "@"
110+ denotes both ``T `` and ``T2 ``.
111+ As you can see from the example above, the usual rules of inheritance apply.
112+
113+ There is no clash between subprogram-valued aspect declarations for the same
114+ aspect name, in the same scope *but * for different types, since the aspect
115+ specification is qualified by "@", which denotes the enclosing type name.
116+
117+ .. code-block :: ada
118+
119+ type A is tagged null record;
120+ procedure A'Constant_Indexing (X : A; I : Natural) return Natural;
121+
122+ type B is tagged null record;
123+ procedure B'Constant_Indexing (X : B; I : Natural) return Natural;
124+
125+ Which intuitively translates to:
126+
127+ - ``type A ... with Constant_Indexing => @'Constant_Indexing ``
128+ where "@" denotes only ``A ``, and
129+
130+ - ``type B ... with Constant_Indexing => @'Constant_Indexing ``
131+ where "@" denotes only ``B ``.
132+
133+ As expected, ``A'Constant_Indexing `` and ``B'Constant_Indexing `` are not
134+ homonyms.
135+
136+ Primitive or Nonprimitive?
137+ --------------------------
138+
139+ Subprogram-valued aspect declarations can be used to define both primitive and
140+ nonprimitive operations of a type.
141+ For instance, we could define a container type as:
142+
143+ .. code-block :: ada
144+
145+ type Container is tagged record
146+ ...
147+ end record;
148+ type Cursor is new Positive;
149+
150+ function Container'First (C : Container) return Cursor;
151+ function Container'Next (C : Container; Pos : Cursor) return Cursor;
152+ function Container'Has_Element (C : Container; Pos : Cursor) return Boolean;
153+ function Container'Element (C : Container; Pos : Cursor) return Integer;
154+
155+ All the subprogram-valued aspect
156+ declarations of ``Container `` are primitive operations.
157+ The usual rules of primitive operations apply.
158+
159+ Scoped Records
160+ --------------
161+
162+ In relation to the scoped records and class types proposed in the *RFC OOP
163+ primitives *,
164+ subprogram-valued aspect declarations can be used accordingly:
41165
42166.. code-block :: ada
43167
44168 type T is tagged record
45169
46- procedure T'Write(
47- Stream : not null access Ada.Streams.Root_Stream_Type'Class;
170+ procedure T'Write
171+ ( Stream : not null access Ada.Streams.Root_Stream_Type'Class;
48172 Item : in T);
49173
50174 end T;
51175
52- This provide a seamless solution in particular to scope a destructor:
53-
176+ This provide a seamless solution in particular to scope oop primitives:
54177
55178.. code-block :: ada
56179
57- type T is tagged record
180+ type T is class record
181+ procedure T'Constructor (Self : in out T);
58182 procedure T'Destructor (Self : in out T);
183+
184+ procedure T'Write
185+ (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
186+ Item : in T);
59187 end record;
60188
189+ This approach is quite natural and consistent with the rest of the proposal:
190+ all aspect specifications are gathered together within the scoped environment.
191+
192+ Unfortunately, the subprogram-valued aspect declarations inside a scoped class
193+ may clash with the class type rules where each subprogram is required to be
194+ primitive and following the rules of First_Controlling_Parameter
195+ (e.g., see ``T'Write `` above
196+ where ``Item `` is the second formal, not the first). The rule's motivation
197+ comes, for instance,
198+ from the fact that class primitives are called with the prefix notation, and
199+ that no primitive operation should be defined afterwards.
200+
201+ To solve this issue, we allow (only) subprogram-valued aspect declarations
202+ inside scoped environments to be nonprimitive and not following the
203+ First_Controlling_Parameter rule. Since, these subprograms are never called
204+ with the prefix notation, this relaxation is safe.
205+
206+ Hierarchy with Write, Read, Output, and Put_Image
207+ -------------------------------------------------
208+
209+ Here it follows an example of usage of subprogram-valued aspect declarations
210+ with a simple hierarchy involving a combination of ``Write ``, ``Read ``,
211+ ``Output ``, and
212+ ``Put_Image `` aspects.
213+
214+ .. code-block :: ada
215+
216+ with Ada.Strings.Text_Buffers; use Ada.Strings.Text_Buffers;
217+ with Ada.Streams; use Ada.Streams;
218+
219+ package Geometry is
220+ type Shape is tagged record
221+ X, Y : Integer;
222+ end record;
223+
224+ procedure Shape'Read
225+ (Stream : not null access Root_Stream_Type'Class;
226+ Item : out Shape);
227+ procedure Shape'Output
228+ (Stream : not null access Root_Stream_Type'Class;
229+ Item : in Shape);
230+ procedure Shape'Put_Image
231+ (Buffer : in out Root_Buffer_Type'Class;
232+ Item : Shape);
233+
234+ type Circle is new Shape with record
235+ Radius : Integer;
236+ end record;
237+
238+ overriding
239+ procedure Circle'Read
240+ (Stream : not null access Root_Stream_Type'Class;
241+ Item : out Circle);
242+ procedure Circle'Write
243+ (Stream : not null access Root_Stream_Type'Class;
244+ Item : in Circle);
245+ overriding
246+ procedure Circle'Put_Image
247+ (Buffer : in out Root_Buffer_Type'Class;
248+ Item : Circle);
249+ end Geometry;
250+
251+ ...
252+
253+ with Ada.Strings.Text_Buffers; use Ada.Strings.Text_Buffers;
254+ with Ada.Streams; use Ada.Streams;
255+ with Ada.Text_IO; use Ada.Text_IO;
256+
257+ package body Geometry is
258+ procedure Shape'Read
259+ (Stream : not null access Root_Stream_Type'Class;
260+ Item : out Shape) is
261+ begin
262+ Put_Line ("Shape'Read called");
263+ Integer'Read (Stream, Item.X);
264+ Integer'Read (Stream, Item.Y);
265+ end Shape'Read;
266+
267+ procedure Shape'Output
268+ (Stream : not null access Root_Stream_Type'Class;
269+ Item : in Shape) is
270+ begin
271+ Put_Line ("Shape'Output called");
272+ Integer'Output (Stream, Item.X);
273+ Integer'Output (Stream, Item.Y);
274+ end Shape'Output;
275+
276+ procedure Shape'Put_Image
277+ (Buffer : in out Root_Buffer_Type'Class;
278+ Item : Shape) is
279+ begin
280+ Put_Line ("Shape'Put_Image called");
281+ Integer'Put_Image (Buffer, Item.X);
282+ Integer'Put_Image (Buffer, Item.Y);
283+ end Shape'Put_Image;
284+
285+ overriding
286+ procedure Circle'Read
287+ (Stream : not null access Root_Stream_Type'Class;
288+ Item : out Circle) is
289+ begin
290+ Put_Line ("Circle'Read called");
291+ Integer'Read (Stream, Item.Radius);
292+ Shape'Read (Stream, Shape (Item));
293+ end Circle'Read;
294+
295+ procedure Circle'Write
296+ (Stream : not null access Root_Stream_Type'Class;
297+ Item : in Circle) is
298+ begin
299+ Put_Line ("Circle'Write called");
300+ Integer'Write (Stream, Item.Radius);
301+ Shape'Output (Stream, Shape (Item)); -- Mixing Write and Output here
302+ end Circle'Write;
303+
304+ overriding
305+ procedure Circle'Put_Image
306+ (Buffer : in out Root_Buffer_Type'Class;
307+ Item : Circle) is
308+ begin
309+ Put_Line (Shape(Item)'Image);
310+ Put_Line ("Circle'Put_Image called");
311+ Integer'Put_Image (Buffer, Item.Radius);
312+ end Circle'Put_Image;
313+ end Geometry;
314+
61315 Reference-level explanation
62316===========================
63317
318+ This proposal introduces syntactic sugar for the declaration and specification
319+ of subprogram-valued aspects. It is fully backward compatible with existing Ada
320+ code.
321+
322+ When a subprogram-valued aspect declaration is encountered, it is equivalent
323+ to an aspect specification where the aspect value is "@'<Aspect_Name>", where
324+ "@" denotes the the name of the enclosing type and any of its derived types.
325+
64326Rationale and alternatives
65327==========================
66328
329+ While the notation "@'<Aspect_Name>" is not a syntactic Ada construct and it is
330+ used above in this proposal at an intuitive level for the aspect
331+ specifications, a syntactic ``@'<Aspect_Name> `` used in the subprogram name
332+ could further shorten our notation. This second use of ``@ `` denotes the
333+ enclosing type name; no need to account for derived types here.
334+ The big advantage is when the type name is long; for instance, we could write:
335+
336+ .. code-block :: ada
337+
338+ type Indexable_Container_With_A_Long_Name is tagged record
339+ function @'Constant_Indexing
340+ (Container : aliased @;
341+ Position : Offset) return Constant_Reference_Type;
342+ function @'Constant_Indexing
343+ (Container : aliased @;
344+ Position : Custom_Offset) return Constant_Reference_Type;
345+ function @'Variable_Indexing
346+ (Container : aliased in out @;
347+ Position : Offset) return Reference_Type;
348+ end record;
349+
350+ This use of ``@ `` transcends the current proposal and should be discussed in a
351+ separate RFC.
352+
67353Drawbacks
68354=========
69355
356+ The IDE support for this shorthand notation might be complex as it would need
357+ to connect aspect calls with their (potential) declarations.
358+
70359Prior art
71360=========
72361
@@ -76,4 +365,3 @@ Unresolved questions
76365Future possibilities
77366====================
78367
79-
0 commit comments