Version 1.7 of ai12s/ai12-0215-1.txt

Unformatted version of ai12s/ai12-0215-1.txt version 1.7
Other versions for file ai12s/ai12-0215-1.txt

!standard 12.3.1(0)          17-01-09 AI12-0215-1/01
!class Amendment 17-01-09
!status Hold 13-0-0 20-09-09
!status work item 18-05-07
!status Hold by Letter Ballot failed (8-2-1) - 18-05-07
!status work item 17-01-09
!status received 16-10-13
!priority Low
!difficulty Hard
!subject Implicit instantiations
!summary
*** TBD.
!problem
Declaring a stand-alone object of a generic type can be unnecessarily verbose, requiring the introduction of extra declaration and name (with the extra cognitive overhead of such declarations).
There should be a short-hand for simple generic objects.
!proposal
[This is not quite wording - Author.]
A stand-alone object can be declared with an anonymous instance subtype. This defines a generic instance along with declaring the object.
We have a new kind of object_declaration (we already have 5, so what's one more??)
defining_identifier_list : [aliased] [constant] anonymous_instance_type [:= expression]
[aspect_specification];
anonymous_instance_type ::= *generic_package_*name.*subtype_*identifier [generic_actual_part]
The generic_package_name shall name a generic package (called G here). The subtype_identifier shall name a tagged type declared in the visible part of G. The generic_actual_part is interpreted as specified in Section 12 for G, all of the rules for an instance of G apply.
The aspect_specification applies to the object(s); if an aspect_specification is needed for the instance, an explicit named instance will be required.
Similar to a prefixed view, we allow "generic prefixed names" for an object has an anonymous instance type. Such an object can prefix:
* types and subtypes declared in the visible part of the generic package;
* exceptions declared in visible part of the generic package;
* objects declared in the visible part of the generic package;
* generic packages declared in the visible part of the generic package;
* subprograms declared in the visible part of the generic package that do not have any parameters of a tagged type.
Reason: The restriction against parameters of a tagged type are to avoid confusion between prefixed views and generic prefixed names of the same subprogram. We allow other subprograms mainly because we want to have some way to call cursor operations of containers.
[Editor's note: This last allowance might be more confusing than it is worth.]
The dynamic semantics of an object of an anonymous instance type is as if an anonynous instantiation of the generic appears immediately before the object declaration.
That is:
X : Ada.Containers.Vectors.Vector(Integer, My_Record);
is equivalent to:
package <Anon> is Ada.Containers.Vectors(Integer, My_Record); X : <Anon>.Vector;
!wording
** TBD.
!discussion
This proposal is purely syntactic sugar which allows eliminating the instance declaration for instances that are only used to declare a single group of objects.
The original e-mail proposal included some structural equivalence features. Such features should be considered separately, as tying some desirable property to some other property is almost always a mistake. We do not want people writing generics just so that they can get some form of structural equivalence! Nor do we want people using anonynmous instances just for structural equivalence (as in such a case, falling back to a named instance would not be a possibility).
The original e-mail proposal included some restrictions on the generics that can be instantiated. There seems to be no important reason for such restrictions: the feature is dynamically equivalent to a normal instance and users should not be confused by that. If there is an important need to show the lifetime of the instance, an explicit instance should be used (if it hurts to use a shorthand, don't use a shorthand!)
We limit this feature to tagged types so that the prefix notation is available for calls. This eliminates the need to name the instance for calls using the object. (For untagged types, one has to either name the package or have a use clause on the package or type -- all of which require naming the instance somehow.)
We define generic prefixed names so that common operations can be accomplished without defining an explicit instance. (If, however, these prefixes become ambiguious, then a normal named instance should be used.)
The original e-mail proposal suggested using ' rather than . for generic prefixed names. That would however interfere with object attributes, requiring far more complex resolution for them than is currently used. Since we already have similar uses of . for prefixes, there doesn't seem to be any important reason to use a different selection character. (Again, ambiguity is best fixed with an explicit instance.)
The motivating case for generic prefixed names is using cursors of a container:
X : Ada.Containers.Ordered_Map.Map (Key_Type => Natural, Element_Type => My_Record); C : X.Cursor;
-- The following are all normal prefixed views: X.Insert (Key => 1, New_Item => Some_Rec); X.Insert (Key => 2, New_Item => Another_Rec); C := X.First_Key; -- The following are generic prefixed names: X.Next (C);
procedure Do_It (Obj : in out X.Map) is ...
Note that one could declare multiple objects from a single instance by using generic prefixed names:
Y : X.Map;
This doesn't allow Y to be used in a generic prefixed name, however. (If you really need a name for an instance, it's better to declare it explicitly.) The intention is that generic prefixed names are used sparingly.
!ASIS
** TBD.
!ACATS test
ACATS B-Tests and C-Tests will be needed to check that the new capabilities are supported. (Note that all objectives for generic instances would be fair game for this construct.)
!appendix

From: Tucker Taft
Sent: Thursday, October 13, 2016  3:14 PM

> ... Meta-point: This is IMHO the third time that it turns out that we
> have weaknesses in the generic model that would force us to have
> built-in solutions instead of hybrid library ones, or completely library ones:
>
> 1. The first is the type of generators.
> 2. The second is the type of lambda functions.
> 3. The third is the 'Reduce function/built-in.

For me, this is a higher priority item.  I would very much like to see
instantiation of (stateless) generics as part of declaring an object.  E.g:

    X : Vectors.Vector(Integer);

or

    use Ada.Containers.Vectors;  --  "use" of a generic package

    ...

    X : Vector(Integer);

This would only be permitted for generic packages that have no namable state
(e.g. a pragma No_Namable_State might allow access types, but no package-level
variables), as otherwise you end up with lots of copies of the state associated
with anonymous instances, which is not a good thing.  You might need a way to
name exceptions, constants, and auxiliary types declared in such a generic
package.  Perhaps the most obvious would be something like X'Cursor and
X'Empty_Vector.

Sharing of code across instances would be encouraged (Randy's RR compiler is way
ahead here, of course ;-).  We would have to see whether "structural" type
equivalence rules should be used (i.e. all instantiations with the "same" actual
parameters are presumed the same, and declare the same types/exceptions).   If
not, then if you wanted to share instantiations semantically, you might be
obliged to use something like:

   Y : X'Type;

or
    type Vec_Int is new Vector(Integer);

    X : Vec_Int;
   ...
    Y : Vec_Int;


You could also imagine implicit instantiation of generic subprograms at a call
point, presuming the generic formal parameters are only types that are used
somewhere in the profile of the generic subprogram or in the definition of other
formal parameter types, e.g.

    with Ada.Unchecked_Deallocation;

    ...

       Ada.Unchecked_Deallocation(P);

I believe this would make Ada generics much more usable, and would reduce the
need for special-case syntactic sugar.

****************************************************************

From: Raphael Amiard
Sent: Thursday, October 13, 2016  3:41 PM

>For me, this is a higher priority item.  I would very much like to see
>instantiation of (stateless) generics as part of declaring an object.  E.g:

I agree.

...

>This would only be permitted for generic packages that have no namable state
>(e.g. a pragma No_Namable_State might allow access types, but no package-level
>variables), as otherwise you end up with lots of copies of the state associated
>with anonymous instances, which is not a good thing.  You might need a way to
>name exceptions, constants, and auxiliary types declared in such a generic
>package.  Perhaps the most obvious would be something like X'Cursor and
>X'Empty_Vector.

I also agree with all of the above. I also think that this should go hand in
hand with other enhancements to generics. For example, your vector example
doesn't work for the moment, because you need both an index type and an element
type. If you introduce Manu's and Bob's AI about default parameters in generics,
we could say that the default index type for generic vectors is Positive for
example.


>Sharing of code across instances would be encouraged (Randy's RR compiler is
>way ahead here, of course ;-).  We would have to see whether "structural"
>type equivalence rules should be used (i.e. all instantiations with the
>"same" actual parameters are presumed the same, and declare the same
>types/exceptions).

I think we should use structural equivalence rules, because Ada is generally
missing any form of higher level structural typing, and that causes a lot of
problems. Also people wanting generics with nominal typing already have the
(very logical in that instance) choice of creating instances. Here is a mail I
sent to Steve in a discussion about this topic:

I think I agree with you, at least insofar as I don't think anonymous types were
introduced in Ada for the good reasons, and I think their design suffer from
that, and from the fact designers actually never wanted structural typing
*anywhere* in Ada.

I do think on the other hand that structural typing is a very useful tool. If we
agree that a type system is a way for the programmer to best express verified
invariants about his programs, then structural typing allows the programmer to
express invariants that are useful, and impossible to express with nominal
typing. And I think this greatly cripples Ada, the area suffering most being
generic programming.

Sometimes, I want to be able to express the fact that I want this specific
access type, which satisfies specific invariants (like a Filesystem string
access), and I think Ada is a great language in allowing me to express that.

Some other times, I want to be able to express that I just want an access to a
string, with no particular properties belonging to the access type itself. Same
for arrays, and same for generics (take a vector of Integer as an example). Ada,
in disallowing me to express that:

* Makes the code harder to read. I'll have to verify that this access type, in a
  library for example, is indeed compatible with my access type. That it
  satisfies the invariants, if there are some. I'll have to possibly change file
  when reading a subprogram signature to check that.

* Makes the code less maintainable. I have to know about every possible usage of
  my type and subprogram in advance if I want to keep types unified. If I merge
  code from different code bases, I have to unify types after the fact. I have
  to find a place to put those types, where there isn't any logical one.

* Makes the code less modular. If I have two libraries, both having a type that
  should be equivalent, I cannot make them cooperate without writing some glue
  code.

* Makes higher order generic programming with sharing nearly impossible. I'll
  have to declare ad-hoc instances of generic packages that I need, often
  several times.

I think a lot those issues arise when you deal with an ecosystem that has a lot
of third-party code. The fact that the Ada ecosystem has gravitated towards
monolithic code bases is a big reason why this hasn't been adressed IMHO. But it
also prevents in some fashion growing reusable libraries ecosystem.

I'm ranting now I'm just trying to explain that my peeve in this domain is not
coming out of nowhere. I know that Ada is a strongly nominally typed language at
its core, and I think keeping it coherent is important. I still thing there are
strong arguments for structural typing out there, that we should not ignore in
our reflections.

[End quoted e-mail.]

Anyway, thinking on it, I wonder if I don't agree with you, and if I don't think
that this is a much more important topic than lambdas and generators.

****************************************************************

From: Randy Brukardt
Sent: Thursday, October 20, 2016  3:39 PM

...
> We would have
> to see whether "structural" type equivalence rules should be used
> (i.e. all instantiations with the "same" actual parameters are
> presumed the
> same, and declare the same types/exceptions).   If not, then
> if you wanted to share
> instantiations semantically, you might be obliged to use something
> like:
>
>    Y : X'Type;

Ignoring the larger topic here, I don't think that making some corner-case
feature structurally equivalent is a very good idea. We already tried that
with anonymous access types, and what it tends to do is encourage people to
use a feature that they otherwise would not. I'd hate to see people wrapping
all of their types into generics just so they can program like they do in C.
(And that's probably unfair to C; it has better typing than that.)

Specifically, we already looked at X'Type in AI12-0123-1. It was a can of
worms, and I don't see how tying it to an instantiation would change that.
First of all, of course, is that "X'Type" is meaningless; it has to be some
sort of subtype. Certainly, we don't want to allow declaring composite objects
having lost the original constraints! There's more in AI12-0123-1 and the
associated meeting minutes, I'm sure.

> or
>     type Vec_Int is new Vector(Integer);
>
>     X : Vec_Int;
>    ...
>     Y : Vec_Int;

This seems to be the more Ada-like solution.

An implicit instantiation would be a short-hand for packages/types that you
are only going to use once (there are a lot of those in the containers). If
you need to use more than once, do it explicitly.

****************************************************************

From: Randy Brukardt
Sent: Thursday, October 20, 2016  3:54 PM

>I also agree with all of the above. I also think that this should go hand in
>hand with other enhancements to generics. For example, your vector example
>doesn't work for the moment, because you need both an index type and an
>element type. If you introduce Manu's and Bob's AI about default parameters
>in generics, we could say that the default index type for generic vectors
>is Positive for example.

Ignoring the larger points again:

(1) I don't see why Tucker's proposal wouldn't work. Surely it would allow
multiple actual parameters (surely everything else does).

    X : Ada.Containers.Vectors.Vector(Integer, Positive);

seems fine to me.

(2) The default generic parameter AI dates to June 12, 2002, proposed by
Thomas Wolf. My reaction to his proposal: "Thanks for writing this, it saved
me from having to do it. (I was going to make a similar proposal when I got a
round tuit.)" Where he got the idea, I can't be sure (there was some
comp.lang.ada discussion at that time that might be interesting to look up).
But it is WAAAAY older than the recent proposal. In addition, I did not use
their proposal at all, since it was NN (ACATS for "Nothing New") and it was
missing parts ("in out" parameters in particular). In any case, giving Manu
and Bob any credit for this idea is really wrong; it way predates that. (To
be fair, Bob may have had the idea before 2002 since it would have made sense
in the context of Ada 9x, but I don't have any evidence of that.)

****************************************************************

From: Tucker Taft
Sent: Thursday, October 20, 2016  4:07 PM

> Ignoring the larger topic here, I don't think that making some
> corner-case feature structurally equivalent is a very good idea. We
> already tried that with anonymous access types, and what it tends to
> do is encourage people to use a feature that they otherwise would not.
> I'd hate to see people wrapping all of their types into generics just so
> they can program like they do in C. (And that's probably unfair to C; it
> has better typing than that.)
>
> Specifically, we already looked at X'Type in AI12-0123-1. ...

That was more of a straw man.  I am in favor of structural equivalence so you
*don't* need to use something like X'Type.

>> or
>>     type Vec_Int is new Vector(Integer);
>>
>>     X : Vec_Int;
>>    ...
>>     Y : Vec_Int;
>
> This seems to be the more Ada-like solution.

It still forces a separate instantiation, which really interferes with
appropriate uses in my view.

> An implicit instantiation would be a short-hand for packages/types
> that you are only going to use once (there are a lot of those in the
> containers).

I suppose anonymous array types in Ada are still useful, so even if you didn't
have structural equivalence, you could create singleton vectors if we allowed:

   X : Vector(Integer);

but say that each such usage is a distinct type.  But I would still prefer the
structural equivalence approach here, and if you want a unique type (aka
"branded" type in Modula-3 parlance), you can declare it with the "type
Vec_Int is new ..." syntax. We are talking about an abstract type in any case,
since this is the result of an instantiation of a generic, so it isn't
particularly similar to allowing free conversion between all numeric types.
We could go further and allow it only for types that are a private view, but
that that seem to be a bit of overprotection of the programmer.

> ... If you need to use more than one, do it explicitly.

Seems like this might be taking strong typing a bit too far.  Anonymous access
types were more of a challenge because of accessibility.  I would suggest that
these instantiations inherit the accessibility of the generic actual
parameters, so structurally equivalent instantiations would necessarily have
the same accessibility.

****************************************************************

From: Bob Duff
Sent: Thursday, October 20, 2016  4:38 PM

>...In any case, giving Manu
> and Bob any credit for this idea is really wrong; it way predates
>that. (To  be fair, Bob may have had the idea before 2002 since it
>would have made  sense in the context of Ada 9x, but I don't have any
>evidence of that.)

It's an obvious idea, so I'm sure I and many others have independently
"invented" it since long ago.  Some kinds of parameters (generic formal, and
normal subp params) allow defaults; others don't.  That's an obvious
inconsistency.

I don't know why Manu left out generic formal 'in out's. I did mention it to him
at some point.  I guess they're so little used that people forget about them.

Note that Ada originally allowed defaults on 'out' and 'in out'
parameters of subprograms.  That was deleted before 1983.

****************************************************************

From: Randy Brukardt
Sent: Thursday, October 20, 2016  5:42 PM

> >...In any case, giving Manu
> > and Bob any credit for this idea is really wrong; it way predates
> >that. (To  be fair, Bob may have had the idea before 2002 since it
> >would have made  sense in the context of Ada 9x, but I don't have any
> >evidence of that.)
>
> It's an obvious idea, so I'm sure I and many others have independently
> "invented" it since long ago.  Some kinds of parameters (generic
> formal, and normal subp params) allow defaults; others don't.  That's
> an obvious inconsistency.

Right. I didn't want to imply that you were stealing the idea (but I probably
managed that anyway). There's a reason that I try to refer to AIs by number and
not by the author. In this case, there was a worked out proposal from years ago,
and that seemed like a better starting point than a new write-up that hadn't
been vetted.

I do find it interesting that Tucker circa 2016 finds the use of "use" in this
context just fine, while it was Tucker circa 2002 that found it completely
unacceptable (according to the Vienna meeting minutes) which led to the
proposal's eventual death.

...
> I don't know why Manu left out generic formal 'in out's.
> I did mention it to him at some point.  I guess they're so little used
> that people forget about them.

It was mentioned in the e-mail thread that was glued onto the proposal. The
omission sealed-the-deal for me to use the old proposal , since it handled that.

> Note that Ada originally allowed defaults on 'out' and 'in out'
> parameters of subprograms.  That was deleted before 1983.

Probably before 1980, since I don't remember that at all. (And Janus/Ada started
out as an Ada-80 compiler, since 1983 was two years in the future. :-)

****************************************************************

From: Randy Brukardt
Sent: Monday, January 9, 2017  9:07 PM

...
> I would very much
> like to see instantiation of
> (stateless) generics as part of declaring an object.  E.g:
>
>     X : Vectors.Vector(Integer);

I've written up a basic proposal for this feature as purely syntactic sugar.
Hopefully, this will give us a decent starting place for discussion.
[This was version /01 of the AI - Editor.]

---

I purposely did not address any structural equivalence ideas in this proposal.
While I remain open to further examples on this point, I am absolutely not
interested in tying any important new functionality to anonymous declarations
(be them new or old functionality).

We almost always come to regret tying one new feature with some other new
feature. (Bob has said this better than I several times in the past.) I
definitely don't want to see people writing generic units just so that they
can get structural type equivalence, nor do I want to see people writing
anonymous instances just for this purpose when otherwise a normal declaration
would do.

Anonymous anything should NEVER have capabilities that the otherwise equivalent
named entity do not have. Take anonymous access-to-subprogram parameters
(please! :-):
   * You cannot attach a predicate to an anonymous access-to-subprogram
     parameter;
   * Given adoption of another current proposal, you cannot attach a
     precondition or postcondition to an anonymous access-to-subprogram
     parameter [aside: and sticking it into the subprogram profile is a
     non-starter, the result would be completely unreadable most of the time];
   * You cannot share a complex definition of an anonymous
     access-to-subprogram parameter;
   * You cannot reasonably extend the facility of calling any subprogram
     indirectly to anything other than parameters. [Whether that would make
     sense is irrelevant now, because it's not possible.]
[Note: Erhard raised these concerns at the time, but we junked the idea of
"named anonymous access-to-subprogram types" for obvious reasons. The problem
though was tying any part of this idea to "anonymous"; if we had given the
concept some silly name - say "frobisher" (thanks Steve ;-), the idea of
"named frobisher access-to-subprogram types" and "anonymous frobisher
access-to-subprogram types" seems fine.]

I'm getting off-topic, but hopefully you see my point.

****************************************************************

From: Tucker Taft
Sent: Monday, January 9, 2017  9:07 PM

> I purposely did not address any structural equivalence ideas in this
> proposal. While I remain open to further examples on this point, I am
> absolutely not interested in tying any important new functionality to
> anonymous declarations (be them new or old functionality).
>
> We almost always come to regret tying one new feature with some other
> new feature. (Bob has said this better than I several times in the
> past.) I definitely don't want to see people writing generic units
> just so that they can get structural type equivalence, nor do I want
> to see people writing anonymous instances just for this purpose when
> otherwise a normal declaration would do.

When I said "structural equivalence" what I meant was that two instantiation
short-hands with the same parameters would refer to the same implicit
instantiation.  So there is only one type, and only one instance, which is
being used in all places where the short-hand specifies the same parameters.
This is important so as to avoid code bloat, and is generally what you want
when using this sort of short-hand for generic instantiation.

This implies there should be no state associated with the generic, or else
you can unintentionally create race conditions, etc.

If we believe it is important to allow implicit instantiations of state-ful
generics (something about which I am not personally convinced), then I would
propose we define an aspect to distinguish the two approaches.  Given that
very few Ada compilers implement generic sharing, this feature is of much
more limited use if it produces extraordinary code bloat.

> Anonymous anything should NEVER have capabilities that the otherwise
> equivalent named entity do not have.

I am not proposing that.  I am merely proposing that the syntactic sugar
involves a more globally instantiated generic, rather than a new one
instantiated at each point of use.

****************************************************************

From: Randy Brukardt
Sent: Monday, January 9, 2017  11:13 PM

> When I said "structural equivalence" what I meant was that two
> instantiation short-hands with the same parameters would refer to the
> same implicit instantiation.

Raphael was talking about something more general in his messages, and that's
mainly what I responded to. But...

> ...  So there is only
> one type, and only one instance, which is being used in all places
> where the short-hand specifies the same parameters.
> This is important so as to avoid code bloat, and is generally what you
> want when using this sort of short-hand for generic instantiation.

I stand by my statement: I don't want special semantics just for anonymous
anything instead of for all anythings.

In this case, I don't really see the problem. If someone is using this
short-hand so much that they're getting code bloat, the fix is easy enough.
You can either write a named instance, or you can use the generic prefixed
name of the type to share the type with another object. You're doing it
wrong if you write a lot of instances, implicit or otherwise.

Moreover, I suspect this semantics would be a can of worms. There are a lot
of generic parameters in the typical instance (including the container
instances - ordered_maps has 4 parameters, for instance), not just types. If
any of those parameters are local (that is, the types or subprograms are
declared locally), you really do not want to be hoisting these instances out
of their scope. And if you're only going to do this in a single scope, you
aren't going to prevent code bloat unless the code is rather unusual.

At a minimum, you'll need rules about actual parameter matching to deal with
when this is and is not done, plus the nesting implications. Sounds like a
lot of work.

> This implies there should be no state associated with the generic, or
> else you can unintentionally create race conditions, etc.

Which is another reason that some sort of implicit instance sharing is a bad
idea. All that seems to do is add complications.

If we do this at all, let it be exactly syntactic sugar, with no semantic
changes.

> If we believe it is important to allow implicit instantiations of
> state-ful generics (something about which I am not personally
> convinced), then I would propose we define an aspect to distinguish
> the two approaches.

It's hard to write a useful generic that doesn't have some sort of state.
The Janus/Ada containers have state (used to detect dangling cursors). I will
not stand for a requirement that the containers packages (or any other
language-defined generics, for that matter, with the obvious exception of the
pure packages) have no state.

As such, there would be hardly any cases where you could use this feature on a
stateless generic, so there would be little value to this compared with the
complications that it would bring.

[Aside: As I wrote the proposal, there is no possibility of having a
discriminant constraint, so one couldn't use this with the bounded containers.
There might be some way to fix that (the wording would be a mess, but if
someone else wants to try it, be my guest), but such an instance would look
really strange:
    B : Ada.Containers.Bounded_Vectors.Vector(100)(Integer, My_Rec);
Only the bounded containers are Pure and thus they'd be the only containers
allowed with Tucker's scheme.]

> Given that very
> few Ada compilers implement generic sharing, this feature is of much
> more limited use if it produces extraordinary code bloat.

That's only a problem if someone is used extraordinary numbers of instances.
I really don't think we ought to be designing syntax sugar around unusual
cases. Moreover, it only works if the instances actually can be hoisted (that
depends on where the actual parameters are declared, as I previously noted).

> > Anonymous anything should NEVER have capabilities that the otherwise
> > equivalent named entity do not have.
>
> I am not proposing that.  I am merely proposing that the syntactic
> sugar involves a more globally instantiated generic, rather than a new
> one instantiated at each point of use.

Yes you are. You're adding a poor-man's sharing mechanism, and only allowing
it to be used from this anonymous instance. If this was a good idea, it
should work for any instance, named or anonymous, so that one can switch
between them without causing semantics changes.

But I don't think this is a good idea, even without considering sharing.
There are too many ways for this sort of thing to go wrong: not just local
data (state) in the generic, but also local types and subprograms, and the
issues of matching arbitrary lists of actual parameters [and across just how
much program text???].

****************************************************************

From: Jean-Pierre Rosen
Sent: Monday, January 9, 2017  11:49 PM

It strikes me that many new proposals are just about ease of writing... Sigh


> When I said "structural equivalence" what I meant was that two
> instantiation short-hands with the same parameters would refer to the
> same implicit instantiation.  So there is only one type, and only one
> instance, which is being used in all places where the short-hand
> specifies the same parameters.

Even if the two implicit instantiations are within different compilation
units?

****************************************************************

From: Randy Brukardt
Sent: Tuesday, January 10, 2017  12:04 AM

> It strikes me that many new proposals are just about ease of
> writing... Sigh

At least most such proposals have a "low" priority.

...
> > When I said "structural equivalence" what I meant was that two
> > instantiation short-hands with the same parameters would refer to
> > the same implicit instantiation.  So there is only one type, and
> > only one instance, which is being used in all places where the
> > short-hand specifies the same parameters.
> Even if the two implicit instantiations are within different
> compilation units?

Yes, I wondered that too. If it is only within one program unit, it would
seem to have little effect on code bloat unless the code is very unusual.
If it is within an entire compilation unit, all kinds of worms show up, and
I still doubt the effect on code bloat. You have to share generics across
compilation units to get much benefit to sharing, some of that is the "rules"
people have learned from using other compilers, but I think a lot of it is
natural. (Especially if we're only talking about container-like generics, as
in this proposal.)

****************************************************************

From: Jean-Pierre Rosen
Sent: Tuesday, January 10, 2017  12:17 AM

I'm afraid that it would end up with something like: shared within a program
unit, but duplicated across different program units... which is a sure
recipe for disaster.

****************************************************************

From: Tucker Taft
Sent: Tuesday, January 10, 2017  7:10 AM

>> When I said "structural equivalence" what I meant was that two
>> instantiation short-hands with the same parameters would refer to the
>> same implicit instantiation.  So there is only one type, and only one
>> instance, which is being used in all places where the short-hand
>> specifies the same parameters.
> Even if the two implicit instantiations are within different
> compilation units?

Yes, it would be independent of where the instantiation occurred, so long as
the actuals match.  Note that we already have a pretty good definition of what
it means for generic actuals to match, thanks to the definition of formal
packages.

****************************************************************

From: Jean-Pierre Rosen
Sent: Tuesday, January 10, 2017  7:36 AM

But where would the instantiation reside?

i.e. if there is a first instantiation deep in a sub-sub-procedure inside a
package body, how can it be shared with an instantiation in a different
library package? What happens if the first one is recompiled (without the
instantiation)? Does it create dependences between unrelated package bodies?

****************************************************************

From: Tucker Taft
Sent: Tuesday, January 10, 2017   9:13 AM

This problem is faced by all languages that share generic instantiations based
on their actuals, including Dec Ada at one point, I believe (RR Ada does
universal sharing, so you just compile the generic body once).

The instantiations are given a unique name based on their actuals.  The code
is then generated into a separate file, one per instantiation, or is put into
something like "common block" sections such that the linker only pulls in one
of them.  In the C++ world, these two approaches are called the "Cfront" model
and the "Borland" model, for historical reasons.  Since most linkers now
support the ability to pull in just one copy of a same-named
"linkonce"/"comdat" section from multiple object modules, the "Borland" model
is used more frequently now.

****************************************************************

From: Randy Brukardt
Sent: Tuesday, January 10, 2017   4:25 PM

In this model, where is the instance elaborated? (This matters for all
instances, as at least the formals need to be evaluated.) How you do deal with
actual parameters that are local to some subprogram if the instance is
elaborated at the library level?

It seems to me that this cannot be used as a general technique in Ada; one has
to limit its applicability both based on the actual parameters and on the
characteristics of the body (as you say, this only works sanely if there is no
state). As such, this seems like a fine implementation technique.

But I fail to see why it is a good idea to tie this technique solely to
anonymous instances, as opposed to using it for all qualified instances.
(After all, sharing implementations is a reasonable as-if optimization.) You
don't want to be putting bizarre restrictions on anonymous instances (such as
no local types or subprograms). And there's no need to make the instance work
differently than if it was explicitly declared at the same place (there's no
need to make the sharing visible to the programmer).

So I could see encouraging this sort of implementation somehow, but not
changing any semantics to in effect force it. (The language has nothing to say
about code size of implementations anyway, else some sort of generic sharing
would have been mandated decades ago.)

****************************************************************

From: Tucker Taft
Sent: Tuesday, January 10, 2017   5:14 PM

> In this model, where is the instance elaborated? (This matters for all
> instances, as at least the formals need to be evaluated.) How you do
> deal with actual parameters that are local to some subprogram if the
> instance is elaborated at the library level?

You can't share at a level any more global than the actuals.  So that case is
simpler, since everything ends up as nested within the subprogram.

For library-level instantiations, the elaboration happens at the library
level. This is similar to asking when package System is elaborated when a use
of 'Address appears.  It is elaborated before any reference.

> It seems to me that this cannot be used as a general technique in Ada;
> one has to limit its applicability both based on the actual parameters
> and on the characteristics of the body (as you say, this only works
> sanely if there is no state). As such, this seems like a fine
> implementation technique.

I guess I don't understand your point.  We want these to be semantically all
the same instance, declaring only a single type, a single set of exceptions,
etc. That is clearly visible at the semantic level, so you can't just sweep
this under the implementation-technique "rug." I believe the formal package
rules for matching given in RM 12.7(5.3-8) are probably adequate to determine
if two "short-hands" are referring to the same instance.

> But I fail to see why it is a good idea to tie this technique solely
> to anonymous instances, as opposed to using it for all qualified instances.
> (After all, sharing implementations is a reasonable as-if
> optimization.) You don't want to be putting bizarre restrictions on
> anonymous instances (such as no local types or subprograms). And
> there's no need to make the instance work differently than if it was
> explicitly declared at the same place (there's no need to make the sharing
> visible to the programmer).

Here is where we disagree. The point is that "Vectors.Vector(Integer)" is a
single type, interchangeable with all other "Vectors.Vector(Integer)" since
they all refer to the same unique instance.

> So I could see encouraging this sort of implementation somehow, but
> not changing any semantics to in effect force it. (The language has
> nothing to say about code size of implementations anyway, else some
> sort of generic sharing would have been mandated decades ago.)

I think you are missing my fundamental point here.

****************************************************************

From: Randy Brukardt
Sent: Tuesday, January 10, 2017  5:49 PM

...
> You can't share at a level any more global than the actuals.
> So that case is simpler, since everything ends up as nested within the
> subprogram.

In other words, even more complication (how the declaration is processed
depends on the actuals). Yuck.

> For library-level instantiations, the elaboration happens at the
> library level.  This is similar to asking when package System is
> elaborated when a use of 'Address appears.  It is elaborated before
> any reference.
>
> > It seems to me that this cannot be used as a general technique in
> > Ada; one has to limit its applicability both based on the actual
> > parameters and on the characteristics of the body (as you say, this
> > only works sanely if there is no state). As such, this seems like a
> > fine implementation technique.
>
> I guess I don't understand your point.  We want these to be
> semantically all the same instance, declaring only a single type, a
> single set of exceptions, etc. That is clearly visible at the semantic
> level, so you can't just sweep this under the implementation-technique
> "rug."

True, but why do you want this? It doesn't seem necessary for the motivating
usage of the feature: to replace an anonymous array declaration by a vector
container.

That is, if I have:

    Buf1 : array (1..MAX_BUF) of Some_Record;
    Buf2 : array (1..MAX_BUF) of Some_Record;

the fact that these are different types is a non-issue. (I have lots of
declarations like this in my code.) If I wanted these to be unbounded vectors
instead:

    Buf1 : Ada.Containers.Vectors.Vector (Natural, Some_Record);
    Buf2 : Ada.Containers.Vectors.Vector (Natural, Some_Record);

The same would be true: the fact that these are different types doesn't
matter.

More generally, one might use an anonymous type in a scenario where operations
on the entire type (object) are not anticipated. I would expect the same to be
true here (if you need to pass Buf1 to some other generic, or to interassign
these, it's better that the instance has a first-class name). [Aside: The
proposal I made does not actually require a named instance to do that, in that
sense it is better than an anonymous array -- if you just need a type name in
some single place, you can get it without introducing another name.]

And if there IS a good reason to support structural equivalence in some cases
(as Raphael argued in the original thread), one surely does not want to force
people to use anonymous instances to get it (which could force people to use
generics in cases where they are not needed).

> I believe the
> formal package rules for matching given in RM 12.7(5.3-8) are probably
> adequate to determine if two "short-hands" are referring to the same
> instance.
>
> > But I fail to see why it is a good idea to tie this technique solely
> > to anonymous instances, as opposed to using it for all qualified instances.
> > (After all, sharing implementations is a reasonable as-if
> > optimization.) You don't want to be putting bizarre restrictions on
> > anonymous instances (such as no local types or subprograms). And
> > there's no need to make the instance work differently than if it was
> > explicitly declared at the same place (there's no need to make the
> > sharing visible to the programmer).
>
> Here is where we disagree.  The point is that
> "Vectors.Vector(Integer)" is a single type, interchangeable with all
> other "Vectors.Vector(Integer)" since they all refer to the same
> unique instance.

Again, why is that a good thing? It seems to damage the Ada type model for no
reason (all other anonymous types are distinct, after all).

> > So I could see encouraging this sort of implementation somehow, but
> > not changing any semantics to in effect force it. (The language has
> > nothing to say about code size of implementations anyway, else some
> > sort of generic sharing would have been mandated decades ago.)
>
> I think you are missing my fundamental point here.

You want to adopt an unnecessarily complex semantics here so you can convince
your employer to implement an optimization that arguably they should implement
anyway? :-)

I don't think I missed it, I just see no justification for it whatsoever. Thus
I'm ignoring it, since it isn't fundamental to the description or usage of
this shorthand.

You'll need to explain why this is valuable semantics for these instances.
[And in the abstract, not reasons that have nothing to do with the Standard
like "code bloat" - implementations have plenty of ways to avoid "code bloat"
for instances should they care to use them; it's not our job to try to force
implementation techniques.]

****************************************************************

From: Tucker Taft
Sent: Tuesday, January 10, 2017  11:03 PM

>> ... I think you are missing my fundamental point here.
>
> You want to adopt an unnecessarily complex semantics here so you can
> convince your employer to implement an optimization that arguably they
> should implement anyway? :-)

It seems we aren't on the same wavelength here.

> I don't think I missed it, I just see no justification for it whatsoever.
> Thus I'm ignoring it, since it isn't fundamental to the description or
> usage of this shorthand.

Well, presume you define a non-generic operation that takes a
Vectors.Vector(My_Integer) as a parameter.  That seems like a pretty normal
thing to do, and I would like to pass it any object declared as
"Vectors.Vector(My_Integer)."  That seems pretty fundamental to me.

> You'll need to explain why this is valuable semantics for these instances. ...

This seems like a fundamental capability once you have a type constructor like
this which you could use to declare the type of a formal parameter, so I am
surprised you see it as unimportant.

****************************************************************

From: Raphael Amiard
Sent: Wednesday, January 11, 2017  4:15 AM

>> You want to adopt an unnecessarily complex semantics here so you can
>> convince your employer to implement an optimization that arguably
>> they should implement anyway? :-)
>
> It seems we aren't on the same wavelength here.

Yes, Randy, structural generic equality is not about code size optimization
IMHO, it's about modularity.

If you have the following code in library 1, from vendor A:

procedure Frobulize (Vec : Vectors.Vector (Natural, Integer));

And the following code in library 2, from vendor B:

function Create_Sequence return Vectors.Vector (Natural, Integer);

You want to be able to use those two subprograms together seamlessly,
without using conversion functions.

This is already a problem, and a missing feature, without inline generic
types. However, this gets much worse if you allow inline generic types,
because with the code above, you intuitively expect those two inline types
to be compatible, and will cause much hair pulling if you break that
intuition.

Regarding your point:

> I purposely did not address any structural equivalence ideas in this
> proposal. While I remain open to further examples on this point, I am
> absolutely not interested in tying any important new functionality to
> anonymous declarations (be them new or old functionality).

I have no strong opinion on that. I agree that having a structural equivalence
feature that is in some way orthogonal would be good, and would allow users to
benefit from the semantic benefits without using the inline notation.

However, I don't think that inline types with nominal semantics rather than
structural make any sense at all, for the reasons outlined above, so I would
still argue that structural equivalence should be the default rules in the
case of inline types.

Tucker, I think we have the same underlying idea, but please expand/correct me
there if I'm mistaken.

****************************************************************

From: Jean-Pierre Rosen
Sent: Wednesday, January 11, 2017  5:09 AM

> If you have the following code in library 1, from vendor A:
>
> procedure Frobulize (Vec : Vectors.Vector (Natural, Integer));
>
> And the following code in library 2, from vendor B:
>
> function Create_Sequence return Vectors.Vector (Natural, Integer);
>
> You want to be able to use those two subprograms together seamlessly,
> without using conversion functions.

Sorry, but I don't accept this example, because it is very bad Ada to have
vectors of Integer indexed by Integer. What do they represent?

Ada is about STRONG TYPING. Never use Integer. Use types that model problem
space. Do you think that two different vendors will need vectors of -say-
Length, without having some common package where this abstraction is defined?
And shouldn't this abstraction (explicitely) instantiate vectors of Length if
needed?

Side note: I'm a bit hot on this topic, because I just finished an expertise
where the only types they used were Integer and Boolean. Not even Positive
when indexing strings! The result is an horrible mess, where they gained
nothing from writing in Ada.

I am not interested in features that help people write bad Ada.

****************************************************************

From: Tucker Taft
Sent: Wednesday, January 11, 2017   7:14 AM

> Ada is about STRONG TYPING. Never use Integer. Use types that model
> problem space. Do you think that two different vendors will need
> vectors of -say- Length, without having some common package where this
> abstraction is defined? And shouldn't this abstraction (explicitely)
> instantiate vectors of Length if needed?

Fair enough.  Presume we have an example like

    procedure Print_Recs (Patients : Vectors.Vector(Patient_ID, Patient_Rec));

> Side note: I'm a bit hot on this topic, because I just finished an
> expertise where the only types they used were Integer and Boolean. Not
> even Positive when indexing strings! The result is an horrible mess,
> where they gained nothing from writing in Ada.
>
> I am not interested in features that help people write bad Ada.

Fine.  I don't see this as affecting whether or not people use "Integer"
rather than "Patient_ID"

****************************************************************

From: Jean-Pierre Rosen
Sent: Wednesday, January 11, 2017  8:27 AM

> Fair enough.  Presume we have an example like
>
>    procedure Print_Recs (Patients : Vectors.Vector(Patient_ID, Patient_Rec));

Well, I would expect Patient_ID and Patient_Rec to be declared in some
package, and if there is a need to maintain lists of patients, there would
certainly be an explicit instantiation of Vectors. Or even better,
operations to manipulate lists of Patients, with Vectors buried in the body.
I see no need for implicit instantiations here.

>> I am not interested in features that help people write bad Ada.
>>
> Fine.  I don't see this as affecting whether or not people use "Integer"
> rather than "Patient_ID"

I have a (not scientifically supported) feeling that people who structure
their code correctly, with appropriate abstract data types, don't need these
implicit instantiations. Only those who program computers* with Integer and
Boolean need this.

* I keep saying in my courses: "stop programming computers! Design software
  applications".

****************************************************************

From: Tullio Vardanega
Sent: Wednesday, January 11, 2017  8:34 AM

I think JP has a point here, which may well severely contrast with the
implementors' wish of being able to "digest", without too much pain, user
code that might have been written differently for the better.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, January 11, 2017  8:58 AM

Finding the "right" place to put these instantiations can be a challenge.
What generally happens is you end up with several, because you have relatively
independent packages that both want to take a "Vector(Blah)."  We have had
heartache in the past over trying to decide where to declare "type String_Ptr
is access String;".  Similar examples might be "Set(Character)" or packed
array of Boolean.

Once you start programming regularly with Sets, Maps, Vectors, etc., it can be
a royal pain to always have to figure out where to put the instantiations.  In
my experience, most of us haven't reached that point, but after programming a
lot in ParaSail, where Sets, Maps, Vectors, etc., are pretty much all you use,
I can't imagine having to figure out where I would have to put all the various
instantiations, if they were explicit.

Sometimes I do create a short-hand using something like "type Sym_Info_Map is
Map<Symbol, Info>" or equivalent, which is easy to do, but many other times
there is no natural place to put that, and in any case, these are just short
hands (equivalent to subtype declarations in Ada).

Ada's need for explicit instantiations is one of the most obvious places (to
me) where Ada feels heavier weight than other languages.  I really don't think
this is enhancing the quality of Ada programs.  It just feels more like a
"hoop" you have to jump through, and sometimes it creates dependences between
compilation units that really don't belong there.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, January 11, 2017  2:02 PM

...
> Yes, Randy, structural generic equality is not about code size
> optimization IMHO, it's about modularity.
>
> If you have the following code in library 1, from vendor A:
>
> procedure Frobulize (Vec : Vectors.Vector (Natural, Integer));

Ada only allows subtype_marks in profiles, and that restriction exists for
semantic reasons having to do with conformance and elaboration -- it is not
some sort of methodological restriction.

I can't imagine changing that, but even if we were going to change it, it
ought to be generally applied and not tied to just this wacky construct.

So you're worrying about something that is very unlikely to be ever part of
the Ada model.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, January 11, 2017  2:58 PM

...
> It seems we aren't on the same wavelength here.
>
> > I don't think I missed it, I just see no justification for it whatsoever.
> > Thus I'm ignoring it, since it isn't fundamental to the description
> > or usage of this shorthand.
>
> Well, presume you define a non-generic operation that takes a
> Vectors.Vector(My_Integer) as a parameter.  That seems like a pretty
> normal thing to do, and I would like to pass it any object declared as
> "Vectors.Vector(My_Integer)."  That seems pretty fundamental to me.

I view this as an analog of the anonymous array type. So let's try the above
statement replacing the new construct with the anonymous array type "array
(1..Max) of Rec":

  Well, presume you define a non-generic operation that takes a
  "array (1..Max) of Rec" as a parameter.  That seems like a
  pretty normal thing to do, and I would like to pass it any
  object declared as ""array (1..Max) of Rec".  That seems
  pretty fundamental to me.

And that seems like a pretty silly statement to me, since it has never been
true in Ada and clearly the world has not fallen in. So it can't be *that*
fundamental.

Indeed, neither of these constructs (anonymous array or anonymous instance)
would ever be allowed in a parameter profile in Ada (as I mentioned to
Raphael), so this is a complete non-concern.

> > You'll need to explain why this is valuable semantics for these
instances. ...
>
> This seems like a fundamental capability once you have a type constructor
> like this which you could use to declare the type of a formal parameter, so
> I am surprised you see it as unimportant.

I've been privately criticized for making absolutist statements here, but I
think I have no choice in this case, so one is about to appear:

I've always regretted my part in the expansion of anonymous access types in
Ada 2005. They proved complex to implement, make code harder to understand
(both for humans and programs), and with one exception, add no capability.
[That one exception should have been provided some other way, as I outlined
in another message.] Several other ARG members (and members of the general
public as well) have expressed similar sentiments to me; this is not a unique
opinion.

The old saying is "fool me once, shame on you; fool me twice, shame on me".
I do not intend to bring shame onto me. As such, I would need the most
compelling arguments possible (essentially, that no other way could possibly
work) before I would sanction any further expansion to the use of anonymous
types in subprogram parameters. That's ANY kind of anonymous types in
parameter profiles.

So I'd rather not waste any more time worrying about things that are not going
to be part of any future Ada so long as I'm around.

-------

Let's switch to the positive.

I recognize the value of the anonymous array declaration; I use it
periodically to avoid having to invent names for types that are only going to
be used once.

Therefore, I can see the value of a similar construct to be used for
containers. It would therefore have similar restrictions in use (thus, it
only can appear in a stand-alone object declaration), and it creates a new
type for each use. That's the proposal I outlined in the draft AI that I
posted.

I can believe that it could use some polishing, but I'm not willing to go
significantly further than that. (I could see some use to an easier way to
describe generic formal parameters of "any" vector type, for instance -- one
can do that with formal packages, but it is clunky).

****************************************************************

From: Randy Brukardt
Sent: Wednesday, January 11, 2017   3:11 PM

...
> Once you start programming regularly with Sets, Maps, Vectors, etc.,
> it can be a royal pain to always have to figure out where to put the
> instantiations.  In my experience, most of us haven't reached that
> point, but after programming a lot in ParaSail, where Sets, Maps,
> Vectors, etc., are pretty much all you use, I can't imagine having to
> figure out where I would have to put all the various instantiations,
> if they were explicit.

Again, let's think of this statement using "arrays" in place of the
containers:

   Once you start programming regularly with arrays, it can be a royal pain
   to always have to figure out where to put the array type declarations.
   In my  experience, most of us haven't reached that point, but after
   programming a lot in <Fortran??>), where arrays are pretty much all you
   use, I can't imagine having to figure out where I would have to put all
   the various type declarations, if they were explicit.

This probably is a true statement, but it just reflects the basic problem of
programming in Ada (any version of Ada): it's a pain to figure out where to
declare things sometimes. That usually is a symptom of too little abstraction,
but we all fall prey to it from time-to-time (abstraction being hard and
time-consuming -- and usually very rewarding down the line).

Which means that this is nothing new in any sense, and somehow attributing it
to containers (or solving it *only* for containers) is really a mistake.
If one was to solve this at all, one would have to solve it for all kinds of
composite types (array, strings, containers, etc.) -- and one would also have
to be quite certain that the solution isn't worse than the problem (as
happened with anonynous access types).

I'd be interested in seeing ideas in this area, but please let's not attribute
an age-old Ada programming problem to just a single newish feature.

****************************************************************

From: Jean-Pierre Rosen
Sent: Wednesday, January 11, 2017  3:40 PM

> I recognize the value of the anonymous array declaration; I use it
> periodically to avoid having to invent names for types that are only
> going to be used once.

Not having to invent names is not a very good justification... But there is
(IMHO) at least one major value to anonymous types: you know it is a
singleton, that it's the only object of its kind, and this can be a useful
information (especially for single tasks) that you want enforced by the
compiler...

... and that would be immediately broken with structural equivalence!

****************************************************************

From: Tucker Taft
Sent: Wednesday, January 11, 2017  5:04 PM

> ... As such, I would need the most
> compelling arguments possible (essentially, that no other way could
> possibly
> work) before I would sanction any further expansion to the use of
> anonymous types in subprogram parameters. That's ANY kind of anonymous
> types in parameter profiles.
>
> So I'd rather not waste any more time worrying about things that are
> not going to be part of any future Ada so long as I'm around.

I think we have reached the "nub" of the question.  Do we think it is important
to allow type "constructors" in formal parameter specifications?  That is,
should the programmer be allowed to declare a procedure that takes a
Vector(My_Integer) or must they always declare a type (perhaps as a side-effect
of an explicit instantiation)?  I would agree that our experience with anonymous
access types has been less than uniformly positive, though I could argue that
allowing them for stand-alone objects is when things went south, and that is
something which I (amazingly! ;-) did not support originally.  Using them for
parameters was important in my view, and I still find the need for them
regularly (unlike stand-alone objects of an anonymous access type, which I don't
believe I have ever used).

It is true that Ada never allowed an anonymous array type as a parameter type,
while allowing it for stand-alone objects.  I would think there are still
arguments on both sides of this decision (I think we know where you stand), and
I guess I am convinced that we should be consistent -- if we think it is
important to allow "Vector(Positive, My_Rec)" as a formal parameter subtype,
then we should similarly allow "array(Positive range <>) of My_Rec".  I think it
would be worth debating this issue in an ARG meeting, because e-mail does not
provide a good decision process.

The next question would be should we allow implicit instantiations as part of a
type declaration?  E.g.:

    "type Vec_Rec is new Vectors.Vector(Positive, My_Rec);"

Here the array type provides a clearer model, that yes, if we allow it for a
stand-alone object declaration, we should allow it in a type declaration.

This at least allows us to eliminate one level of explicit instantiation.

So perhaps we can agree on some of this, and we can leave the debate about the
implicit instantiation as part of a formal parameter specification for an ARG
meeting (perhaps over a few beers...).  Along with perhaps the discussion of
whether implicit instantations should be permitted for "state-ful" generic
packages, and maybe explicit type conversion rules. ;-)

> ... (I could see some use to an easier way to describe generic formal
> parameters of "any" vector type, for instance -- one can do that with
> formal packages, but it is clunky).

I agree, that is clearly of value, and much more pleasant than the complexities
of formal packages.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, January 11, 2017  6:13 PM

> I think we have reached the "nub" of the question.  Do we think it is
> important to allow type "constructors" in formal parameter
> specifications?  That is, should the programmer be allowed to declare
> a procedure that takes a
> Vector(My_Integer) or must they always declare a type (perhaps as a
> side-effect of an explicit instantiation)?  I would agree that our
> experience with anonymous access types has been less than uniformly
> positive, though I could argue that allowing them for stand-alone
> objects is when things went south, and that is something which I
> (amazingly! ;-) did not support originally.  Using them for parameters
> was important in my view, and I still find the need for them regularly
> (unlike stand-alone objects of an anonymous access type, which I don't
> believe I have ever used).

Luckily you don't work for me, because anyone that "regularly used anonymous
access parameters" here would be subject to termination for not following the
coding guidelines! :-)

The only time I've ever used anonymous access parameters was in the Claw
Builder, and that was as a stand-in for the non-existent "in out" parameters for
functions. Luckily we've fixed *that* lack.

> It is true that Ada never allowed an anonymous array type as a
> parameter type, while allowing it for stand-alone objects.
> I would think there are still arguments on both sides of this decision
> (I think we know where you stand), and I guess I am convinced that we
> should be consistent -- if we think it is important to allow
> "Vector(Positive, My_Rec)"
> as a formal parameter subtype, then we should similarly allow
> "array(Positive range <>) of My_Rec".  I think it would be worth
> debating this issue in an ARG meeting, because e-mail does not provide
> a good decision process.

I certainly agree that we should be consistent. But that's tough because a
parameter profile can't allow anything that requires evaluation/runtime
elaboration. So, for instance, "array(Positive range <>) of My_Rec" is OK, and
"array(Positive range <>) of My_Rec(N)" is not (assuming N is not static). The
complications make it arguable whether it is worth it. (You have similar
problems with anonymous instances, of course.)

> The next question would be should we allow implicit instantiations as
> part of a type declaration?  E.g.:
>
>     "type Vec_Rec is new Vectors.Vector(Positive, My_Rec);"
>
> Here the array type provides a clearer model, that yes, if we allow it
> for a stand-alone object declaration, we should allow it in a type
> declaration.
>
> This at least allows us to eliminate one level of explicit
> instantiation.

My initial thought was that this is fine, (with the obvious cavat that this is a
tagged type, so there has to be a null extension on the type) but then I
remembered that you can't usefully derive a type in a containers package. That's
because a lot of the important operations aren't primitive for a container type
(they're on the cursor type only) and thus they don't come along [formally:
aren't inherited]. (A side issue is that the cursor type ends up being the same,
which is more of a problem for non-null extensions where you may not want the
cursor to point into both old and new containers. I once spent quite a bit of
time trying to solve this problem with "co-derivation", but it didn't seem to
work out well. That's definitely a problem that needs a solution, not just for
the containers but in general).

> So perhaps we can agree on some of this, and we can leave the debate
> about the implicit instantiation as part of a formal parameter
> specification for an ARG meeting (perhaps over a few beers...).  Along
> with perhaps the discussion of whether implicit instantations should
> be permitted for "state-ful"
> generic packages, and maybe explicit type conversion rules. ;-)

That was what I was trying to do with the proposal I made in the AI that started
this thread. :-)

Perhaps you could take a look at it (since obviously I don't object to it) and
make suggestions for extensions as opposed to starting with your similar
proposal that is IMHO an unexceptable mess?

> > ... (I could see some use to an easier way to describe generic
> > formal parameters of "any" vector type, for instance -- one can do
> > that with formal packages, but it is clunky).
>
> I agree, that is clearly of value, and much more pleasant than the
> complexities of formal packages.

I don't have any ideas for that, though, so someone else will need to come up
with one.

****************************************************************

From: Tucker Taft
Sent: Thursday, January 12, 2017  7:03 AM

>> ...

> ... The only time I've ever used anonymous access parameters was in
> the Claw Builder, and that was as a stand-in for the non-existent "in
> out" parameters for functions. Luckily we've fixed *that* lack.

If you are generally passing around access types, and you want to get some
dispatching, then access parameters are quite appropriate, in my experience.  I
agree that using them for function out-parameters is not important any more, and
was somewhat of a kludge before.

>> It is true that Ada never allowed an anonymous array type as a
>> parameter type, while allowing it for stand-alone objects.
>> I would think there are still arguments on both sides of this
>> decision (I think we know where you stand), and I guess I am
>> convinced that we should be consistent -- if we think it is important
>> to allow "Vector(Positive, My_Rec)"
>> as a formal parameter subtype, then we should similarly allow
>> "array(Positive range <>) of My_Rec".  I think it would be worth
>> debating this issue in an ARG meeting, because e-mail does not
>> provide a good decision process.
>
> I certainly agree that we should be consistent. But that's tough
> because a parameter profile can't allow anything that requires
> evaluation/runtime elaboration. So, for instance, "array(Positive
> range <>) of My_Rec" is OK, and "array(Positive range <>) of
> My_Rec(N)" is not (assuming N is not static). The complications make
> it arguable whether it is worth it. (You have similar problems with
> anonymous instances, of course.)

The semantics could be defined even in this case -- the subtypes are elaborated
at the point of the subprogram declaration, and then rely on conformance -- but
it would also be reasonable to require that there be no references to variables,
only constants, in the parameter subtype specifications.  I don't see it as
particularly difficult to specify the rules, but I agree that whether the
feature has sufficient benefit is still debatable.

>> The next question would be should we allow implicit instantiations as
>> part of a type declaration?  E.g.:
>>
>>     "type Vec_Rec is new Vectors.Vector(Positive, My_Rec);"
>>
>> Here the array type provides a clearer model, that yes, if we allow
>> it for a stand-alone object declaration, we should allow it in a type
>> declaration.
>>
>> This at least allows us to eliminate one level of explicit
>> instantiation.
>
> My initial thought was that this is fine, (with the obvious caveat
> that this is a tagged type, so there has to be a null extension on the
> type) but then I remembered that you can't usefully derive a type in a
> containers package.

This seems more of a failing of the structure of the containers packages, and
doesn't argue against the value of the feature more generally.  As you mention
below, and we have discovered for other reasons in the context of SPARK, having
cursor operations that don't take the associated container as a parameter create
numerous problems when it comes to doing anything "formal" with the containers.

>> So perhaps we can agree on some of this, and we can leave the debate
>> about the implicit instantiation as part of a formal parameter
>> specification for an ARG meeting (perhaps over a few beers...).
>> Along with perhaps the discussion of whether implicit instantations
>> should be permitted for "state-ful"
>> generic packages, and maybe explicit type conversion rules. ;-)
>
> That was what I was trying to do with the proposal I made in the AI
> that started this thread. :-) ...

Fair enough, but I believe we have made progress in this back-and-forth in terms
of understanding where are the challenges, and the differences in views.

>>> ... (I could see some use to an easier way to describe generic
>>> formal parameters of "any" vector type, for instance -- one can do
>>> that with formal packages, but it is clunky).
>>
>> I agree, that is clearly of value, and much more pleasant than the
>> complexities of formal packages.
>
> I don't have any ideas for that, though, so someone else will need to
> come up with one.

All I know is that it will probably use "<>" in various places... ;-)

****************************************************************

From: Jeff Cousins
Sent: Thursday, January 12, 2017  8:08 AM

I'm afraid Tucker doesn't seem to be convincing people that we need anything
more than what is in Randy's proposal. Would a fuller example help?  Maybe
something based on the nested containers examples in the indefinite containers
sections of Programming in Ada 2012?

****************************************************************

From: Steve Baird
Sent: Thursday, January 12, 2017  2:25 PM

> Along with perhaps the discussion of whether implicit instantations
> should be permitted for "state-ful" generic packages

[Another message...]
> If you have the following code in library 1, from vendor A:
>
> procedure Frobulize (Vec : Vectors.Vector (Natural, Integer));
>
> And the following code in library 2, from vendor B:
>
> function Create_Sequence return Vectors.Vector (Natural, Integer);
>
> You want to be able to use those two subprograms together seamlessly, without
> using conversion functions.

I get wary when we start talking about combining implicit instantiations of
"state-ful" generic packages with more complicated models aimed at cutting down
on the number of implicitly-declared instances by coalescing multiple such
instances into one.

In the case of a "state-ful" package, I would strongly disagree with Raphaël's
statement (perhaps Raphaël shares my opinion and had only state-less instances
in mind). If two instances which happen to have the same actual parameters are
not really interchangeable, then I want it to be very clear which one I am
talking about at any given point.

Incidentally, my notion of "state-ful" is fairly conservative.

Consider, for example

     generic
        type T is private;
     package G1 is
        X : constant Integer := Some_Package.Some_Global_Variable;
        type Rec is
          record
             F1 : Integer := X;
             F2 : T;
          end record;
     end G;

If I see

    Var_1 : G1 (Float).Rec;

in one place then I certainly don't want the presence or absence of

    Var_2 : G1 (Float).Rec;

in another place to influence the initial value of Var_1.F1 by somehow affecting
the point at which the relevant instance of G1 is elaborated.

****************************************************************

From: Tucker Taft
Sent: Thursday, January 12, 2017  3:08 PM

>> Along with perhaps the discussion of whether implicit instantations
>> should be permitted for "state-ful" generic packages

If it wasn't clear, I am not a fan of allowing implicit instantiation of
state-ful generic packages.  And I would agree that state-less implies you don't
want something that captures the value of a global variable upon instantiation
-- you really want the instantiations to produce the same result given the same
actual parameters.

****************************************************************

From: Randy Brukardt
Sent: Thursday, January 12, 2017  3:46 PM

> > ... The only time I've ever used anonymous access parameters was in
> > the Claw Builder, and that was as a stand-in for the
> non-existent "in
> > out" parameters for functions. Luckily we've fixed *that* lack.
>
> If you are generally passing around access types, and you want to get
> some dispatching, then access parameters are quite appropriate, in my
> experience.

I've said this before, so I'll be brief (for me!): Using access parameters in
this way save 4 characters (".all"), which hardly is enough for the complexity
of the feature in the first place. And ADTs that "pass around access types" are
unnecessarily limiting, as they force a memory management style on the user,
preventing them from using containers, local objects, or even smart pointers for
memory management. That might be OK for one-time use quick & dirty code, but not
anything that might get reused (and a lot of code has reuse potential). I don't
think the language should be designed to specifically help quick & dirty code.

...
> I agree that using them
> for function out-parameters is not important any more, and was
> somewhat of a kludge before.
> >
> >> It is true that Ada never allowed an anonymous array type as a
> >> parameter type, while allowing it for stand-alone objects.
> >> I would think there are still arguments on both sides of this
> >> decision (I think we know where you stand), and I guess I am
> >> convinced that we should be consistent -- if we think it is
> >> important to allow "Vector(Positive, My_Rec)"
> >> as a formal parameter subtype, then we should similarly allow
> >> "array(Positive range <>) of My_Rec".  I think it would be worth
> >> debating this issue in an ARG meeting, because e-mail does not
> >> provide a good decision process.
> >
> > I certainly agree that we should be consistent. But that's tough
> > because a parameter profile can't allow anything that requires
> > evaluation/runtime elaboration. So, for instance, "array(Positive
> > range <>) of My_Rec" is OK, and "array(Positive range <>) of
> > My_Rec(N)" is not (assuming N is not static). The complications make
> > it arguable whether it is worth it. (You have similar problems with
> > anonymous instances, of course.)
>
> The semantics could be defined even in this case -- the subtypes are
> elaborated at the point of the subprogram declaration, and then rely
> on conformance -- but it would also be reasonable to require that
> there be no references to variables, only constants, in the parameter
> subtype specifications.  I don't see it as particularly difficult to
> specify the rules, but I agree that whether the feature has sufficient
> benefit is still debatable.

Yes, and whether the language design inconsistency (that dynamic constraints are
allowed everywhere but in a profile) is more trouble than it is worth. It's easy
to explain the restriction to identifiers without having to get into the
semantic problems in detail - it seems natural in a "name everything" language
design. A restriction to static constraints pretty much would require some sort
of semantic explanation (and we'd still get pressure from the underinformed to
eliminate the restriction).

...
> >> The next question would be should we allow implicit instantiations
> >> as part of a type declaration?  E.g.:
> >>
> >>     "type Vec_Rec is new Vectors.Vector(Positive, My_Rec);"
> >>
> >> Here the array type provides a clearer model, that yes, if we allow
> >> it for a stand-alone object declaration, we should allow it in a
> >> type declaration.
> >>
> >> This at least allows us to eliminate one level of explicit
> >> instantiation.
> >
> > My initial thought was that this is fine, (with the obvious caveat
> > that this is a tagged type, so there has to be a null extension on
> > the
> > type) but then I remembered that you can't usefully derive
> a type in a containers package.
>
> This seems more of a failing of the structure of the containers
> packages, and doesn't argue against the value of the feature more
> generally.  As you mention below, and we have discovered for other
> reasons in the context of SPARK, having cursor operations that don't
> take the associated container as a parameter create numerous problems
> when it comes to doing anything "formal" with the containers.

I take it from this that you agree with my suggestion to add the missing
container reading operations so that derivation and formal analysis work
appropriately? (I made that a separate thread, that no one seems to have
commented on so far.) We can't get rid of the existing operations for obvious
reasons, but we could add ones that work "right" and formal uses could just
ignore the ones without a container parameter. (That seems like a more practical
solution than defining a completely separate set of containers that have better
behavior.) [Comment on my other message if possible, that helps keep the threads
together.]

> >> So perhaps we can agree on some of this, and we can leave the
> >> debate about the implicit instantiation as part of a formal
> >> parameter specification for an ARG meeting (perhaps over a few beers...).
> >> Along with perhaps the discussion of whether implicit instantations
> >> should be permitted for "state-ful"
> >> generic packages, and maybe explicit type conversion rules. ;-)
> >
> > That was what I was trying to do with the proposal I made in the AI
> > that started this thread. :-) ...
>
> Fair enough, but I believe we have made progress in this
> back-and-forth in terms of understanding where are the challenges, and
> the differences in views.

Yes, we have.

> >>> ... (I could see some use to an easier way to describe generic
> >>> formal parameters of "any" vector type, for instance -- one can do
> >>> that with formal packages, but it is clunky).
> >>
> >> I agree, that is clearly of value, and much more pleasant than the
> >> complexities of formal packages.
> >
> > I don't have any ideas for that, though, so someone else will need
> > to come up with one.
>
> All I know is that it will probably use "<>" in various places... ;-)

Yup. Someone needs to construct an example of how it would be done today so that
we can see where the verbosity and challenges lie. I'd do it myself, but it
wouldn't do to use up my entire 2017 budget in the first two weeks of January...

****************************************************************

From: Randy Brukardt
Sent: Thursday, January 12, 2017  4:21 PM

> >> Along with perhaps the discussion of whether implicit instantations
> >> should be permitted for "state-ful" generic packages
>
> If it wasn't clear, I am not a fan of allowing implicit instantiation
> of state-ful generic packages.

That only matters, though, if you are doing some sort of instance combining. If
it is one instance to one implicit usage (in parallel with anonymous arrays),
then it does not matter.

> And I would
> agree that state-less implies you don't want something that captures
> the value of a global variable upon instantiation -- you really want
> the instantiations to produce the same result given the same actual
> parameters.

I worry that a state-less restriction would be about as useful as Pure packages:
they sound like a good idea, but are very difficult to use in practice. There
always seems to be some reason that something not allowed in a Pure package is
needed (often for debugging, frequently for other things). That same would seem
likely here; as I noted, all of the non-pure Ada.Containers packages have state
in Janus/Ada. And debugging often involves local state.

****************************************************************

From: Tucker Taft
Sent: Thursday, January 12, 2017  4:31 PM

> ... I've said this before, so I'll be brief (for me!): Using access
> parameters in this way save 4 characters (".all"), which hardly is
> enough for the complexity of the feature in the first place. And ADTs
> that "pass around access types" are unnecessarily limiting, as they
> force a memory management style on the user, preventing them from
> using containers, local objects, or even smart pointers for memory
> management. That might be OK for one-time use quick & dirty code, but
> not anything that might get reused (and a lot of code has reuse
> potential). I don't think the language should be designed to specifically help
> quick & dirty code.

You are presuming quite a lot here about how people structure O-O systems, and
clearly we are not about to get rid of access parameters.  So I would suggest we
don't spend too much energy arguing about whose approach is appropriate for any
given O-O system, since it seems pretty much beside the point for this
discussion.

> ... I take it from this that you agree with my suggestion to add the
> missing container reading operations so that derivation and formal
> analysis work appropriately? (I made that a separate thread, that no
> one seems to have commented on so far.)

Yes, I agree with that proposal.

> ... We can't get rid of the existing operations for obvious reasons,
> but we could add ones that work "right" and formal uses could just
> ignore the ones without a container parameter. (That seems like a more
> practical solution than defining a completely separate set of
> containers that have better behavior.) [Comment on my other message if
> possible, that helps keep the threads together.]

When I have a bit more time, I will do so.

****************************************************************

From: Tucker Taft
Sent: Thursday, January 12, 2017  4:39 PM

>> If it wasn't clear, I am not a fan of allowing implicit instantiation
>> of state-ful generic packages.
>
> That only matters, though, if you are doing some sort of instance combining.
> If it is one instance to one implicit usage (in parallel with
> anonymous arrays), then it does not matter.

I guess I don't agree.  If one writes:

    X, Y : Vectors.Vector(Natural, My_Rec);

it is weird to have to worry that the order of X & Y might matter.

>> And I would
>> agree that state-less implies you don't want something that captures
>> the value of a global variable upon instantiation -- you really want
>> the instantiations to produce the same result given the same actual
>> parameters.
>
> I worry that a state-less restriction would be about as useful as Pure
> packages: they sound like a good idea, but are very difficult to use
> in practice. There always seems to be some reason that something not
> allowed in a Pure package is needed (often for debugging, frequently for
> other things).
> That same would seem likely here; as I noted, all of the non-pure
> Ada.Containers packages have state in Janus/Ada. And debugging often
> involves local state.

A reasonable consideration, so we would have to see whether we can define
"state-less" in a way that seems to work for almost all interesting container
generics.  I'd be curious what "state" your Ada.Containers package have, and how
that can work if you use the same instance to define objects manipulated in
different tasks.  For what it is worth, I would presume that declaring an access
type would be allowed in a "state-less" generic, despite the weirdness with Pure
and access types (probably a major part of the problem with "Pure").

As far as debugging, the old export/import trick seems to be a relatively common
way to allow debugging I/O from a Pure package, but of course I would never do
such a thing myself.

****************************************************************

From: Randy Brukardt
Sent: Thursday, January 12, 2017  4:48 PM

...
> > ... I've said this before, so I'll be brief (for me!): Using access
> > parameters in this way save 4 characters (".all"), which hardly is
> > enough for the complexity of the feature in the first place. And
> > ADTs that "pass around access types" are unnecessarily limiting, as
> > they force a memory management style on the user, preventing them
> > from using containers, local objects, or even smart pointers for
> > memory management. That might be OK for one-time use quick & dirty
> > code, but not anything that might get reused (and a lot of code has
> > reuse potential). I don't think the language should be designed
> to specifically help quick & dirty code.
>
> You are presuming quite a lot here about how people structure O-O
> systems,

They structure them badly!! :-) A lot of designs are similar to C++ or Java
designs, and Ada has additional capabilities that allow it to be done better.
I'm not much interested in copying the mistakes of other languages...

> ... and clearly we are not about to get rid of access parameters.  So
> I would suggest we don't spend too much energy arguing about whose
> approach is appropriate for any given O-O system, since it seems
> pretty much beside the point for this discussion.

...but I definitely agree with you here. (I minimized my justification of the
above for exactly that reason.) It's only relevant to the point that I don't
want to put any *additional* effort into features that I see as bad for Ada
usage. (I'd be surprised if anyone here feels differently, but of course the
features in question will differ.)

...

****************************************************************

From: Jean-Pierre Rosen
Sent: Thursday, January 12, 2017  11:25 PM

>>> Along with perhaps the discussion of whether implicit instantations
>>> should be permitted for "state-ful" generic packages
>
> If it wasn't clear, I am not a fan of allowing implicit instantiation
> of state-ful generic packages.  And I would agree that state-less
> implies you don't want something that captures the value of a global
> variable upon instantiation -- you really want the instantiations to
> produce the same result given the same actual parameters.

But wouldn't it mean that sharing (or not) would depend on the generic body?
We certainly don't want THAT!

****************************************************************

From: Randy Brukardt
Sent: Friday, January 13, 2017  12:12 AM

Tucker had suggested an aspect to mark such packages, so it wouldn't depend on
the body.

But we already have such an aspect (Pure), so what another similar aspect would
buy us isn't clear to me. (Only the bounded containers are Pure, and that's for
good reasons that would also apply to Tuck's "stateless" aspect, so his
anonymous instances couldn't be used for most containers.)

****************************************************************

From: Raphaël Amiard
Sent: Friday, January 13, 2017  3:22 AM

> In the case of a "state-ful" package, I would strongly disagree with
> Raphaël's statement (perhaps Raphaël shares my opinion and had only
> state-less instances in mind).

Yes, and yes.

****************************************************************

From: Bob Duff
Sent: Sunday, January 15, 2017  5:49 PM

> They structure them badly!! :-)

Off topic!! ;-)

****************************************************************

From: Bob Duff
Sent: Sunday, January 15, 2017  5:49 PM

Finally got around to reading this monster thread.

> I've said this before, so I'll be brief (for me!): Using access
> parameters in this way save 4 characters (".all"), which hardly is
> enough for the complexity of the feature in the first place. And ADTs
> that "pass around access types" are unnecessarily limiting, as they
> force a memory management style on the user, preventing them from
> using containers, local objects, or even smart pointers for memory
> management. That might be OK for one-time use quick & dirty code, but
> not anything that might get reused (and a lot of code has reuse
> potential). I don't think the language should be designed to specifically
> help quick & dirty code.

Unless everyone on ARG agrees with that point of view, then we shouldn't be
designing the Ada programming language based on that point of view.
You shouldn't be trying convince Tucker that he shouldn't be "passing around
access values", and Tucker shouldn't be trying to convince you of the
opposite. You should accept each other's different styles as valid, and design
the language accordingly.

****************************************************************

From: Bob Duff
Sent: Sunday, January 15, 2017  5:50 PM

> I guess I don't agree.  If one writes:
>
>     X, Y : Vectors.Vector(Natural, My_Rec);
>
> it is weird to have to worry that the order of X & Y might matter.

Yes.  Arrays are broken for the same reason:

    X, Y: array (1..Read(From => Keyboard)) of ...;

> A reasonable consideration, so we would have to see whether we can
> define "state-less" in a way that seems to work for almost all
> interesting container generics.  I'd be curious what "state" your
> Ada.Containers package have, and how that can work if you use the same
> instance to define objects manipulated in different tasks.  For what
> it is worth, I would presume that declaring an access type would be allowed in a "state-less" generic, despite the weirdness with Pure and access types (probably a major part of the problem with "Pure").

Yes, I think it was a mistake to consider heap allocation to be a "side effect".
After all, pure functional languages are lot purer than Ada, and they do heap
allocation at the drop of a hat.

Heap allocation is a side effect, yeah, but it shouldn't matter at the semantic
level we care about, any more than the fact that running code has the side
effect of increasing the temperature of the CPU chip.  (We would need to allow
access types that don't have "=", though.)

> As far as debugging, the old export/import trick seems to be a
> relatively common way to allow debugging I/O from a Pure package, but of course I would never do such a thing myself.

In my own language designing, I've invented ways of saying, "Never mind that
this is Pure, I want to have a hidden side effect anyway, but I promise it's not
a side effect that matters."  (Defining "matters" is tricky. And if it DOES
matter, you get erroneousness.)

****************************************************************

From: Bob Duff
Sent: Sunday, January 15, 2017  5:52 PM

> All I know is that it will probably use "<>" in various places... ;-)

I once had a consulting gig where part of my job was to tutor a FORTRAN
programming in Ada.  He asked, "What does '<>' mean in Ada?" That question
wasn't easy to answer way back then, and it hasn't gotten easier.  ;-)

****************************************************************

From: Bob Duff
Sent: Sunday, January 15, 2017  5:56 PM

> I certainly agree that we should be consistent. But that's tough
> because a parameter profile can't allow anything that requires
> evaluation/runtime elaboration.

Why?  I have never understood why Ada doesn't allow:

    procedure P (X: Integer range 1 .. N);

I can think of three possible rules:

    - N must be static.

    - N need not be static, and:

        - N is evaluated as part of the declaration of P.

        - N is evaluated at each call site.

As far as I can see, none of these introduce any semantic anomalies.
The last one would allow some interesting things, if we also changed the
visibility rules:

    procedure P(S: String; X: Integer range S'Range := S'First);

****************************************************************

From: Jean-Pierre Rosen
Sent: Monday, January 16, 2017  12:22 AM

>> All I know is that it will probably use "<>" in various places... ;-)
>
> I once had a consulting gig where part of my job was to tutor a
> FORTRAN programming in Ada.  He asked, "What does '<>' mean in Ada?"
> That question wasn't easy to answer way back then, and it hasn't
> gotten easier.  ;-)

??
In my courses, I explain: "it's a placeholder, meaning: something here will have
to be filled later, like a box in a form showing wher to put your name".

****************************************************************

From: Randy Brukardt
Sent: Monday, January 16, 2017  8:06 PM

> > I certainly agree that we should be consistent. But that's tough
> > because a parameter profile can't allow anything that requires
> > evaluation/runtime elaboration.
>
> Why?  I have never understood why Ada doesn't allow:
>
>     procedure P (X: Integer range 1 .. N);
>
> I can think of three possible rules:
>
>     - N must be static.
>
>     - N need not be static, and:
>
>         - N is evaluated as part of the declaration of P.
>
>         - N is evaluated at each call site.
>
> As far as I can see, none of these introduce any semantic anomalies.

They're all inconsistent with the rest of the language. For the first rule, we
don't restrict the use of dynamic constraints anywhere else. The second rule
implies that bodies and declarations are treated differently for the evaluation
of subtype_indications (or there has to be some form of dynamic conformance,
which would be madness). That would be a mild maintanance hazard at best. The
third rule is very inconsistent with the rest of the language; subtypes are
always elaborated in place.

Any of these might have made sense in a new language, but none of them seem very
consistent with Ada.

I recall we talked about this (informally?? - I didn't try to look this up) in
the context of Ada 2005 and decided that a change wasn't likely to be in the
best interest of Ada. That seems unlikely to have changed (although enough time
and membership change has happened that we could discuss it again if there is
enough interest).

****************************************************************

From: Randy Brukardt
Sent: Monday, January 16, 2017  8:26 PM

....
> > For what it is worth, I would presume that declaring an access type
> > would be allowed in a "state-less" generic, despite the weirdness
> > with Pure and access types (probably a major part of the problem
> > with "Pure").
>
> Yes, I think it was a mistake to consider heap allocation to be a
> "side effect".  After all, pure functional languages are lot purer
> than Ada, and they do heap allocation at the drop of a hat.
>
> Heap allocation is a side effect, yeah, but it shouldn't matter at the
> semantic level we care about, any more than the fact that running code
> has the side effect of increasing the temperature of the CPU chip.
> (We would need to allow access types that don't have "=",
> though.)

Heap allocation per-se isn't the problem, the problem is that the allocated
memory is (or maybe "can be") global and used by other tasks. For instance, Claw
has many allocated objects that are used only locally, but since there is always
another task (the human operator of the Claw program) that can change the state,
one can never trust that (and as a consequence, pre-testing Claw objects is
pointless). That has to be taken account in terms of things like Pure.

"No allocation" is too strong a club, but just allowing any allocation
necessarily allows cases of global state as well. Not sure how to reconcile
that.

> > As far as debugging, the old export/import trick seems to be a
> > relatively common way to allow debugging I/O from a Pure
> package, but of course I would never do such a thing myself.
>
> In my own language designing, I've invented ways of saying, "Never
> mind that this is Pure, I want to have a hidden side effect anyway,
> but I promise it's not a side effect that matters."  (Defining
> "matters" is tricky.
> And if it DOES matter, you get erroneousness.)

The problem with that is that it depends on the use of Pure as to whether any
side-effects can be allowed. For optimization purposes, such a declaration (of
"trust me") is clearly fine. OTOH, if one is trying to determine what is safe to
use in a parallel loop, any side-effect is going to be trouble (because of the
unsynchronized use of the side-effect).

The existing Ada Pure is at the wrong level for pretty much any use; hopefully
the proposed Global aspect will do a better job (by being finer-grained - both
as to what it applies and to what is used).

****************************************************************

From: Randy Brukardt
Sent: Monday, January 16, 2017  8:39 PM

...
> Unless everyone on ARG agrees with that point of view, then we
> shouldn't be designing the Ada programming language based on that
> point of view.
> You shouldn't be trying convince Tucker that he shouldn't be "passing
> around access values", and Tucker shouldn't be trying to convince you
> of the opposite.  You should accept each other's different styles as
> valid, and design the language accordingly.

Everybody has "hot button" issues, and this is one of mine. One can respect the
existence of other opinions while still being completely convinced that they're
wrong. I want to spend as little time as possible on improving the language in
ways that cannot possibly help the use of Ada, and certainly not to help the
spread of dubious programming techniques.

I would not expect any of our group to feel differently. Of course, compromise
is possible (my entire intent with the proposal that started this thread was to
try to find some sort of compromise; I don't see any real problem here but it
seems clear that others do and I wanted to be part of the solution rather than
just saying "no" repeatedly. There were 4 features of Tucker's original proposal
that I consider unacceptable for Ada, so we were a LOOONNNGGG ways from
compromise there -- I could have said "no chance in hell" and left it at that.
In hindsight I probably should have done that...).

****************************************************************

From: Randy Brukardt
Sent: Monday, January 16, 2017  9:00 PM

...
> >> If it wasn't clear, I am not a fan of allowing implicit
> >> instantiation of state-ful generic packages.
> >
> > That only matters, though, if you are doing some sort of instance combining.
> > If it is one instance to one implicit usage (in parallel with
> > anonymous arrays), then it does not matter.
>
> I guess I don't agree.  If one writes:
>
>     X, Y : Vectors.Vector(Natural, My_Rec);
>
> it is weird to have to worry that the order of X & Y might matter.

Why would it matter? If there are two separate instances, each with their own
state, then the objects are fully independent. The order shouldn't matter. I
suppose it could matter if you had some sort of mad generic which depends on a
global variable outside of the instance and for which that dependence matters in
some way that is visible in the dynamic semantics. But such a generic would be
problematic in any use; you'd have care about the order of instantiations in any
usage and that would be highly unusual (and difficult to use). I can't get very
concerned about such cases (if it hurts, don't do that). Bob noted that the
order matters for an anonymous array like:

     X, Y : array (1 .. Some_Function) of Integer;

where Some_Function is a function that returns different values on each call.
But again such a declaration is so unusual it hardly pays to worry about it in
terms of language rules. (One could imagine requiring any anonymous subtypes to
be static, but Ada didn't take that path.)

> >> And I would
> >> agree that state-less implies you don't want something that
> >> captures the value of a global variable upon instantiation -- you
> >> really want the instantiations to produce the same result given the
> >> same actual parameters.
> >
> > I worry that a state-less restriction would be about as useful as
> > Pure
> > packages: they sound like a good idea, but are very difficult to use
> > in practice. There always seems to be some reason that something not
> > allowed in a Pure package is needed (often for debugging, frequently
> > for other things). That same would seem likely here; as I noted, all
> > of the non-pure Ada.Containers packages have state in Janus/Ada. And
> > debugging often involves local state.
>
> A reasonable consideration, so we would have to see whether we can
> define "state-less" in a way that seems to work for almost all
> interesting container generics.  I'd be curious what "state" your
> Ada.Containers package have, and how that can work if you use the same
> instance to define objects manipulated in different tasks.

The state in the containers (different in each container, of course):
(1) Storage pool-related stuff;
(2) A serial-number generator for dangling cursor detection;
(3) For the containers with individual nodes, pointers at a small number of
    recently freed nodes;
(4) Some performance measurement data (only when debugging).

For Janus/Ada, which uses "run until task dispatching point" semantics, it's not
a worry about surprise interruptions, so the tasking protection doesn't need
much. (2) and (3) use atomic components to avoid tasking trouble; (1) uses the
standard storage pool (which hopefully avoids tasking trouble); and (4)
generally is only used in limited circumstances so I've never worried about what
happens when tasking is involved.

...
> For what it is worth, I
> would presume that declaring an access type would be allowed in a
> "state-less" generic, despite the weirdness with Pure and access types
> (probably a major part of the problem with "Pure").

I mentioned in my reply to Bob that there certainly is some way to make that
work, but one has to be careful about multiple tasks banging on the same heap
allocated objects. Claw does that a lot and one has to be careful to not assume
any Claw operations are Pure even though the implementation appears that way.
Not sure what the solution is for that (it would be good to find one, for Global
at least).

> As far as debugging, the old export/import trick seems to be a
> relatively common way to allow debugging I/O from a Pure package, but
> of course I would never do such a thing myself.

Me either, and I would like to have a way to debug things without having to
resort to that (or to passing a Logger routine as a parameter to every routine,
which is what I usually do).

****************************************************************

From: Tucker Taft
Sent: Saturday, January 21, 2017  3:45 PM

>> I guess I don't agree.  If one writes:
>>
>>     X, Y : Vectors.Vector(Natural, My_Rec);
>>
>> it is weird to have to worry that the order of X & Y might matter.
>
> Why would it matter? If there are two separate instances, each with
> their own state, then the objects are fully independent.

This was in the context of Steve's comment that related to his definition of
"stateless" where he wanted to disallow references to global variables in the
initialization of the package-level constants.  Even worse would be a case where
a global variable is update during the elaboration of an instantiation.  Here is
a quote from that e-mail:

> ... Consider, for example
>
>     generic
>        type T is private;
>     package G1 is
>        X : constant Integer := Some_Package.Some_Global_Variable;
>        type Rec is
>          record
>             F1 : Integer := X;
>             F2 : T;
>          end record;
>     end G;  ...


> ... The order shouldn't
> matter. I suppose it could matter if you had some sort of mad generic
> which depends on a global variable outside of the instance and for
> which that dependence matters in some way that is visible in the dynamic semantics. ...

That is exactly what Steve was worrying about.  Steve is of course the master of
"mad" generics (and other "mad" examples), which is helpful when you are trying
to evaluate whether a given proposed rule actually provides some measure of
safety.

****************************************************************

From: Randy Brukardt
Sent: Saturday, January 21, 2017  8:50 PM

But why is such a "mad" generic a worry? There's no semantic problem with it,
and it's not the sort of thing that a programmer is likely to do. After all, Ada
always has had cases like that:

    X, Y : Disc_Rec (Func);

If Func returns a different value for each call, then X and Y have different
constraints. (3.3.1(7)). If that's a problem, don't do it!

Moreover, this kind of state could be useful in some cases. If one wanted each
instance to have a unique serial number, for instance, one would use a global
object and modify it during the elaboration of the instance. But the order
wouldn't matter, just that the serial numbers are unique.

So I don't see why this sort of thing is a concern. If it happens, one presumes
that the programmer knows what they're doing. After all, there will never be a
programming language in which it is the least bit hard to write ill-advised
programs! (I remember someone saying that many years ago, and I doubt that has
changed since.)

I really don't want to define some complex mechanism that's rather like another
mechanism (that didn't work out very well) just to avoid such an unlikely issue.
It seems like massive overkill.

****************************************************************

From: Bob Duff
Sent: Saturday, January 21, 2017  10:01 AM

> Not having to invent names is not a very good justification...

Every name adds a cognitive burden to the reader of the program. It's easy to
invent names, but having to read names and match them up with their declaration
is a burden.

That's why aspects are better than pragmas, for example. With the pragma you
have to match the name in the pragma with the declaration it refers to.  Aspects
are syntactically part of the declaration, so don't have that problem.

>...But there
> is (IMHO) at least one major value to anonymous types: you know it is
>a  singleton, that it's the only object of its kind, and this can be a
>useful information (especially for single tasks) that you want enforced
>by the compiler...

Agreed.

> ... and that would be immediately broken with structural equivalence!

Not if structural equivalence is triggered by some mechanism other than
anonymity.

****************************************************************

From: Randy Brukardt
Sent: Saturday, January 21, 2017  8:41 PM

+1

If structural equivalence is really useful, it shouldn't be mixed up with
anonymity: I don't want any more incentives to use anonymity rather than named
declarations. Ideally, the choice to omit named declarations is driven solely by
the programmer's determination that an additional name doesn't help readability.
Not by a desire to get dynamic accessibility or closures (both features that are
bizarrely tied to anonymity today) or structural equivalence.

****************************************************************

From: Bob Duff
Sent: Saturday, January 21, 2017  10:04 AM

> we could say that the default index type for generic vectors is 
> Positive for example.

Unless you're making parallel arrays, different arrays/vectors should usually
have different index types.

> I think we should use structural equivalence rules, because Ada is 
> generally missing any form of higher level structural typing, and that 
> causes a lot of problems. Also people wanting generics with nominal 
> typing already have the (very logical in that instance) choice of 
> creating instances. Here is a mail I sent to Steve in a discussion about
> this topic:

Structural typing for components, but not index?

****************************************************************


Questions? Ask the ACAA Technical Agent