Version 1.7 of ais/ai-00419.txt

Unformatted version of ais/ai-00419.txt version 1.7
Other versions for file ais/ai-00419.txt

!standard 3.4(01)          05-08-08 AI95-00419-01/06
!standard 3.4(02)
!standard 3.4(03)
!standard 3.4(05)
!standard 3.4(15)
!standard 3.7(10)
!standard 3.9.1(03)
!standard 3.9.4(01)
!standard 7.3(02)
!standard 7.3(06)
!standard 7.3(08)
!standard 7.3(10)
!standard 7.5(02)
!standard 7.5(04)
!standard 7.5(06)
!standard 9.1(9.1)
!standard 9.4(11)
!standard 12.5.1(03)
!standard 12.5.1(05)
!class amendment 05-03-09
!status Amendment 200Y 05-03-10
!status ARG Approved 11-0-0 05-04-16
!status work item 05-03-09
!status received 05-03-09
!priority High
!difficulty Medium
!subject Limitedness of derived types
!summary
(See proposal.)
!problem
Draft 10 of the Reference Manual defines the limitedness of a derived type based on the limitedness of its parent. This has the unpleasant consequence that, for a type whose parent is an interface, the parent and the progenitors do not play symmetrical roles, as shown by the following example:
type LI is limited interface; type NLI is interface;
type T1 is new LI and NLI with ...; -- Limited, illegal. type T2 is new NLI and LI with ...; -- Nonlimited, OK.
(The declaration of T1 is actually illegal because NLI is nonlimited, and it can only be implemented by a nonlimited type.)
Furthermore, declaring a nonlimited type that is derived from limited interfaces requires the introduction of a dummy interface to get the limitedness right:
type LI1 is limited interface; type LI2 is limited interface;
type T1 is new LI1 and LI2 with ...; -- Limited, but this -- is not what we want.
type Nonlimited is interface;
type T2 is Nonlimited and LI1 and LI2 with ...; -- Nonlimited, after -- going through hoops.
This is likely to confuse users and to create unnecessary complication in real code.
!proposal
Do not inherit limitedness from a parent interface. A derived type whose parent is an interface is nonlimited by default. New syntax is provided to make it possible to declare that such a type is limited, thus:
type T1 is new LI1 and LI2 with ...; -- Nonlimited. type T2 is limited new LI1 and LI2 with ...; -- Limited.
There is one additional difficulty due to the fact that an interface type can be passed as an actual for a formal abstract type. For instance:
generic type A is abstract limited tagged private; package G is type T is new A with ...; end G;
In the generic, type T is surely limited. However, in an instance, the actual type for A could be an interface. Seen from outside the instance, type T would then be nonlimited. There must be a way for the author of the generic to ensure that T is limited. This means that we must allow the following:
generic type A is abstract limited tagged private; package G is type T is limited new A with ...; end G;
Given that we have to allow this syntax in this particular situation, we might as well allow it uniformly for every derived type: it could be used as a documentation. Although this is optional syntax, and we don't like optional syntax too much, one could argue that it should have been that way from day one as the implicit nature of limitedness is hiding an important property.
We also note that derived types are undefined in the Standard; there is no wording saying that a derived type comes from a derived_type_declaration. Similarly, various types that aren't derived types need to have wording to trigger the inheritance rules.
!wording
Change 3.4(1) as amended by AI95-00401 to read:
A derived_type_definition defines a {derived} type{} (and its first subtype) whose characteristics are derived from those of a parent type, and possibly from progenitor types.
Change 3.4(2) as amended by AI95-00251 to read (note: interface_list is moved):
derived_type_definition ::= [abstract] [limited] new parent_subtype_indication [[and interface_list] record_extension_part]
Change 3.4(3) as amended by AI95-00251 and AI95-00401 to read:
The parent_subtype_indication defines the parent subtype; its type is the parent type. The interface_list defines the progenitor types (see 3.9.4). A derived type has one parent type and zero or more {progenitor} types.
Add after 3.4(5)
If the reserved word limited appears in a derived_type_definition, the parent type shall be a limited type.
AARM Reason: We allow limited because we don't inherit limitedness from interfaces, so we must have a way to derive a limited type from interfaces. The word limited has to be legal when the parent could be an interface, and that includes generic formal abstract types. Since we have to allow it in this case, we might as well allow it everywhere as documentation, to make it explicit that the type is limited.
However, we do not want to allow limited when the parent is nonlimited: limitedness cannot change in a derivation tree. End AARM.
Delete 3.4(15) (this is defined in 7.5).
Add the following to the syntax in the new subclause 3.9.4:
interface_list ::= interface_subtype_mark {and interface_subtype_mark}
Change 3.7(10) (using the terminology introduced by AI-318-2):
A discriminant_specification for an access discriminant shall appear only in the declaration for a task or protected type, or for a type {that is a descendamt of an explicitly limited record type} [with the reserved word limited in its [(full)] definition or in that of one of its ancestors].
Change 3.9.1(3):
... If the parent type{ or any progenitor} is nonlimited, ...
Add the following at the end of the Static Semantics of the new subclause 3.9.4:
An interface_subtype_mark in an interface_list names a progenitor subtype; its type is the progenitor type. An interface type inherits user-defined primitive subprograms from each progenitor type in the same way that a derived type inherits user-defined primitive subprograms from its progenitor types (see 3.4).
Change 7.3(3) as amended by AI95-0251 to read:
private_extension_declaration ::= type defining_identifier [discriminant_part] is [abstract] [limited] new ancestor_subtype_indication [and interface_list] with private;
Change the first sentence of 7.3(6) to read:
Redundant[A private type is limited if its declaration includes the reserved word limited; a private extension is limited if its ancestor type is a limited type that is not an interface type, or if the reserved word limited appears in its definition.]
Add after 7.3(8):
If the reserved word limited appears in a private_extension_declaration, the ancestor type shall be a limited type.
Add after 7.3(10):
If the full_type_declaration for a private extension is a derived_type_declaration, then the reserved word limited shall appear in the full_type_declaration if and only if it also appears in the private_extension_declaration.
AARM Reason: The word limited is optional (unless the ancestor is an interface), but if you use it, do so consistently. Otherwise things would be too confusing for the reader.
Change 7.5(2) to read:
If a tagged record type has any limited components, then the reserved word limited shall appear in its record_type_definition. Redundant[If the reserved word limited appears in the definition of a derived_type_definition, its parent type and any progenitor interfaces shall be limited.]
Change 7.5(3-6) to read: (This includes the change of AI-411):
A type is limited if it is one of the following:
* A type with the reserved words *limited*, *synchronized*, *task* or
protected in its definition;
* A composite type with a limited component; * A derived type whose parent is limited and is not an interface.
AARM Reason: We considered a rule where limitedness was always inherited from the parent for derived types, but in the case of a type whose parent is an interface, this meant that the first interface is treated differently than other interfaces. It also would have forced users to declare dummy nonlimited interfaces just to get the limitedness right. We also considered a syntax like not limited to specify nonlimitedness when the parent was limited, but that was unsavory. The rule is more uniform and simpler to understand.
The rules for interfaces are unsymmetrical, but the language is not: if the parent interface is limited, the presence of the word limited determines the limitedness, and nonlimited progenitors are illegal by the rules in 3.9.4. If the parent interface is nonlimited, the word limited is illegal by the rules in 3.4. The net effect is that the order of the interfaces doesn't matter. End AARM.
Modify the first paragraph added after 9.1(9.1) by AI-345 as follows:
For a task_type_declaration {with an interface_list, the task type inherits user-defined primitive subprograms from each progenitor type (see 3.9.4), in the same way that a derived type inherits user-defined primitive subprograms from its progenitor types (see 3.4).} [i]{I}f the first parameter of a primitive inherited subprogram is of the task type or an access parameter designating the task type, and there is an entry_declaration for a single entry with the same identifier within the task_type_declaration, whose profile is type conformant with the prefixed view profile of inherited subprogram, the inherited subprogram is said to be implemented by the conforming task entry.
Modify the first paragraph added after 9.1(11) by AI-345 as follows:
For a protected_type_declaration {with an interface_list, the protected type inherits user-defined primitive subprograms from each progenitor type (see 3.9.4), in the same way that a derived type inherits user-defined primitive subprograms from its progenitor types (see 3.4).} [i]{I}f the first parameter of a primitive inherited subprogram is of the protected type or an access parameter designating the protected type, and there is a protected_operation_declaration for a protected subprogram or single entry with the same identifier within the protected_type_declaration, whose profile is type conformant with the prefixed view profile of the inherited subprogram, the inherited subprogram is said to be implemented by the conforming protected subprogram or entry.
Change 12.5.1(3) as amended by AI95-00251 to read:
formal_derived_type_definition ::= [abstract] [limited] new subtype_mark [[and interface_list] with private]
Add at the end of 12.5.1(5):
Finally, the reserved word limited shall appear only if the ancestor type and any progenitor types are limited types.
!discussion
(See proposal.)
!example
(See proposal.)
!corrigendum 3.4(01)
!comment This includes the changes of AI-00401.
@drepl A @fa<derived_type_definition> defines a new type (and its first subtype) whose characteristics are @i<derived> from those of a @i<parent type>. @dby A @fa<derived_type_definition> defines a @i<derived type> (and its first subtype) whose characteristics are derived from those of a parent type, and possibly from progenitor types.
!corrigendum 3.4(02)
Replace the paragraph:
derived_type_definition ::= [abstract] new parent_subtype_indication [record_extension_part]
by:
interface_list ::= interface_subtype_mark {and interface_subtype_mark}
derived_type_definition ::= [abstract] [limited] new parent_subtype_indication [[and interface_list] record_extension_part]
!corrigendum 3.4(03)
Replace the paragraph:
The parent_subtype_indication defines the parent subtype; its type is the parent type.
by:
The parent_subtype_indication defines the parent subtype; its type is the parent type. The interface_list defines the progenitor types (see 3.9.4). A derived type has one parent type and zero or more progenitor types.
!corrigendum 3.4(05)
Insert after the paragraph:
If there is a record_extension_part, the derived type is called a record extension of the parent type. A record_extension_part shall be provided if and only if the parent type is a tagged type.
the new paragraph:
If the reserved word limited appears in a derived_type_definition, the parent type shall be a limited type.
!corrigendum 3.4(15)
Delete the paragraph:
!corrigendum 3.7(10)
Replace the paragraph:
A discriminant_specification for an access discriminant shall appear only in the declaration for a task or protected type, or for a type with the reserved word limited in its (full) definition or in that of one of its ancestors. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit.
by:
A discriminant_specification for an access discriminant shall appear only in the declaration for a task or protected type, or for a type that is a descendant of an explicitly limited record type. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit.
!corrigendum 3.9.1(03)
Replace the paragraph:
The parent type of a record extension shall not be a class-wide type. If the parent type is nonlimited, then each of the components of the record_extension_part shall be nonlimited. The accessibility level (see 3.10.2) of a record extension shall not be statically deeper than that of its parent type. In addition to the places where Legality Rules normally apply (see 12.3), these rules apply also in the private part of an instance of a generic unit.
by:
The parent type of a record extension shall not be a class-wide type. If the parent type or any progenitor is nonlimited, then each of the components of the record_extension_part shall be nonlimited. The accessibility level (see 3.10.2) of a record extension shall not be statically deeper than that of its parent type. In addition to the places where Legality Rules normally apply (see 12.3), these rules apply also in the private part of an instance of a generic unit.
!corrigendum 3.9.4(01)
!comment Dummy to cause a conflict.
@drepl @xcode<@fa<interface_list ::= >@ft<@i<interface_>>@fa<subtype_mark {>@ft<@b<and>>@fa< >@ft<@i<interface_>>@fa<subtype_mark}>> @dby @xcode<@fa<interface_list ::= >@ft<@i<interface_>>@fa<subtype_mark {>@ft<@b<and>>@fa< >@ft<@i<interface_>>@fa<subtype_mark}>>
!corrigendum 7.3(02)
Replace the paragraph:
private_extension_declaration ::= type defining_identifier [discriminant_part] is [abstract] new ancestor_subtype_indication with private;
by:
private_extension_declaration ::= type defining_identifier [discriminant_part] is [abstract] [limited] new ancestor_subtype_indication [and interface_list] with private;
!corrigendum 7.3(06)
Replace the paragraph:
A private type is limited if its declaration includes the reserved word limited; a private extension is limited if its ancestor type is limited. If the partial view is nonlimited, then the full view shall be nonlimited. If a tagged partial view is limited, then the full view shall be limited. On the other hand, if an untagged partial view is limited, the full view may be limited or nonlimited.
by:
A private type is limited if its declaration includes the reserved word limited; a private extension is limited if its ancestor type is a limited type that is not an interface type, or if the reserved word limited appears in its definition. If the partial view is nonlimited, then the full view shall be nonlimited. If a tagged partial view is limited, then the full view shall be limited. On the other hand, if an untagged partial view is limited, the full view may be limited or nonlimited.
!corrigendum 7.3(08)
Insert after the paragraph:
The ancestor subtype of a private_extension_declaration is the subtype defined by the ancestor_subtype_indication; the ancestor type shall be a specific tagged type. The full view of a private extension shall be derived (directly or indirectly) from the ancestor type. In addition to the places where Legality Rules normally apply (see 12.3), the requirement that the ancestor be specific applies also in the private part of an instance of a generic unit.
the new paragraph:
If the reserved word limited appears in a private_extension_declaration, the ancestor type shall be a limited type.
!corrigendum 7.3(10)
Insert after the paragraph:
If a private extension inherits known discriminants from the ancestor subtype, then the full view shall also inherit its discriminants from the ancestor subtype, and the parent subtype of the full view shall be constrained if and only if the ancestor subtype is constrained.
the new paragraph:
If the full_type_declaration for a private extension is a derived_type_declaration, then the reserved word limited shall appear in the full_type_declaration if and only if it also appears in the private_extension_declaration.
!corrigendum 7.5(02)
Replace the paragraph:
If a tagged record type has any limited components, then the reserved word limited shall appear in its record_type_definition.
by:
If a tagged record type has any limited components, then the reserved word limited shall appear in its record_type_definition. If the reserved word limited appears in the definition of a derived_type_definition, its parent type and any progenitor interfaces shall be limited.
!corrigendum 7.5(03)
Replace the paragraph:
A type is limited if it is a descendant of one of the following:
by:
A type is limited if it is one of the following:
!corrigendum 7.5(04)
!Comment note that the rewrite of this list eliminates the AI-411 change.
@drepl @xbullet<a type with the reserved word @b<limited> in its definition;> @dby @xbullet<a type with the reserved word @b<limited>, @b<synchronized>, @b<task>, or @b<protected> in its definition;>
!corrigendum 7.5(05)
Delete the paragraph:
!corrigendum 7.5(06)
Replace the paragraph:
the new paragraph:
!corrigendum 9.1(9.1)
!comment Dummy to force conflict; actual change is in the conflict file.
@drepl Dummy. @dby Dummy.
!corrigendum 9.4(11)
!comment Dummy to force conflict; actual change is in the conflict file.
@drepl Dummy. @dby Dummy.
!corrigendum 12.5.1(03)
Replace the paragraph:
formal_derived_type_definition ::= [abstract] new subtype_mark [with private]
by:
formal_derived_type_definition ::= [abstract] [limited] new subtype_mark [[and interface_list] with private]
!corrigendum 12.5.1(05)
Replace the paragraph:
The ancestor subtype of a formal derived type is the subtype denoted by the subtype_mark of the formal_derived_type_definition. For a formal derived type declaration, the reserved words with private shall appear if and only if the ancestor type is a tagged type; in this case the formal derived type is a private extension of the ancestor type and the ancestor shall not be a class-wide type. Similarly, the optional reserved word abstract shall appear only if the ancestor type is a tagged type.
by:
The ancestor subtype of a formal derived type is the subtype denoted by the subtype_mark of the formal_derived_type_definition. For a formal derived type declaration, the reserved words with private shall appear if and only if the ancestor type is a tagged type; in this case the formal derived type is a private extension of the ancestor type and the ancestor shall not be a class-wide type. Finally, the reserved word limited shall appear only if the ancestor type and any progenitor types are limited types.
!ACATS test
ACATS tests should be constructed to test the legality rules here.
!appendix

From: Tucker Taft
Sent: Thursday, February 17, 2005  3:31 AM

I had a grab-bag of morning after thoughts
after the Paris ARG meeting.  (Unfortunatly, that
meant I didn't get much sleep on the overnight
train to Tignes. ;-)  I'll mention just the first two
in this e-mail, since I am running out of time
before I have to zip off to the airport.

-----------------

1) There seems no reason we can't "hide" the
fact that a type implements an interface, so long
as the interface has no user-defined primitive
subprograms.  In some ways, this is analogous to
hiding the fact that a type is nonlimited, if
we think of "nonlimitedness" as being an interface
with ":=" and "=".

2) I am concerned about the rule that if you
have a type that should be nonlimited, but also
needs to implement one or more limited interfaces,
that you have to concoct an "artificial" nonlimited
parent type or interface.  One possibility that
popped into my head was:

     type T is not limited and Int1 and Int2 with ...

This way the type can implement some limited interfaces without
having to specify a nonlimited parent.  This syntax
also suggests a similar solution for partial views
of tasks and protected types that implement interfaces:

     type TT is task and Int1 and Int2 with private;
     type PT is protected and Int1 and Int2 with private;

And for completeness, and also to make the fact that a type
is "limited" clear:

     type LT is limited and Int1 and Int2 with ...

and at which point we might as well allow

     type ST is synchronized and Int1 and Int2 with private;

Hence the overall syntax would be approximately:

     type <identifier>[<discrim_part>] is
       <kind> and <interface_list> with ...

     <kind> ::= [not] limited | task | protected | synchronized

This syntax would only be permitted when there is an interface_list,
as a way to specify the kind of the type without having to rely
on there being an interface that has that same kind.

My biggest fear is that there will be a proliferation of
silly interfaces just to provide parent types when wanting
to make a type nonlimited, or indicate that a generic formal
type is a task, etc.  Having a proliferation of these will
make integration across independently developed subsystems
that much harder, and is worse than the similar situation
with access types, since we have said there may be no
hidden interfaces.  Note that idea (1) above is a partial
solution to this problem, but I still think the proliferation
will be nasty.  I also think the added documentation of
specifying "not limited" or "limited" explicitly will
be an advantage.

More later ;-)

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

From: Pascal Leroy
Sent: Thursday, February 17, 2005  7:59 AM

> 1) There seems no reason we can't "hide" the
> fact that a type implements an interface, so long
> as the interface has no user-defined primitive
> subprograms.  In some ways, this is analogous to
> hiding the fact that a type is nonlimited, if
> we think of "nonlimitedness" as being an interface
> with ":=" and "=".

Hmm.  I wouldn't be so optimistic.

In the case of nonlimited interfaces, it seems to me that at a minimum you
would not want to hide the nonlimitedness.  In other words, the following
should be illegal:

	type I is interface;

	package P is
	   type T is tagged limited private;
	private
	   type T is new I with null record;
	end P;

If it is not illegal, clients of P can derive from P.T and redefine "=",
and we would end up with the same situation as described in the AARM note
of AI 396, where the client ends redefine an operation that they don't
know about, or where the type ends up with two distinct "=" operators. In
the case of limited interfaces, you have to deal with the compatibility
rules between task and protected interfaces.  For example:

	type TI is task interface

	package P is
	   type T is tagged limited private;
	private
	   type T is new TI with null record;
	end P;

	type PI is protected interface;
	type T2 is new P.T with PI and null record; -- Legal

One hope that the declaration of type T2 is illegal, because there is no
way that a type can implement both a task interface and a protected
interface, but of course this leads to a privacy violation.

I suppose the idea could be made to work with appropriate restrictions,
but it's probably not worth the effort.  I for one cannot get excited
about interfaces which have no user-defined subprograms.

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

From: Randy Brukardt
Sent: Thursday, February 17, 2005  11:30 AM

I would go further and say that a type with no subprograms and no components
is next to useless. The mostly likely place for them to show up would be
prototyping, and you wouldn't be doing anyone any favors by allowing them in
places that would become illegal when the subprograms are later added to the
interface.

So I suggest that Tuck get some sleep rather than worrying about these
ideas. :-)

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

From: Tucker Taft
Sent: Thursday, February 17, 2005  4:06 PM

These are used pretty heavily in Java, as a run-time testable
property of a type.  E.g. Cloneable, Serializable, EventListener.
They are called "marker" interfaces, in one article I found.

I think they might turn out to serve a similar purpose in
Ada, and if so, we wouldn't want to place unnecessary requirements
on them such as requiring them to be visible in all cases.

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

From: Randy Brukardt
Sent: Thursday, February 17, 2005  5:59 PM

I don't see how the latter follows the former. I'm strongly opposed to rules
that depend on the shapes of types, whether there are components or
interfaces, the phase of the moon, or anything else that is easy to change.
Those are maintenance bombs waiting to happen. I still oppose AI-391 (not
sure why I didn't vote against it), and I'll oppose this, too.

The model of interfaces is that it doesn't matter where in the hierarchy
they are added. That makes them fundamentally privacy-breaking, and the rule
that requires them to be visible for tagged types eliminates problems. If we
go away from that, we'll have to revisit lots of things (like the
Interface_Ancestor_Tags function we added last week), because they wouldn't
be meaningful.

I also do not see how this could work in Ada. (I don't see how it could work
in any language, for that matter, that claims to have strong typing.) The
only possible thing that you could do is test for membership, as there are
no operations to dispatch to. (That's already very dubious from an O-O
perspective, but let's not go there.) You can't even say "Foobar in
Serializable", because both operands have to be class-wide before you are
even allowed to combine them. You would have to say "Foobar in
Serializable'Class", which is bizarre, since there would never be an
interface descended from Serializable for this use. And once that proved to
be true, what could you do? Absolutely nothing, unless you use this to
assume some implementation in terms of predefined operations. "Serializable"
is an example; presumably it means that the type has 'Read and 'Write; but
the language already handles that in a variety of ways. A further marker
isn't needed in this particular case.

I certainly see no reason to copy hair-brained ideas from Java; Ada can (and
does) do this stuff so much better. If all we're trying to do is clone Java,
count me out.

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

From: Pascal Leroy
Sent: Friday, February 18, 2005  3:27 AM

Tuck wrote:

> These are used pretty heavily in Java, as a run-time testable
> property of a type.  E.g. Cloneable, Serializable,
> EventListener. They are called "marker" interfaces, in one
> article I found.

I see this as poor design on the part of the Java folks, and I don't
understand why we would want to replicate it in Ada.

Take for instance Cloneable.  The Java hierarchy is such that Object (the
root of everything) has a clone method.  But for some classes clone
doesn't make sense.  So they add a class Cloneable that is just a marker
(I have seen this called a tagging interface, too), and the method clone
raises an exception for all classes that don't implement Cloneable, and no
others.

Yuck.  Someone was terribly confused when they decided to provide clone at
the Object level.

Note that .Net has a structure that I find much cleaner: the interface
ICloneable has a single method, Clone, and you inherit Clone iff you
implement ICloneable.  No cheesy exceptions, membership tests, etc.

Serializable has similar semantics.  I didn't check the others, but it
certainly seems like this "tagging" mechanism is widely (mis)used in Java.

Of course, in Ada Cloneable is spelled "nonlimited" and Serializable is
spelled "stream-oriented attributes are available" so for these two
particular interfaces we have support hard-wired in the language.

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

From: Jean-Pierre Rosen
Sent: Friday, February 18, 2005  4:03 AM

[...]
I strongly support that view. I always claimed that this use of
interfaces was a kludge, used only for "magic" interfaces that were
recognized specially by the language.

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

From: Pascal Leroy
Sent: Thursday, February 17, 2005  8:15 AM

> 2) I am concerned about the rule that if you
> have a type that should be nonlimited, but also
> needs to implement one or more limited interfaces,
> that you have to concoct an "artificial" nonlimited
> parent type or interface.
...

We discussed something like that in the past, and one Tucker Taft was not
exactly enthusiastic.  See the mail dated October 29 in AI 345.

I am not at all opposed to a syntax like the above, as it's clear that
making the first interface "special" is somewhat unpleasant.  However, I
am going to insist that whatever syntax we choose include the word "new"
somewhere, as we are really writing type derivations.  We want to make
sure that the syntax clearly distinguishes an interface declaration like:

	type I is I1 and I2 and I3;

from a "normal type" declaration like:

	type T is new I1 and I2 and I3 with ...;

When I look at the syntax above, it's not clear to me if you are declaring
bona fide types or mere interfaces.

Oh, and I hate to mention this, but it's awfully late.  At this point the
only part of the core language that you should be changing is AI 416...

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

From: Robert Eachus
Sent: Thursday, February 17, 2005  3:51 PM

I agree that it is almost certainly too late to entertain a change in
the syntax of this magnitude.  However, I think that
a legality rule should fix the problem.  If a type which inherits
interfaces is not declared limited, it is not limited.  Add that
if a rule that says that a type which inherits from an interface which
must be limited (such as a synchronized interface) must be declared limited.

Now in the example above,
    type T is limited and Int1 and Int2 with...
is a limited type, and
    type T is Int1 and Int2 with...
*if legal*, is a non-limited type.

Now you get down to the real issue, when can an interface declared as
limited be inherited by a non-limited type?  I think the rule has to be
that if public interface is a task, protected or synchronized interface,
a non-limited type or interface can't inherit it. It probably makes
sense in that case to say that interfaces derived from task, protected,
or synchronized interfaces must either be declared to be of the same
class or (the more general) synchronized.

So why have limited interfaces in the first place?  Because there are
cases where you want to declare an interface that doesn't require
assignment and equality.  There is no reason to prevent such interfaces
from being inherited by non-limited types.  But the task, protected, and
synchronized interface declarations make no sense if implemented by a
type with copying permitted.

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

From: Tucker Taft
Sent: Thursday, February 17, 2005  4:13 PM

> We discussed something like that in the past, and one Tucker Taft was not
> exactly enthusiastic.  See the mail dated October 29 in AI 345.

I admit my view has changed (that's why it is called "morning
after" ;-).  In examples, I have been bumping into this
requirement to have a "bogus" parent type just to avoid
having the new type end up limited if it wants to implement
a limited interface.

>
> I am not at all opposed to a syntax like the above, as it's clear that
> making the first interface "special" is somewhat unpleasant.  However, I
> am going to insist that whatever syntax we choose include the word "new"
> somewhere, as we are really writing type derivations.

I guess I was somewhat attracted to the idea these were *not*
like type derivations, in that they aren't inheriting code or components
from anything.  They are simply "promising" to implement one or
more interfaces.  These types would *not* have a parent type, though
they would have progenitor interfaces, and hence a set of
interface ancestors.

 > ... We want to make
> sure that the syntax clearly distinguishes an interface declaration like:
>
> 	type I is I1 and I2 and I3;

That is not a legal interface declaration.  Interface
declarations always need the word "interface":

     type I is interface and I1 and I2 and I3;

>
> from a "normal type" declaration like:
>
> 	type T is new I1 and I2 and I3 with ...;
>
> When I look at the syntax above, it's not clear to me if you are declaring
> bona fide types or mere interfaces.

All interface type declarations have the word "interface,"
so I don't see the possible confusion.  I think it would
be worse to have the word "new" but have no parent type
specified immediately thereafter.

>
> Oh, and I hate to mention this, but it's awfully late.  At this point the
> only part of the core language that you should be changing is AI 416...

The morning after is always a bit late...

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

From: Randy Brukardt
Sent: Thursday, February 17, 2005  6:07 PM

> I guess I was somewhat attracted to the idea these were *not*
> like type derivations, in that they aren't inheriting code or components
> from anything.  They are simply "promising" to implement one or
> more interfaces.  These types would *not* have a parent type, though
> they would have progenitor interfaces, and hence a set of
> interface ancestors.

I had suggested something like this well back at the start. I was told I was
confused. I do not look kindly on being told I was right all along, but we
should invent some totally new syntax to handle this. I thought then that
interface ancestors needed to be added to root tagged record declarations
(like we eventually did for tasks and protected types). Something like:

    type Rec is tagged limited record {I1 and}
         Comp1 : ...
    end record;

If we're going to declare types with no parents, they ought to look like
them. And I see no reason whatsoever to invent a new kind of type
declaration (as Tucker did) to support that.

But make no mistake about it -- if we even think about changing this syntax,
we'll have to delay the completion of the Amendment by at least one meeting.
And that would substantially increase the likelyhood of running out of money
and effort. It had better be *important* in order to do that.

(If we were to make this change, we would have to reread the entire AARM
looking for places where a parent is assumed if there are any progenitors.
That's going to be a huge job.)

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

From: Tucker Taft
Sent: Thursday, February 17, 2005  6:49 PM

I believe task and protected types that implement interfaces
already have this situation, for what it's worth.

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

From: Pascal Leroy
Sent: Friday, February 18, 2005  3:08 AM

> > 	type I is I1 and I2 and I3;
>
> That is not a legal interface declaration.  Interface
> declarations always need the word "interface":
>
>      type I is interface and I1 and I2 and I3;

Yeah, I can never get the syntax right.  We changed it so many times...

>     type T is not limited and Int1 and Int2 with ...
>     type TT is task and Int1 and Int2 with private;
>     type PT is protected and Int1 and Int2 with private;
>
> All interface type declarations have the word "interface,"
> so I don't see the possible confusion.  I think it would
> be worse to have the word "new" but have no parent type
> specified immediately thereafter.

From the perspective of syntax, it seems clear to me that it ought to
mimic the syntax for full type declarations, with the task/protected
definition replaced by the word private.  Thus:

	task type TT is new Int1 and Int2 with private;
	protected type PT is new Int1 and Int2 with private;

Having two unrelated syntax for the partial view and the full view would
just be too confusing.

The situation is a bit less clear for the "not limited" case, but my
preference would go to:

	type T is not limited new Int1 and Int2 with private;

Anyway, the syntax is, as usual, not the big problem here.  The impact on
the overall (static) semantics of the language is daunting.  You'd have to
define the semantics of "partial task-ish" and "partial protected-ish"
views.  And you'd have to decide what to do with generics: it would seem
reasonable to define new kinds of formal types corresponding to the
partial views above, with appropriate matching rules, and a definition of
what you can do with the formal type inside the generic.

At first sight, this looks like an earth-shattering change to me.  But if
you really feel strongly about it you can always try to put together an
AI.  Maybe we'll discover that it's not so bad after all.  (I have my
doubts.)

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

From: Tucker Taft
Sent: Friday, February 18, 2005  5:45 AM

I'll write an AI, if I continue to have
enthusiasm for this.  Note that we allow
*adding* an interface_list to task, protected,
and interface type definitions, without
forcing the use of "new".  What I am proposing
is to add the ability to include an interface_list
in record definitions and private type definitions.

Then any type that is non-array composite would
allow an interface_list, including derived types
of course.  And if Randy finds it familiar, I hope
that isn't a bad thing.  Two big changes that occurred
during the evolution of the interface feature were:

   1) non-limited types could implement limited interfaces;
   2) task and protected type declarations allow an
      interface list.

I'm not sure we went back after making these two
changes and reconsidered the original tight connection
between the derived type syntax and interface_list.
We clearly want to allow interfaces as parents of
derived types, so one can change an abstract non-interface
type into an interface type with minimum disruption.
However, one may similarly want to add interfaces
to a record type or a private type, without taking
on the other requirements of a derived type (i.e.
a parent), and that seems to me what is missing.

As far as two different syntaxes for partial view
tasks/protected and full view tasks/protected, we
already have that if you declare a derived type
whose parent is a task, protected, or synchronized
interface.  It seems odd that you can get a partial
view task only if you name a task interface, but not
if you want to be more explicit about it.

Anyway, I'll dump all these arguments into a !discussion
section if I find the energy to write up an AI.

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

From: Tucker Taft
Sent: Friday, February 18, 2005  8:05 AM

Well, I don't know what version of Chapter 9
my evil twin was reading, but clearly "new"
is there, as clear as day, in protected
and task types that implement interfaces.
That certainly weakens my argument about
leaving out "new" for the others.

This AI may not see the light of day...

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

From: Randy Brukardt
Sent: Friday, February 18, 2005  10:36 AM

Humm; OK by me, but it leaves the original problem unaddressed. That is,
that deriving a non-limited type from a limited interface (which is likely
to be the most common kind) is messy. You pretty much have to put a dummy
non-limited interface around, which hardly is going to increase the clarity
of programs.

The problem seems limited (see below) to root record types, so that seems to
be the only thing that needs addressing. Let's leave "new" in the syntax.
Perhaps we need to add that to root record types:
    type Rec is tagged new I1 and I2 record
which would be a good thing if I1 and I2 were limited. The grammar would be:
    record_type_definition ::=
      [[abstract] tagged [new interface_list]] [limited] record_definition
(The idea is that the interface list associates with "tagged", same as
"abstract".)

That would be pretty consistent with task and protected types (I dropped
"with" here, because it doesn't make sense before "record", and this doesn't
belong after "record").

I'm not sure that this is a good idea, but if you are going to pursue
something, I think it should be on this line.

I have a lot harder time getting excited about partial views of task or
protected types, because I think that the interface will naturally have one
of the right classes in most interesting cases. Nonlimited isn't allowed,
and all of the others seem fine to specify a partial view.

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

From: Tucker Taft
Sent: Saturday, February 19, 2005  7:48 AM

I think you may have the same problem with non-limited private
types/extensions, where you are required to make the limitedness
of the partial view match the limitedness of the full view.
Your idea of using "tagged" as a stand-in for "non-limited"
is intriguing, though it is a bit subtle. Another possibility
is to define non-terminals "parent_subtype_indication" and
"ancestor_subtype_indication" used by derived_type_definition
and private_type_extension as follows:

    parent_subtype_indication ::=
       subtype_indication | [not] limited

    ancestor_subtype_indication :=
       subtype_indication
     | [not] limited | task | protected | synchronized

and then say that when the parent_subtype_indication is given by
a type "category" rather than a subtype_indication, the parent
type is an anonymous operation-less interface type of the
appropriate category.  This way we still have a parent type,
albeit anonymous, which has exactly the desired characteristics.
You would be allowed to have a "hidden" anonymous interface ancestor
of course, since we have other rules that determine when limitness,
nonlimitedness, etc., need to be revealed.

I might be tempted to call these anonymous interfaces the "root"
interfaces, but that might create some wording headaches
(or heartaches? ;-).

Here are examples of using this anonymous interface parent syntax.
I presume we would not allow this notation in the absence
of an interface_list (and that could probably be expressed
in the BNF).

    type NT is new not limited and Int1 with private;
    type TT is new task and Int2 with private;
    type NR is new not limited and Int3 with record ... end record;
    type NNR is new not limited and Int4 with null record;

Following Randy's suggestion, substituting "tagged" for
"not limited" is a possible alternative, though I fear
that might be confusing, albeit a bit pleasanter grammatically.

> I have a lot harder time getting excited about partial views of task or
> protected types, because I think that the interface will naturally have one
> of the right classes in most interesting cases. Nonlimited isn't allowed,
> and all of the others seem fine to specify a partial view.

Yes, I agree this is less important, though I do suspect
we will see the creation of operation-less task interfaces,
which could create annoyances when integrating independently
developed code.

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

From: Tucker Taft
Sent: Thursday, March 3, 2005  4:46 PM

The more I think about this problem, the more it seems
that adding syntax to solve this problem is overkill.
Adding one additional library package with just a
"bit" of magic would seem to solve the problem:

    package Ada.Root_Interfaces is
        type Root_Limited is limited interface;
        type Root_Nonlimited is interface and Root_Limited;
        type Root_Synchronized is
          synchronized interface and Root_Limited;
        type Root_Task is
          task interface and Root_Synchronized;
        type Root_Protected is
          protected interface and Root_Synchronized;
    end Ada.Root_Interfaces;


All tagged types and interfaces implicitly
implement Root_Limited.
All non-limited tagged types and interfaces implicitly
implement Root_Nonlimited.
All synchronized tagged types and interfaces implicitly
implement Root_Synchronized.
All task interfaces implicitly implement Root_Task, as
do all tagged task types (i.e. those that implement at least
one interface).
All protected interfaces implicitly implement Root_Protected,
as do all tagged protected types (i.e. those that implement
at least one interface).


Then any time you want to define a non-limited type
that also implements a limited interface:

     type T is new Ada.Root_Interfaces.Root_Nonlimited and lim_intf
       with ...

A partial view of a tagged task type becomes:

     type TT is new Ada.Root_Interfaces.Root_Task with private;

I believe I suggested something similar to this in the past,
but it was more of a "nice to have."  Now with the ability
to have non-limited types implementing limited interfaces, and
the general ability to have partial views of task types presuming
they implement some interface, it seems important to have
some way to specify an interface of a particular sort without
having to create one just for the purpose.

This seems preferable to new syntax...

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

From: Pascal Leroy
Sent: Friday, March 4, 2005  3:13 AM

You have indeed proposed exactly the same idea not so long ago.  See the
mail dated October 26, 2004 in AI 345.  Surely no new technical
information has surfaced since then.  In particular, it was already
possible for non-limited types to implement limited interfaces.

--

I think that all the arguments that have been voiced at the time against
this idea are still valid.  I will merely point out that this is a hybrid
approach, and I find that particularly distasteful.  Long ago we decided
to use syntax to declare interfaces; thus:

	type TI is task interface;

as opposed to using magic types, which would have looked like:

	type TI is interface and Ada.Root_Interfaces.Root_Task;

I believe that using syntax was the right approach, because it improves
legibility and avoids the confusion that results from having a single root
for the entire inheritance hierarchy.

But now you are proposing to mix up the two approaches, so the two
declarations below would be equivalent:

	type I2 is interface and Ada.Root_Interfaces.Root_Task and I1;
	type I2 is task interface and I1;

This doesn't make sense to me.  There must be only one way to declare
interface I2, not two ways based on whether you prefer syntax or magic
interfaces.  So if we go for magic interfaces, we must get rid of the
syntax for declaring limited/synchronized/task/protected interfaces.

--

One thing that I dislike with the rules as they currently are is that the
order of the interfaces matters (and your proposal doesn't change that).
Consider:

	type LI is limited interface;
	type NLI is interface;

	type T1 is new LI and NLI with null record; -- limited
	type T2 is new NLI and LI with null record; -- nonlimited

The fact that T1 and T2 differ wrt limitedness is confusing and I wonder
if we should try to change this.  Of course, in the case where the parent
is a non-interface type, the limitedness has to be inherited from the
parent.  But in the case where the parent is an interface type, I am not
sure if the rule we have is ideal.  I might try to write an AI to fix
this.

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

From: Robert I. Eachus
Sent: Satursday, March 5, 2005  5:00 PM

Pascal Leroy wrote:

>	type T1 is new LI and NLI with null record; -- limited
>	type T2 is new NLI and LI with null record; -- nonlimited
>
>The fact that T1 and T2 differ wrt limitedness is confusing and I wonder
>if we should try to change this.  Of course, in the case where the parent
>is a non-interface type, the limitedness has to be inherited from the
>parent.  But in the case where the parent is an interface type, I am not
>sure if the rule we have is ideal.  I might try to write an AI to fix
>this.

I think it is extremely confusing, which is why I proposed that a type
that inherits only from interfaces must contain the reserved word
'limited' to be a limited type:

	*type* T1 *is limited new* LI *and* NLI *with null record*; -- limited
	*type* T2 *is new* LI *and* NLI *with null record*; -- nonlimited

The only problem with this idea, and I am not sure it is a problem, is
when such a type must be limited because one of the parent interfaces is
synchronized or a task interface.  Favoring readers over writers in that
situation argues for a legality rule.  Yes, the writer may be a bit
exasperated as he goes back to add the (otherwise unnecessary)
'limited.'  but readers would sure find it useful.

This could be extended to allow/require:

      *type* T2* is not limited **new *L1* and *NLI* with null record;
*
I my opinion that would be extreme overkill.  In Ada the rule is that a
type is non-limited, unless it is derived from a limited type, is a task
or protected type, or is defined to be limited.  Let's keep it that
way.  Incidentally I am not at all sure that it is necessary that types
that implement synchronized interfaces must be limited.  But I don't see
any real reason to open that can of worms in the declaration.  Certainly
there will be cases where in the body of the package that implements a
synchronized type copying objects of the type may be necessary.  But in
those cases I expect that the visible type will be a record containing
an indirection pointer.*
*
****************************************************************

From: Tucker Taft
Sent: Saturday, March  5, 2005  7:30 AM

Pascal Leroy wrote:

> One thing that I dislike with the rules as they currently are is that the
> order of the interfaces matters (and your proposal doesn't change that).
> Consider:
>
> 	type LI is limited interface;
> 	type NLI is interface;
>
> 	type T1 is new LI and NLI with null record; -- limited
> 	type T2 is new NLI and LI with null record; -- nonlimited
>
> The fact that T1 and T2 differ wrt limitedness is confusing and I wonder
> if we should try to change this.  Of course, in the case where the parent
> is a non-interface type, the limitedness has to be inherited from the
> parent.  But in the case where the parent is an interface type, I am not
> sure if the rule we have is ideal.  I might try to write an AI to fix
> this.

Ok, I'm convinced.  Not to preempt you, but combining various
suggestions from you, Randy, Robert Eachus, etc., I would
support the following:

     type NT is [abstract] [limited] new I1 and I2 and I3 with ...

Limitness may be asserted on a derived type, in the
same way it may be asserted on a record type.  Limitness
is *not* inherited from a parent or progenitor that is a
limited, non-synchronized interface, so limitness *must* be
asserted if the parent and progenitors are all limited,
non-synchronized interfaces, and the programmer wants
the derived type to be limited.

We could go further and say that limitness is *not*
inherited from interfaces, period.  That would mean
that "limited new" is *required* if the parent or any of
the progenitors are synchronized.  I suppose that would
be consistent with the current rule for tagged limited
record types, where you have to say limited explicitly
even if there is a task or protected component.  It
would also be consistent with our rule on defining
interfaces, where you must respecify limited (or some
other qualifier like "synchronized") always,
independent of the progenitors.

I would allow limitedness to be asserted when it *is*
inherited, so that you can change between an interface
and a tagged type during maintenance, and not have to
remove "limited" from the places where it is now
inherited.  This would be consistent with untagged
records, where we allow "limited" even when it is
redundant.

I suppose we could go further and allow "limited" to
be asserted on an untagged derived type, and in that
way support a coding convention where limited is always
re-asserted, whether required or not, as documentation.
Perhaps in Ada 2015, we could make it mandatory,
in the same way "abstract" is always re-specified.
Or even now, we could make omitting "limited" obsolescent.

I suppose the one downside here is in a generic, where you
*want* to inherit from the formal type without having
to specify whether or not the derived type is limited.
So it seems like we probably want to continue to allow
"limited" to be implicit in some circumstances.
This might actually argue for saying you don't inherit
limitness from a null record type, to keep the equivalence
between an interface and a null record type.  On the
other hand, a null record type can easily become non-null
during maintenance, whereas an interface will forever
have no components.  But we do need to keep an eye on
this equivalence between interfaces and null records,
especially with respect to generics.

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

From: Bob Duff
Sent: Saturday, March  5, 2005  9:47 AM

Tuck wrote:

> Ok, I'm convinced.  Not to preempt you, but combining various
> suggestions from you, Randy, Robert Eachus, etc., I would
> support the following:
>
>      type NT is [abstract] [limited] new I1 and I2 and I3 with ...
>
> Limitness may be asserted on a derived type, in the
> same way it may be asserted on a record type.  Limitness
> is *not* inherited from a parent or progenitor that is a
> limited, non-synchronized interface, so limitness *must* be
> asserted if the parent and progenitors are all limited,
> non-synchronized interfaces, and the programmer wants
> the derived type to be limited.

Sounds OK.

> We could go further and say that limitness is *not*
> inherited from interfaces, period.  That would mean
> that "limited new" is *required* if the parent or any of
> the progenitors are synchronized.  I suppose that would
> be consistent with the current rule for tagged limited
> record types, where you have to say limited explicitly
> even if there is a task or protected component.  It
> would also be consistent with our rule on defining
> interfaces, where you must respecify limited (or some
> other qualifier like "synchronized") always,
> independent of the progenitors.

That sounds OK, too.  This rule might be easier for programmers to learn
and remember, so I slightly prefer it.

> I would allow limitedness to be asserted when it *is*
> inherited, so that you can change between an interface
> and a tagged type during maintenance, and not have to
> remove "limited" from the places where it is now
> inherited.  This would be consistent with untagged
> records, where we allow "limited" even when it is
> redundant.

I don't like allowing it.  Anything that's not forbidden is required.
;-)

The analogy with "limited record" doesn't work.  The real rule (the one
I follow in my code) is to always say "limited record" on a limited
record.  I think we didn't make that the language rule purely for upward
compatibility.

But the corresponding rule "always specify limited on a limited derived
type" seems like overkill.

The changing-to-interface argument is a good one.  But it doesn't seem
like that big of a deal -- you're changing the code anyway, and
recompiling it all, so it's not so horrible to go around and add
"limited" all over.

> I suppose we could go further and allow "limited" to
> be asserted on an untagged derived type, and in that
> way support a coding convention where limited is always
> re-asserted, whether required or not, as documentation.
> Perhaps in Ada 2015, we could make it mandatory,
> in the same way "abstract" is always re-specified.
> Or even now, we could make omitting "limited" obsolescent.

I could live with the option suggested as an *option*,
but I'd be against making it mandatory in 2015 (or ever).

Limitedness is unlike abstractness.  Abstractness is a property of a
specific type, and it makes no sense to inherit it.  Limitedness is a
class property, and should be inherited from the parent type, just like
all the other operations.  (Pascal is wrong to think that having a named
type for the root of this hierarchy is a kludge.  Trees are cleaner than
forests; all hierarchies should have roots.  But he's right that the
mixed-syntax-and-magic-types idea was kind of ugly.)  Anyway, it would
not be upward compatible.

Making this an option makes sense only if you think people should always
do it, and compatibility prevents you from making it a real rule.  But I
don't think that, so I wouldn't follow such a convention.  Options like
this cause the language to diverge into different styles.  There are at
least three different styles floating around for whether to put 'in' on
an in-mode parameter, and that's not helpful; Ada 83 should have made it
mandatory or forbidden.

> I suppose the one downside here is in a generic, where you
> *want* to inherit from the formal type without having
> to specify whether or not the derived type is limited.

I took advantage of that in some code I wrote recently.

> So it seems like we probably want to continue to allow
> "limited" to be implicit in some circumstances.
> This might actually argue for saying you don't inherit
> limitness from a null record type, to keep the equivalence
> between an interface and a null record type.

That wouldn't be upward compatible.  In the case I mentioned above, the
thing I was inheriting from *was* a null record (sometimes limited,
sometimes not).

>...On the
> other hand, a null record type can easily become non-null
> during maintenance, whereas an interface will forever
> have no components.  But we do need to keep an eye on
> this equivalence between interfaces and null records,
> especially with respect to generics.

How so?

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

From: Tucker Taft
Sent: Saturday, March  5, 2005  12:30 PM

>>We could go further and say that limitness is *not*
>>inherited from interfaces, period. ...
>
>
> That sounds OK, too.  This rule might be easier for programmers to learn
> and remember, so I slightly prefer it.

Yes, it seems simpler to say that limitness is
not inherited from an interface, even if it is synchronized.
That applies whether the new type is an interface
or is a "normal" tagged type.

 >> ...  But we do need to keep an eye on
>>this equivalence between interfaces and null records,
>>especially with respect to generics.
>
>
> How so?

An interface can be passed as an actual type when the formal
is an abstract limited tagged type.  If you extend this
formal type inside the generic, then whether you inherit
limitness from it depends on whether or not the actual
turns out to be an interface (based on this new proposal).
That breaks the equivalence between interfaces and
abstract null records, which is OK, since we know
that interfaces have some special properties, but we
have to be sure that this distinction doesn't break
some other assumption.

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

From: Robert I. Eachus
Sent: Saturday, March  5, 2005  6:19 PM

My head hurts a lot after just reading this, and I am not sure I see
the advantage to what could be a maintenance nightmare for both Ada
programmers and compiler implementors.  I can see reasons for passing an
interface to a generic as an interface, and probably there is an
advantage to providing a generic formal type that matches all the
possibilities:

type Formal is new {limited | task | protected | synchronized} interface;

(Currently AI-345 seems to match this, except for the limited.)

I don't quite understand, though, why a generic formal synchronized
interface can't be matched by a protected interface. And I can't seem to
get my mind around Tuck's comment above and AI-251.  I had been thinking
of interface types as a class hierarchy where an interface of lesser
generality could be passed as an actual.  In other words, a generic that
took a synchronized interface as a formal parameter could have an actual
which was a task or protected interface.  Of course the interface type
must be derived from within the generic to do anything useful with it,
and there the hierarchy does come into play.  But AI-251 seems to assume
that there is no easy way to have the actual interface type do other
than match the formal.  I don't see the implementation difficulties--but
I also don't see any reason not to have that restriction.  The case that
Tuck is in effect arguing for looks much more difficult than
synchronized formal and protected actual interfaces, and much less
useful.  Am I missing something?

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

From: Pascal Leroy
Sent: Monday, March  7, 2005  9:59 AM

> I would
> support the following:
>
>      type NT is [abstract] [limited] new I1 and I2 and I3 with ...

Fine.

> We could go further and say that limitness is *not*
> inherited from interfaces, period.  That would mean
> that "limited new" is *required* if the parent or any of
> the progenitors are synchronized.

That's what I prefer.  It's easier to explain.  And limitedness of
interfaces is peculiar anyway, since a limited interface can be
implemented by a limited type.  (Interfaces are analogous to generic
formal types from that perspective: the fact that a formal is limited
doesn't guarantee that an actual is; the fact that an interface is limited
doesn't guarantee that a concrete type is.)

I have been playing for some time with the notion that *nonlimitedness*
should be inherited for interfaces: if an interface is nonlimited, it
depends on the fact that any concrete type has assignment and "=".  But of
course inheriting nonlimitedness is at odds with the rest of the language.
Your approach is better.

> I would allow limitedness to be asserted when it *is* inherited

Like Bob, I don't like optional syntax.

> I suppose we could go further and allow "limited" to
> be asserted on an untagged derived type, and in that
> way support a coding convention where limited is always
> re-asserted, whether required or not, as documentation.
> Perhaps in Ada 2015, we could make it mandatory, in the same
> way "abstract" is always re-specified. Or even now, we could
> make omitting "limited" obsolescent.

Nay, don't go there, too late, and not useful enough.  Jean should have
done it that way back in '79.  Let's blame him, as usual.

> This might
> actually argue for saying you don't inherit limitness from a
> null record type, to keep the equivalence between an
> interface and a null record type.  On the other hand, a null
> record type can easily become non-null during maintenance,
> whereas an interface will forever have no components.  But we
> do need to keep an eye on this equivalence between interfaces
> and null records, especially with respect to generics.

If we can preserve this equivalence between interfaces and abstract null
records, fine, but I wouldn't bend over backwards to do it.  First, I
don't think there are many abstract null records out there: when I use
abstract types, I always put "common" components there; if there are no
common components, sure, I have a null record, but it's a fluke rather
than a conscious decision.  Second, I believe that taking advantage of
interfaces will require serious rearchitecturing, so people won't do that
as an afternoon project by peppering a few packages with the word
"interface".  Chances are that massive changes will be required anyway.I
am wondering now if the fact that an interface can be passed to an
abstract formal type is creating a hole.  Consider:

	type Intf is interface;

	generic
	   type A is abstract tagged private;
	package G is
	   type T is tagged private;
	private
	   type T is new A with ...
	end G;

	package Inst is new G (Intf);

It seems that the instantiation Inst is effectively violating the
no-hidden-interfaces rule.  Am I missing something, or should this just be
illegal?

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

From: Pascal Leroy
Sent: Monday, March  7, 2005  10:07 AM

> Pascal is wrong to think that having a named type for the
> root of this hierarchy is a kludge.  Trees are cleaner than
> forests; all hierarchies should have roots.

You won't convince me and I won't convince me, but hopefully you'll agree
that changing Ada from a forest language to a tree language is a major
design decision, and should not happen as part of a last-minute fix.

Not to mention that you were part of the 9X design which, in its infinite
wisdom, chose to make Ada a forest language ;-)

(Actually, Ada is tree-ish in the sense that all "interesting" types have
to be derived from Ada.Finalization.Controlled.  After 10 years of usage,
it still sticks out like a sore thumb.)

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

From: Pascal Leroy
Sent: Monday, March  7, 2005  10:18 AM

> I don't quite understand, though, why a generic formal synchronized
> interface can't be matched by a protected interface.

This has been puzzling me for a long time, too, but one rather compelling
answer is that there are things that you can do with synchronized
interfaces and that you cannot do with protected interfaces.  In
particular, you can extend a protected interface to create a taskish
thing.  Consider:

	generic
	   type SI is synchronized interface;
	package G is
	   type TI is task interface and SI;
	   task type TT is new SI with
	      entry E;
	   end TT;
	end G;

If G could be instantiated with a protected interface, it would be bad
news, because TI would be a task-and-protected interface, and TT would be
a task-and-protected type.

This could be taken care of by rechecking the rules on instantiation, and
assuming-the-worst in generic bodies.  But that doesn't seem like the
right trade-off.

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

From: Tucker Taft
Sent: Monday, March  7, 2005  12:50 PM

> That's what I prefer.  It's easier to explain.  And limitedness of
> interfaces is peculiar anyway, since a limited interface can be
> implemented by a limited type.

I bet you meant "by a *non*limited type."



>>I would allow limitedness to be asserted when it *is*
>>inherited
>
>
> Like Bob, I don't like optional syntax.

But I think you want to at least allow it in a generic, since
you may not know whether the parent is an interface or
a "normal" abstract tagged type.  And if you want the
new type to be limited even if the actual is an interface,
there is no easy way to get that with this new rule.

> ...I
> am wondering now if the fact that an interface can be passed to an
> abstract formal type is creating a hole.  Consider:
>
> 	type Intf is interface;
>
> 	generic
> 	   type A is abstract tagged private;
> 	package G is
> 	   type T is tagged private;
> 	private
> 	   type T is new A with ...
> 	end G;
>
> 	package Inst is new G (Intf);
>
> It seems that the instantiation Inst is effectively violating the
> no-hidden-interfaces rule.  Am I missing something, or should this just be
> illegal?

I think the no-hidden-interfaces rule has to be applied in the private
part of an instance.  Since you can't extend a formal tagged type
in a body, then checking for no hidden interfaces can always be
performed without breaking the contract model.

I do think it is important to allow interfaces to be passed
when the formal is an abstract tagged type, or else we will
get into having to have two generics when one does the job.
But we now have two differences between an interface and a
"normal" abstract tagged type:

   1) no hidden interfaces
   2) limitedness not inherited

These differences obviously complicate the model, but we pretty
clearly can't impose the interface rules on all abstract types
for compatibility reasons.  Imposing the equivalence only on abstract
null records doesn't really help, because that just moves the "bump"
to a new place, since you can't specify "nullness" of a generic
formal abstract tagged type.

So to summarize my suggestion:
   1) You never inherit limitness from an interface;
   2) You can always assert limitness on a type extension,
      to address the possibility that the parent is a formal
      abstract tagged type whose actual is a limited interface.

Part (1) above jibes with the preference expressed by you and Bob.
Part (2) above, which violates the "optional syntax" is bad metarule,
I claim is analogous to the optional "limited" for record types
with limited components -- think of a non-interface parent like
a component, and you get pretty much the same model.  Predefined
equality also treats a non-interface parent like a component,
so there is some precedent there...

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

From: Randy Brukardt
Sent: Monday, March  7, 2005  1:45 PM

> It seems that the instantiation Inst is effectively violating the
> no-hidden-interfaces rule.  Am I missing something, or should this just be
> illegal?

It *is* illegal, because you always recheck legality rules in the spec. of
an instance. Certainly this one doesn't pass that recheck. In the body, we
don't allow deriving from a formal type anyway, for the precise reason that
we can't recheck the legality rules, and there are a number of (existing)
ones that can fail. This is just one more to add to that list. (The legality
rule certainly apply in the private part of an instance; I've never
understood why that isn't the default, *not* checking legality rules should
be the unusual special case.)

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

From: Tucker Taft
Sent: Monday, March  7, 2005  2:30 PM

I think we were trying to preserve the "fiction" that privacy
applies when instantiating a generic.  But alas, in many cases,
you need to look in the private part of the generic to know
whether a given instantiation will be legal.

I guess the real "privacy" rule that applies to generics is that
to *use* an instance of a generic, you don't need to look in the
private part.  But to instantiate a generic, you probably do need
to look to see how the formal types are actually used in the
private part.  I guess that shouldn't be *too* surprising...

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

From: Bob Duff
Sent: Monday, March  7, 2005  3:12 PM

Pascal wrote:

> You won't convince me and I won't convince me, ...

And *I* won't convince *me*.  ;-)

>... but hopefully you'll agree
> that changing Ada from a forest language to a tree language is a major
> design decision, and should not happen as part of a last-minute fix.

Yes, I agree.

> Not to mention that you were part of the 9X design which, in its infinite
> wisdom, chose to make Ada a forest language ;-)

Hmm.  I don't remember whether we proposed all of the Root_Limited and
friends, but I do remember that we proposed a Root_Task type, and got
tarred and feathered for it.  To this day, I think that was a cleaner
solution than the Task_Ids package, which is untypesafe and allows
dangling pointers.

But I'm not pushing to add Root_Task *now*.

> (Actually, Ada is tree-ish in the sense that all "interesting" types have
> to be derived from Ada.Finalization.Controlled.  After 10 years of usage,
> it still sticks out like a sore thumb.)

Well, I still use untagged records quite a bit.  There are many places
in my code where the overhead of finalization (given most compiler's
implementations) would be intolerable.  I also use 'limited' quite a
lot, because ":=" doesn't work, and I don't want Controlled.

You may have noticed me pushing for various AI's that make limited types
usable.  ;-)

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

From: Bob Duff
Sent: Monday, March  7, 2005  5:21 PM

> But I think you want to at least allow it in a generic,

OK, I'm convinced.  I don't entirely buy your limited-records analogy,
but the above generics argument is pretty compelling.

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

From: Pascal Leroy
Sent: Tuesday, March  8, 2005  5:17 AM

> > Like Bob, I don't like optional syntax.
>
> But I think you want to at least allow it in a generic, since
> you may not know whether the parent is an interface or a
> "normal" abstract tagged type.  And if you want the new type
> to be limited even if the actual is an interface, there is no
> easy way to get that with this new rule.

You're right.  But if you allow it in a generic, you have to allow it
everywhere, because you want to make it easy to turn a nongeneric package
into a generic (or vice-versa).

> I do think it is important to allow interfaces to be passed
> when the formal is an abstract tagged type, or else we will
> get into having to have two generics when one does the job.

I am not sure if I agree with this.  If it comes for free, fine, but if it
hairs up the language, it bothers me.  I guess I don't see the need as
compelling enough.  On the other hand if you cannot pass an interface to a
generic formal abstract, it means that you don't have a "most general
formal tagged type" and that's unpleasant.

> But we now have two differences between an interface and a
> "normal" abstract tagged type:
>
>    1) no hidden interfaces
>    2) limitedness not inherited
>
> These differences obviously complicate the model, but we
> pretty clearly can't impose the interface rules on all
> abstract types for compatibility reasons.  Imposing the
> equivalence only on abstract null records doesn't really
> help, because that just moves the "bump" to a new place,
> since you can't specify "nullness" of a generic formal
> abstract tagged type.
>
> So to summarize my suggestion:
>    1) You never inherit limitness from an interface;
>    2) You can always assert limitness on a type extension,
>       to address the possibility that the parent is a formal
>       abstract tagged type whose actual is a limited interface.

I strongly suggest writing an AI.

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

From: Tucker Taft
Sent: Tuesday, March  8, 2005  8:02 AM

I thought you were going to write one.  Did I preempt
you with my blabbing so that now it is mine?  Let me
know...

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

From: Pascal Leroy
Sent: Tuesday, March  8, 2005  11:02 AM

I guess you touched it last, so yes, it's yours.

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


Questions? Ask the ACAA Technical Agent