A small blog post that you might find interesting:
https://deepbluecap.com/calling-inherited-primitive-operations-in-ada/
We are preparing a number of other posts, but right now this is the only one on our web site, so the links are a bit hidden. They will become available on https://deepbluecap.com/blog/ as we publish them.
Emmanuel
A small blog post that you might find interesting:
https://deepbluecap.com/calling-inherited-primitive-operations-in-ada/
generic
type Foo is ...;
package
subtype Actual_Foo is Foo;
genericTo me, this is an orthogonal issue though (which would be worth its own blog post in fact). I can never remember (or perhaps not even understand) the reason for this limitation in Ada, which is a major pain when dealing with generics indeed...
type Foo is ...;
package
subtype Actual_Foo is Foo;
I like the "Actual_" prefix, which I assume is some sort of convention in your code.
On Thursday, September 1, 2022 at 8:56:28 AM UTC+2, briot.e...@gmail.com wrote:
genericTo me, this is an orthogonal issue though (which would be worth its own blog post in fact). I can never remember (or perhaps not even understand) the reason for this limitation in Ada, which is a major pain when dealing with generics indeed...
type Foo is ...;
package
subtype Actual_Foo is Foo;
I like the "Actual_" prefix, which I assume is some sort of convention in your code.
Is this about how according to some mystifying rules generic formals are[n't] visible from outside the generic?
Is this about how according to some mystifying rules generic formals are[n't] visible from outside the generic?
On 2022-09-01 09:57, amo...@unizar.es wrote:
Is this about how according to some mystifying rules generic formals
are[n't] visible from outside the generic?
This seems like a non-issue to me. Any code that has visibility to a
generic instance knows the actuals used for that instance.
Can anyone
provide real examples where this is a problem?
Can anyone provide real examples where this is a problem?
Defaulted formal package actual part:
generic
package Foo is new Bar (<>);
package Baz is ...
-- What were these actuals in Foo?
I have seen quite a number of cases of needing the subtype like Dmitry was showing. In a small number of cases, those were actual bugs in GNAT, but most of the time the compiler was correct.
Mostly, you start to see the issue when you have generic packages that have formal generic packages.
There are more interesting examples, somehow this one doesn't seem that bad. So here is another one:
generic
type T is private;
package Gen is
end Gen;
with Gen;
generic
type T is private;
with package Must_Match is new Gen (T);
with package Need_Not_Match is new Gen (<>);
package Gen2 is
V1 : Must_Match.T; -- "T" is not a visible entity of "Must_Match"
V2 : Need_Not_Match.T; -- instance of same package, but this time T is visible
end Gen2;
with Gen, Gen2;
procedure P2 is
package G is new Gen (Integer);
package G2 is new Gen2 (Integer, G, G);
begin
null;
end P2;
I dug out the explanation that Tucker Taft once sent to the Ada-Comment mailing list (2019-11-14):formal parameter of the template is included in the visible part of the formal package. If the copied declaration is for a formal type, copies of the implicit declarations of the primitive subprograms of the formal type are also included in the visible
<<<
10/2
{AI95-00317-01} The visible part of a formal package includes the first list of basic_declarative_items of the package_specification. In addition, for each actual parameter that is not required to match, a copy of the declaration of the corresponding
10.a/2expanded names outside the formal package.If only some of the actual parameters are given by <>, then the declaration corresponding to those parameters (but not the others) are made visible.
Ramification: {AI95-00317-01} If the formal_package_actual_part is (<>), then the declarations that occur immediately within the generic_formal_part of the template for the formal package are visible outside the formal package, and can be denoted by
10.b/3least, they might have different and inconsistent sets of primitive operators due to predefined operator “reemergence.” Formal derived types exacerbate the difference. We want the implicit declarations of the generic_formal_part as well as the
Reason: {AI05-0005-1} We always want either the actuals or the formals of an instance to be nameable from outside, but never both. If both were nameable, one would get some funny anomalies since they denote the same entity, but, in the case of types at
This is true in all these examples. I have used Ada since 1984, and this has never been a problem for me (as initially presented, this would have existed in
I have seen quite a number of cases of needing the subtype like Dmitry was showing. In a small number of cases, those were actual bugs in GNAT, but most of the time the compiler was correct.
Mostly, you start to see the issue when you have generic packages that have formal generic packages.
Reason: {AI05-0005-1} We always want either the actuals or the formals of an instance to be nameable from outside, but never both.This is true in all these examples. I have used Ada since 1984, and this has never been a problem for me (as initially presented, this would have existed in Ada 83). Of course, I generally avoid generic formal pkgs. They seem to me to be
So basically saying that you do not use generics (or only very basic versions) and you have never seen
the bug that relates to complex generics. Not very useful input ?
generic
type Element_Type is private;
type Stored_Type is private;
package Elements is
end Elements;
with Elements;
generic
type Element_Type is private;
package Definite_Elements is
package Traits is new Elements (Element_Type, Stored_Type => Element_Type);
end Definite_Elements;
with Definite_Elements;
generic
type Key_Type is private;
package Maps is
package Keys is new Definite_Elements (Key_Type);
function "=" (L, R : Keys.Traits.Stored_Type) return Boolean -- "Stored_Type" is not a visible entity of "Traits"
is (False);
end Maps;
This is not case where the actual is visible unless I happen to know how Definite_Element is implemented and that it will use Element_Type for Stored_Type (and this is not a knowledge I wish client packages to have, the whole point of Element andDefinite_Element is to basically hide how elements can be stored in a container, and whether we need memory allocation for instance).
As presented, this seems very over complicated. Elements and Definite_Elements
add no value, and this is just a complex way of writing
One hopes that the actual library makes better use of these pkgs than this small
example.
Ada has excellent features for hiding, but these are not among them. Assuming
that pkg Keys serves some actual role in the spec of Maps, then someone wanting
to use pkg Maps will need to understand that role, which involves understanding
Definite_Elements, which involves understanding pkg Traits, which involves understanding Elements.
On 2022-09-01 20:54, Emmanuel Briot wrote:
As presented, this seems very over complicated. Elements and Definite_Elements
add no value, and this is just a complex way of writing
On Thursday, September 1, 2022 at 11:33:47 PM UTC+2, Jeffrey R.Carter wrote:very soon faces this conundrum about duplicating most interfaces, if not implementations, or resort to non-trivial generics, or accept an unnecessary penalty for definite types, or push to the client the definite storage matter. There's simply no
On 2022-09-01 20:54, Emmanuel Briot wrote:
As presented, this seems very over complicated. Elements and Definite_Elements
add no value, and this is just a complex way of writing
Going in a tangent, and I guess you know perfectly well, but this is caused by the painful duplication of code that Ada pushes you to by not having a native way to abstract storage of definite vs indefinite types. So the provider of a generic library
I can understand the desire to have full control of allocation and object sizes, but that there's not a language way to work around this duplication, with appropriate restrictions to go with it, is... bothersome. Not making a dig at the ARG, which Iunderstand is overstretched as it is.
There was a proposal circulating some time ago that seemed promising, that I can't quickly find. Something like
type Blah is record
Dont_care_if_in_heap: new Whatever_Definiteness; -- Would apply to indefinite types or formals
end record;
Containers should be implementable without generics. Just saying.
I would prefer constraint propagation/management support + tuples instead:
type Blah (Parameters : Whatever_Definiteness'Constraints) is
Sill_Care : Whatever_Definiteness (Parameters);
end record;
On Friday, September 2, 2022 at 10:48:57 AM UTC+2, Dmitry A. Kazakov wrote:
Containers should be implementable without generics. Just saying.
Are you now referring to current Ada or to hypothetical features?
The only indefinite data structure that is needed seems to be holders.
Going in a tangent, and I guess you know perfectly well, but this is caused by the painful duplication of code that Ada pushes you to by not having a native way to abstract storage of definite vs indefinite types. So the provider of a generic libraryvery soon faces this conundrum about duplicating most interfaces, if not implementations, or resort to non-trivial generics, or accept an unnecessary penalty for definite types, or push to the client the definite storage matter. There's simply no
I did point you to the full repository if you prefer an extensive, real-life code sample. This was just an extract showing the gist of the issue.
One can check the full code and extensive documentation on the design in the github repository.
These packages are mostly implementation details. They are used to build high-level packages similar to the Ada containers
that pkg Keys serves some actual role in the spec of Maps, then someone wanting
to use pkg Maps will need to understand that role, which involves understanding
Definite_Elements, which involves understanding pkg Traits, which involves >> understanding Elements.
which involves understanding Ada, which involves understanding the compiler, which involves understanding the kernel sources, ...
How many times have you checked the whole sources of the GNAT runtime to understand how they implemented the Ada containers ?
not user friendly. There appears to be extensive HTML documentation, but it can
only be viewed as HTML source in Github, which is not easy to read, so I didn't.
typical user, for whom using a hashed-map abstraction should be as simple as
function Hash (Key : in Identifier) return Hash_Value;
package Maps is new Lib.Maps
(Identifier => Identifier, Associated_Value => Associated_Value);
I was not willing to spend more than about 15 minutes trying to understand this,
so I may be missing something.
Maps are created as
package Maps0 is new GAL.Maps.Def_Def_Unbounded
(Integer,
Element_Type => Integer,
Hash => Test_Support.Hash,
Container_Base_Type => GAL.Controlled_Base);
On 2022-09-01 20:54, Emmanuel Briot wrote:
As presented, this seems very over complicated. Elements and
Definite_Elements
add no value, and this is just a complex way of writing
Going in a tangent, and I guess you know perfectly well, but this is caused >by the
painful duplication of code that Ada pushes you to by not having a native
way to
abstract storage of definite vs indefinite types.
So the provider of a generic library very soon faces this conundrum about duplicating most interfaces, if not implementations, or resort to
non-trivial
generics, or accept an unnecessary penalty for definite types, or push to
the client the definite storage matter.
There's simply no satisfying solution here [that I know of]. The
duplication of
every standard container to have both [in]definite variants is a strong indictment.
These packages are mostly implementation details. They are used to build >high-level
packages similar to the Ada containers, except with much better code reuse, >more
efficient, and SPARK-provable.
On 2022-09-02 12:41, Jeffrey R.Carter wrote:
The only indefinite data structure that is needed seems to be holders.
The language should support and encourage design that does not rely on
memory pools.
In my view one of major advantages of Ada is that indefinite objects can
be handled without resorting to hidden or explicit pointers to pools.
And Ada doesn't support default values for the formal parameters of
generics (another annoying limitation !)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:teso01$16gg$1@gioia.aioe.org...
On 2022-09-02 12:41, Jeffrey R.Carter wrote:
The only indefinite data structure that is needed seems to be holders.
The language should support and encourage design that does not rely on
memory pools.
In my view one of major advantages of Ada is that indefinite objects can
be handled without resorting to hidden or explicit pointers to pools.
But they're implemented with some sort of hidden allocation. (GNAT uses a "secondary stack", whatever that is, but that is just a restricted form of pool). Janus/Ada uses built-in pools with cleanup for all such things to simplify the interface (the code for allocations and stand-alone objects is mostly shared, both within the compiler and at runtime).
One
principle that we insisted on was that a single instantiation was the
maximum we would use
But they violate the #1 principle of the Ada.Containers: ease of
use. One principle that we insisted on was that a single instantiation
was the maximum we would use
Where is that? All I saw in my quick look were Maps, Maps.Generics, and Maps.Impl.
And this was one reason that I didn't put up any arguments at Ada Europe
2002 for the Ada 95 Booch Components to form a basis for Ada.Containers
- you'd need 3 instantiations, one after the other.
Assuming otherwise is certainly premature optimization.
The only indefinite data structure that is needed seems to be holders
Ada *DOES* support default values for formal parameters of generics
Although it is certainly true that using holders works, it is not applicable when designing a containers library that intends to be
mostly compatible with Ada containers.
The main benefit to this approach is that users still retrieve directly the type they are interested in (e.g. String)
rather than a holder-to-string.
I must admit I have very limited experience with Holders, which I have never used in production code (nor, apparently, have my
colleagues and ex-colleagues).
"amo...@unizar.es" <amo...@unizar.es> wrote in message news:672e9bc6-1e53-42cb...@googlegroups.com...
On Thursday, September 1, 2022 at 11:33:47 PM UTC+2, Jeffrey R.Carter wrote:
On 2022-09-01 20:54, Emmanuel Briot wrote:
As presented, this seems very over complicated. Elements and
Definite_Elements
add no value, and this is just a complex way of writing
Going in a tangent, and I guess you know perfectly well, but this is caused >by the
painful duplication of code that Ada pushes you to by not having a native >way to
abstract storage of definite vs indefinite types.
This is premature optimization at it's worst.
So the provider of a generic library very soon faces this conundrum about duplicating most interfaces, if not implementations, or resort to non-trivialThere is no penalty in a code sharing implementation like Janus/Ada: the implementation of definite types is essentially the same as you would write by hand for an indefinite type. In most cases, all one needs is an
generics, or accept an unnecessary penalty for definite types, or push to the client the definite storage matter.
indefinite generic.
(The plan for Janus/Ada was always to use post-compilation optimization to reduce the overhead of generics, but admittedly, that part never got built. If I had infinite time...)
Assuming otherwise is certainly premature optimization.
There's simply no satisfying solution here [that I know of]. The duplication ofThe original expectation for the containers was that there would be many variants of each container, because the needs for memory management, task management, and persistence differ between applications: there is no
every standard container to have both [in]definite variants is a strong indictment.
one-size fits all solution.
But I agree on one point: the "basic" container is unncessary; one should either use the indefinite or bounded container (depending on your memory management needs, either fully fixed or fully heap-based) -- stopping in the middle makes little sense.
Randy.
On Saturday, September 3, 2022 at 2:01:32 AM UTC+2, Randy Brukardt wrote:
Well, that sounds neat for Janus/Ada, but is a different issue to clients having to wrap their indefinite types prior to instantiation, and suffer the unwrapping throughout the code.
I definitely see the same issue. The way my library is trying to workaround that is as follows:
Those instantiations are only needed for people who want/need to control every aspects of their containers, for instance
how elements are stored, how/when memory is allocated, what is the growth strategy for vectors, and so on.
Most users should not have to care about that in practice. So we use code generation at compile time to generate
high-level packages similar to the Ada containers, with a limited set of formal parameters (in src/generated, to be
more specific). We can generate bounded/unbounded versions, definite/indefinite versions, and any combination of
those.
Although it is certainly true that using holders works, it is not applicable when designing a containers library that intends to be
mostly compatible with Ada containers. The latter have chosen, long ago and before Holder was a thing, to have definite and
indefinite versions. The main benefit to this approach is that users still retrieve directly the type they are interested in (e.g. String)
rather than a holder-to-string.
Randy Brukardt:
Assuming otherwise is certainly premature optimization.
I am quoting a bit out of context, though I believe it is close enough. Designers of containers must care about performance
from the get-go. Otherwise, people might just as well use a list for everything and just traverse the list all the time. We all
know this would be way too inefficient, of course, which is why there
are various sorts of containers. Anyone who has actually written performance-sensitive code knows that memory allocations is definitely something to watch out for, and the library design should definitely take that into account.
On 2022-09-03 02:07, Randy Brukardt wrote:
One
principle that we insisted on was that a single instantiation was the
maximum we would use
Except for queues
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 344 |
Nodes: | 16 (2 / 14) |
Uptime: | 81:39:12 |
Calls: | 7,537 |
Calls today: | 2 |
Files: | 12,720 |
Messages: | 5,645,119 |