Skip to content

Commit 7324396

Browse files
author
Denis Mazzucato
committed
Subprogram-valued Aspect Declarations
1 parent 118f948 commit 7324396

File tree

1 file changed

+305
-17
lines changed

1 file changed

+305
-17
lines changed

features/rfc-oop-attributes.rst

Lines changed: 305 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,68 +5,357 @@
55
Summary
66
=======
77

8+
This RFC introduces a shorthand notation to declare subprogram-valued aspects.
9+
810
Motivation
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+
1120
Guide-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+
64326
Rationale 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+
67353
Drawbacks
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+
70359
Prior art
71360
=========
72361

@@ -76,4 +365,3 @@ Unresolved questions
76365
Future possibilities
77366
====================
78367

79-

0 commit comments

Comments
 (0)