CVS difference for ais/ai-00325.txt

Differences between 1.3 and version 1.4
Log of other versions for file ais/ai-00325.txt

--- ais/ai-00325.txt	2003/12/07 04:54:13	1.3
+++ ais/ai-00325.txt	2003/12/10 00:14:05	1.4
@@ -2437,3 +2437,1305 @@
 
 *************************************************************
 
+From: Robert I. Eachus
+Sent: Sunday, December 7, 2003 12:17 AM
+
+>>Here is an approach that might accomplish all of our goals.
+>>Enhance the procedure-renamed-as-function by also
+>>allowing the renaming of something that looks like a procedure *call*.
+>
+>Well, I think this would solve my specific concern. But I think you're just
+>making the idea uglier.
+
+Once I bent my head around it, it isn't that bad, and adding functions
+seems to cover the case of unknown discriminants nicely.  But there is
+no need to subject users to this mind bending experience.  What is the
+difference between renaming a procedure call as a function, and having a
+constructor function that calls the procedure instead?  Just a lot of
+mental gymnastics that users wil complain about forever.  The way the
+construction returns its result needs to be special because the value
+has to be built in place.  Adding the special subprogram type which is
+needed for many reasons to the "return ... do ... end;" construct which
+should only be allowed in subprograms that are marked in the
+specification in some way--and the name constructor deos that
+nicely--results in a solution that users will just use without
+complaining about the need for renaming, or that there is no obvious way
+to convert a C++ program with constructors to Ada.  This is a nice to have.
+
+Note one other very nice to have about the "return...do...end;
+construct.  The object name after the return can be used as a prefix
+within the sequence of statements.  The compiler has to do whatever
+magic is required to match this to the place where the created object
+belongs, and raise Constraint_Error if there is a mismatch.
+(Constraints in both the return value and the object being created and
+they don't match.)  But the normal cases work just fine.  If the type is
+classwide, or the object is of an unconstrained subtype of a type with
+discriminants, the values of the discriminants are supplied by the
+object named in the return statement.  From that point on, from both the
+user and compiler's perspective, all the magic is done.  And if you need
+attributes of the actual object to do the initialization, they are
+already there "for free."    There is a LOT of subtle semantics here.
+Probably the most subtle part is what happens if  a constructor is
+constructing an object of a class-wide type:
+
+Foo: Bar'Class := Make_Foo(Param1, Param2);
+
+This is actually an overload resolution issue.  If the compiler can
+disambiguate which constructor to call from the parameters, fine.  If
+not the user will have to change the object type to a specific type.  So
+I think we do need the overload resolution rule that the declared return
+type of a constructor must be a specific type. (I could see having a
+constructor with several different return statements that returned
+different specific types, while the declared return type was classwide.
+It might be a fun idea to play with, but I think it should be out of
+scope for this revision.)
+
+I also feel, and I may be alone on this one, that constructors which are
+predefined operations of a type should not be derived as abstract the
+way functions are.  With constructors of tagged types there will be many
+cases where calling the constructor for the parent type on a view
+conversion is the magic you want.  (But if it creates compiler issues, I
+can easily be talked out of it.)
+
+>The fundamental problem is that a constructor is not a function and it
+>certainly isn't a procedure. It is more a custom definition of ":=
+>initialization", and really should have properties appropriate to that.
+
+I agree with Randy.  This is a special thing from both the language
+viewpoint and the user's viewpoint.  Trying to avoid that results in a
+cognitive dissonance that is an invitation to headaches, both for
+language lawers and Ada programmers.
+
+(Randy said:)
+
+>I don't see this as much of an issue. The "constructor" proposal has all of
+>that localized to the return statement. And the cost of doing it there is
+>not really any different than it is now -- I already have to stand on my
+>head to keep the return object from being finalized in the function. Doing
+>the same for tasks is trivial (it's the same chain for us anyway).
+
+Yep.
+
+(Back to Tucker:)
+
+>>If we consider this direction, we *might* want to allow the
+>>obvious generalization of allowing a function to rename
+>>another function *call,* specified using a similar syntax.
+
+I am not sure that I like this new idea, but without renaming of
+function calls, I think it is a non-starter.  And once you look at the
+renaming of function calls case we quickly get back to the constructors
+with special return values.
+
+>The one problem with this seems to be that if you can write an appropriate
+>aggregate to initialize the object, you probably don't need the constructor
+>in the first place. It also means that the constructor is split into two
+>parts arbitrarily: the initial initialization, and the rest of it. That
+>seems ugly.
+
+Worse.  From a user's point of view, the action of the contructor will
+often have to be split into two parts, the initialization of the parent
+part of the object (posibly done as part of an aggregate), then what is
+special to this constructor.  However, the split that Tucker's approach
+creates will only match this cognative division by accident.  (Or by
+very careful planning on the user's part..)  I just don't think we need
+that additional hurdle to using this language feature.
+
+I really keep coming back to the same thought.  The purpose of this
+effort is to make limited types more usable in Ada.  (Some people would
+take the more out of that statement.)  To do that is going to require
+constructors which are pain free from a user's perspective.  If we don't
+have that, we have junk that is not worth implementing.   Tucker's
+approaches may be very clever tricks, but that is how they will be seen
+by users.  It would be fine to implement the actual constructors that
+way, but the user view needs to be simple and straightforward to use.
+
+And as I said (by now yesterday my time), I think that the issue Bob
+Duff identified with:
+
+type T(<>) is limited private;
+
+is crucial.  Why would I declare such a type?   Because I want to
+control the creation of all objects of the type.  But unless I can
+export constructors, the type is pretty useless.  Getting rid of that
+special field, the only one with an initial value, that says this is
+really an uninitialized object, would make me very happy.  But right now
+that is the only way to have limited objects with control of creation.
+(Actually, I have done nastier things with access discriminants, and
+default initial values that call a protected object.  That is one big
+hairy kludge.  But it did insure that each object had a unique,
+sequential ID.)
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Sunday, December 7, 2003  8:27 AM
+
+Randy Brukardt wrote:
+> ...
+> I have to wonder if this approach would work for a type with unknown
+> discriminants (Bob indicated that he wants to do that.) I don't see how it
+> would be able to set them in that case....
+
+This is straightforward:
+
+    type Lim(<>) is limited private;
+    procedure P(Y : Integer; Out_Parm : Lim);
+    function F(X : Integer) return Lim;
+  private
+    type Lim(F1 : Integer) is limited record
+        F2 : Task_Type(F1);
+    end record;
+
+    function F(X : Integer) return Lim
+      renames
+        procedure P(Y => X, Out_Parm => (F1 => X+2, F2 => <>));
+
+Now the user can't declare just "L : Lim;" but rather
+must call a function like F at the declaration point
+("L : Lim := F;").
+
+---------
+
+I understand the concern about the split between the functionality
+specified at the rename, and the functionality buried in the procedure.
+However, if we want the discriminants (and hence the size) known
+to the caller, then you need to be able to write something
+available to the caller (in Dan's idea, it was the function
+result subtype) which is a function of the parameters.
+But Dan's idea doesn't work for a "type Lim(<>) is ..." type
+since the discriminants are not visible.  The renaming approach
+because of the possible separation between the initial
+declaration in the visible part, and the renaming in the
+private part, allows for that.
+
+Furthermore, the separation has other advantages.  A single
+procedure can be used with several different renamings, with
+the renamings differing in the expressions passed for some
+of the IN parameters of the procedure, or the initializing
+expression for the [IN] OUT parameter.  This is similar
+to what is done now with renamings where you can create
+several renamings of the same subprogram, with different
+default expressions.  I would say this approach is
+actually *clearer* than the "trick" of changing the default
+expressions on renaming.  Presuming it is available for
+function-to-function renaming and procedure-to-procedure
+renaming, then it becomes a generally useful capability,
+which can also be extended to handle procedure-to-function
+renaming which is what limited types need.
+
+I will say (again ;-) I am quite concerned about introducing a new
+kind of program unit.  This brings up the issue of library
+constructors, generic constructors, subunit constructors,
+constructor stubs, etc.  Furthermore, from the caller's
+point of view, is there any limitation on where the can
+be called, or is it just like a function, and a call on
+a constructor can be used anywhere a function can be used?
+
+In my view, functions *are* Ada's constructors, and the fact
+that we can name our functions anything we want is a step
+up from C++.  The problem is that you can't write decent
+functions for limited types.  I think "fixing" this by
+inventing a completely new notion of a "constructor" leaves
+the existing use of functions out in the cold.  Does this
+mean that we should go back and change all the functions
+we have created for non-limited types which are often
+used as constructors, and make them "true" constructors?
+
+I believe you are unnecessarily "orphaning" functions.
+
+I would rather "orphan" the existing ability to return
+by reference, which I think is of marginal use.  If we
+wanted to call something a "limited function", I would
+say a function that returns by reference is such a thing.
+
+So perhaps the "right" fix is to say that if you want
+to continue to use return-by-reference, you have to
+label the function a "limited function."  All other
+functions are "constructors" in that they create "new"
+objects.  For limited types, the caller needs to
+know enough to be able to allocate and at least partially
+initialize the object, since this "new" object cannot
+be copied as part of the function call, but must be
+initialized in its final resting place.
+
+Randy indicates he already deals with functions returning
+controlled types, but I think for limited controlled,
+it is a somewhat different problem, because *no* copying
+is permitted (there is no "adjust" procedure).  We have
+ourselves worked to minimize the number of copies involved,
+but for types with discriminants, we end up with at least
+one final copy even in "initializing" contexts.
+To support limited types with discriminants, it seems
+clear that something available to the caller (such as
+a renaming declaration) has to provide an indication
+of the value of the discriminants of the "new" object.
+
+*************************************************************
+
+From: Robert A. Duff
+Sent: Sunday, December 7, 2003  10:26 AM
+
+> > I've heard this vague claim many times.  Could you be more specific?  My
+> > feeling is that the claim is true exactly because of this initialization
+> > problem, and if we solve that, limited types would be very useful
+> > indeed.  Are there *other* issues that you or others think make limited
+> > type useless in Ada?
+>
+> The main problem is that you can't have a component of one in something that
+> you want non-limited.
+
+But that sort of begs the question.  I mean, if I ask, "why can't you
+make type T1 limited?", and you say, "because I want T2 to have a
+component of type T1, and T2 can't be limited," then I'll ask again,
+"OK, why can't you make *T2* limited then?"
+
+My feeling is that the reason you can't make T2 limited is because of
+these initialization issues (including aggregates and constructor
+functions), and if that were solved, then you would be happy to make
+both T2 and T1 limited.  (Or maybe T2 is a component of T3, and so on
+-- but somewhere down the line, there's got to be a real reason, other
+than, "if I make *this* limited then I'd have to make *that* limited.")
+
+>... Of course that has to be the case. But the workaround
+> (use a pointer) flies in the face of my philosophy of never using pointers
+> unless dynamic allocation is needed.
+>
+> The net effect is that either everything in your program has to be limited
+> types, or everything has to be non-limited. Mixed systems don't work very
+> well, because they don't compose very well.
+
+I don't see any problem with that.  Why would you *want* to compose
+them?  I mean, if you have something that's naturally limited (say,
+Window_Handle), then I would want things containing Window_Handles to be
+limited, too.
+
+> I think that's fundamental to limited types. What we can do, however, is
+> make it more possible to make everything limited (which is often the right
+> choice anyway), and the constructors are part of them.
+
+Yeah, that's what I think we should do.  My question was, if we do that,
+will people *still* gripe that limited types "sound good but are useless
+in practise"?  I think not (i.e. fixing the initialization problems is
+sufficient to make limited types useful).
+
+> But please keep in mind that constructors are not just for limited types,
+> and in fact allow things for non-limited types that require doing the
+> operations twice. See my Unbounded_String example, for instance. (BTW, that
+> type is not one that I made up. That's the type definition in Janus/Ada and
+> in GNAT (at least when I last looked) for Unbounded_String.) You can work
+> around double initialization with flags, but that has a distributed
+> overhead: every operation has to check the flags and be prepared for a
+> missing data component. That is a lot harder to get right and hurts
+> performance as well.
+
+I agree that the double-initialization is not nice.  But I don't see a
+better alternative.  (OTOH, as I said, I don't fully understand all the
+details.)
+
+> > >... And I think that virtually all ADTs ought to be controlled.
+> >
+> > Unfortunately, that's not feasible if you care about efficiency, given
+> > the huge overhead most compilers have for controlled types.
+>
+> If Ada compilers have "huge overhead" for controlled types, they're
+> strangling proper use of the language.
+
+I agree.
+
+> For Janus/Ada, it takes roughly 8 instructions (not counting the
+> user-defined code) to finalize an object, and about twice that to initialize
+> it.
+
+Pretty impresive, I think.
+
+Anyway, let's not argue about whether finalization is good or evil or
+fast or slow.  The fact is, there are some programs (the one I'm working
+on right now is an example), that would like to have lots of
+non-controlled limited types, with constructors.  You said:
+
+>...What I find important is that we solve
+> this problem in a way that works for as many users as possible.
+
+and I agree with *that*.  It implies that we should not create a
+solution that works only for controlled types.
+
+*************************************************************
+
+From: Robert I. Eachus
+Sent: Sunday, December 7, 2003  3:48 PM
+
+This is a long post.  It is necessary that someone completely work
+through these proposals to find any potential problems if they are
+adopted.  Not everyone needs to check my work.  (At least the example
+package as included here compiles. ;-)
+
+The short version of this post is that Tucker's old or new approach may
+cause problems that need to be resolved in the area of protected
+objects.  I don't see it as a killer, but it deserves thought.  The new
+constructor subprogram type can be allowed as a part of protected types,
+but I don't see a pressing need.  Note that it is not the object being
+created that needs protecting, it is parameters to the constructors that
+may  raise concurrency issues.  'Allowing' a constructor of a type to be
+part of a protected object to me is not needed even for orthogonality
+reasons.  A constructor never needs a way to protect the object that it
+is creating, and of course a constructor in a protected type would not
+be creating objects of the protected type.
+
+Tucker Taft wrote:
+
+> Randy Brukardt wrote:
+>
+>> ...
+>> I have to wonder if this approach would work for a type with unknown
+>> discriminants (Bob indicated that he wants to do that.) I don't see
+>> how it
+>> would be able to set them in that case....
+>
+>
+> This is straightforward:
+>
+>    type Lim(<>) is limited private;
+>    procedure P(Y : Integer; Out_Parm : Lim);
+>    function F(X : Integer) return Lim;
+>  private
+>    type Lim(F1 : Integer) is limited record
+>        F2 : Task_Type(F1);
+>    end record;
+>
+>    function F(X : Integer) return Lim
+>      renames
+>        procedure P(Y => X, Out_Parm => (F1 => X+2, F2 => <>));
+>
+> Now the user can't declare just "L : Lim;" but rather
+> must call a function like F at the declaration point
+> ("L : Lim := F;").
+
+Um, that is "L: Lim := F(3);" or some such.  There are two limitiations
+with this approach.  First, it is difficult to have really unknown
+discriminants--the discriminant types probably have to be visible so
+that the visible function declaration can have parameters of the type.
+(Not a big deal.)   The other limitation is the one that bothers me.  Go
+back and look at the example I posted of converting a linked list of
+Unbounded_Strings to an array.  There is a workaround to use with this
+approach, have a function which walks the list and counts the entries
+and use a call to that to pass the array size to the function.  Let me
+give a fully worked out (but currently useless) example:
+---------------------------------------------------------------------------------------------------------
+with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
+package Unbounded_String_Utilities is
+   type String_Array(<>) is limited private;
+   type String_List is limited private;
+   function Size (Arr: String_Array) return Natural;
+   function Size (List: String_List) return Natural;
+   function To_String_Array(List: in String_List) return String_Array;
+private
+   type List_Node;
+   type String_List is access List_Node;
+   type String_Array is array(Natural range <>) of Unbounded_String;
+   type List_Node is record
+     Value: Unbounded_String;
+     Next: String_List;
+   end record;
+end Unbounded_String_Utilities;
+
+package body Unbounded_String_Utilities is
+
+   function Size (Arr: String_Array) return Natural is
+   begin return Arr'Length; end Size;
+
+   function Size (List: String_List) return Natural is
+     Count: Natural := 0;
+     Temp: String_List := List;
+   begin
+     while Temp /= null loop
+       Temp := Temp.Next;
+       Count := Count + 1;
+     end loop;
+     return Count;
+   end Size;
+
+   function To_String_Array(List: in String_List) return String_Array is
+     Result: String_Array(1..10);
+     Temp: String_List := List;
+   begin
+     if Temp = null then return Result(1..0); end if;
+     for I in 1..10 loop
+       Result(I) := Temp.Value;
+       if Temp.Next = null then return Result(1..I); end if;
+     end loop;
+     return Result & To_String_Array(Temp);
+   end To_String_Array;
+
+end Unbounded_String_Utilities;
+---------------------------------------------------------------------------------------------------------
+Right now, without the limited keywords, I can use this package.  But
+with String_Array and String_List declared limited, I can't even declare
+an object of type String_Array outside the package private part, body,
+or child packages.  My proposed solution is to declare To_String_Array
+as a constructor, and, if necessary change the return statements.  (But
+it shouldn't be necessary in this case, since type String_Array is not
+limited inside the body of To_String_Array.)
+
+What do I have to do for Tucker's solution?  I have to replace the body
+To_String_Array with a procedure, and a renaming of  a call to the
+procedure:
+
+   procedure To_Array(List: in String_List; Result: out String_Array) is
+     Temp: String_List := List;
+   begin
+     for I in Result'Range loop
+       Result(I) := Temp.Value;
+     end loop;
+   end To_Array;
+
+   Junk: Unbounded_String;
+
+   function To_String_Array(List: in String_List) return String_Array
+renames
+          To_Array(List, (1..Size(List) =>Junk));
+
+Making all those copies of Junk just to throw them away is something the
+compiler might figure out.  But the other problem is the one that
+concerns me.  Specifying the size of the array in the renaming makes the
+procedure somewhat simpler than the function it replaces.  Instead there
+is a call to Size, I probably have to write that function anyway, but
+what if someone appends something to the List, or worse, shortens the
+list while I am working on it?  Ah, I'll just create a protected object,
+and insure that operations in the package have the appropriate locking
+semantics.   Oops!   The problem is not that To_Array is a procedure, it
+is that locking for Size then locking for To_Array is useless.  You have
+to allow this funny renaming to be an operation of a protected type, and
+that operation has to include the evaluation of the parameters of the
+procedure call.  The renamed procedure probably also has to be an
+operation of the protected type so that  internal calls are
+appropriately recognized.  Of course, you could use semaphores and put
+the P and V operations in different subprogram bodies.  But doing that
+first requires that those particular subprograms not be visible to the
+user of the package, and even then is a maintenance nightmare.
+
+What about the new operation approach?   I don't think we need to touch
+protected types at all.  I might want to have a per list lock for the
+list type, but I can do that by making a new list head type which is the
+public type, make it the protected object. (The public type is already
+limited, right? ;-) When creating the array, I can have calls in the
+constructor to lock and unlock the list head.  I don't need to worry
+about protecting the array object during construction, it can't be
+referenced before the declaration has been completely elaborated.  If I
+had the need, I could do the same thing, in the other direction, lock an
+array, copy the data, unlock the array and return.
+
+Note that this does mean that the locking and unlocking operations needs
+to go into the sequence of statements of the return ... do ... end.
+That should be no problem compared to all the cruft necessary to make
+the other approach work with protected objects.
+
+If  everyone thinks that a constructor should be treated like a function
+within a protected type declaration for orthogonality reasons. (Or for
+that matter a procedure.)  It can be done, but I don't see any real need
+for it.
+
+> I will say (again ;-) I am quite concerned about introducing a new
+> kind of program unit.  This brings up the issue of library
+> constructors, generic constructors, subunit constructors,
+> constructor stubs, etc.  Furthermore, from the caller's
+> point of view, is there any limitation on where the can
+> be called, or is it just like a function, and a call on
+> a constructor can be used anywhere a function can be used?
+
+I think that I have the same concerns as Tucker does, but a different
+viewpoint.  There is no real need, outside of orthogonality issues to
+have library constructors, generic constructors, or subunit
+constructors.  A generic package could certainly contain a constructor
+for the ADT it defines, but a constructor as a library unit or generic
+unit makes no real sense to me.   Separate bodies for constructors may
+be a nice to have, but not if it kills the proposal.
+
+As for where a constructor can be called, my worry is that with Tuck's
+proposal, a procedure renamed in private to create a thing that publicly
+looks like a function means that is can be called anywhere a function
+name is legal.  Otherwise we have a major contract violation.  With my
+proposal, my intent is to keep things  as restricted as possible.  If we
+have the return ... do ... end construct as Randy advocates, it
+certainly should be legal to call the constructor for the parent type in
+an aggregate.  You also need to be able to use a constructor to create a
+target of an allocator.  And, of course, you need to be able to call a
+constructor to create the initial value in an object declaration.
+
+What about as an in parameter  in a procedure or other call?   I don't
+see any harm in requiring users to actually  create an object with a
+declaration, then pass the object in a call.  The problem now is that in
+some cases you can't create a useful object to pass.
+
+In fact, this is probably the hardest part of what we are doing here.
+We are not just fixing a problem in the language we are refining what
+the properties of a limited object are.   I actually want to make it
+more difficult to create a limited object outside the facilites provided
+by the ADT.   But that requires  making constructors for limited types
+work.
+
+> In my view, functions *are* Ada's constructors, and the fact
+> that we can name our functions anything we want is a step
+> up from C++.  The problem is that you can't write decent
+> functions for limited types.  I think "fixing" this by
+> inventing a completely new notion of a "constructor" leaves
+> the existing use of functions out in the cold.  Does this
+> mean that we should go back and change all the functions
+> we have created for non-limited types which are often
+> used as constructors, and make them "true" constructors?
+>
+> I believe you are unnecessarily "orphaning" functions.
+
+LOL!  Most current functions, even those that are part of an ADT, do not
+return a value of a private or limited private type.  If you want to
+change To_Unbounded_String, etc., to constructors, I see no problem with
+that.  But most of the functions in Ada.Strings.Unbounded are actually
+operators, and intended to be used as such.  In fact, if you think about
+it there are several 'special' types of functions in Ada already, such
+as character literals, string literals, operators, and attributes.
+
+If it helps you to think in terms of "constructor function Foo return
+Bar is...", fine.  Or perhaps "function Foo return in_place Bar;" is
+even better.  I am trying to stay away from "limited function Foo is..."
+because I feel that constructors will be used for both limited and
+non-limited ADTs.  I also feel that avoiding the word constructor is
+wrong.  If it walks like a duck, and quacks like a duck, we don't do
+anyone any favors by saying that in Ada we call that a moose.
+
+> I would rather "orphan" the existing ability to return
+> by reference, which I think is of marginal use.  If we
+> wanted to call something a "limited function", I would
+> say a function that returns by reference is such a thing.
+
+I definitely disagree.  It may be that the compiler you are familiar
+with seldom returns values by reference, but at least in my code it is
+pretty common.
+
+> To support limited types with discriminants, it seems
+> clear that something available to the caller (such as
+> a renaming declaration) has to provide an indication
+> of the value of the discriminants of the "new" object.
+
+ It may be that this is the real crux of the matter.  I don't want the
+discriminants made available to the caller. Tucker is probably using
+this as shorthand for "made available to the compiler in the context
+from which the call is made."   But there are many useful programming
+idioms where it is the constructor that determines the properties of the
+object created.  Most Ada compilers currently handle this (in the
+unconstrained function return value case) very well, and very
+efficiently.  What is different in the limited case is that the return
+value has to be created in place.  I see this as a dialog between the
+calling environment and the constructor which takes place in the return
+statement.  Randy apparantly sees this as well,  and it fits very nicely
+with the "return ... do ... end" construct.  The constructor does
+whatever it must then reaches the return statement.  There it either
+calls a thunk or whatever and gets the space it needs for to create the
+returned object.  In my mental model, it is fine for the limited case to
+allocate space on the heap and the caller will deallocate the object
+when the scope is left.  (Finalization, right?)  It is also possible to
+have a separate call stack for functions so that return values can be
+built directly in the calling procedure's context.  (Works very well in
+practice because of the way that functions are used in Ada.)
+
+But these compiler efficiency issues as far as I am concerned are low on
+the priority list when discussing this issue.   I would have to put my
+weightings at 40% usability, 30% ease of understanding/teaching, 20%
+compiler implementation difficulty, and 10% efficiency issues.  I
+thought about making a table of issues and comparing proposals  but this
+message is long enough as it is.  Maybe someone else will give that a
+try, possibly someone not associated with any current position. ;-)
+
+Wish I could join you at the meeting, but right now even Route 128 is
+outside my travel radius.
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Sunday, December 7, 2003  4:27 PM
+
+> ... In my mental model, it is fine for the limited case to
+> allocate space on the heap and the caller will deallocate the object
+> when the scope is left.   ...
+
+I don't see how that works for a component.  Only the caller
+knows exactly where the object is to be allocated.  Trying
+to communicate that to the called routine is not trivial.
+
+*************************************************************
+
+From: Robert A. Duff
+Sent: Sunday, December 7, 2003  9:35 PM
+
+Randy wrote:
+
+> Bob Duff wrote:
+>
+> > Constructors ought to be composable.  That is, clients should be able to
+> > write constructors, given primitive constructors.  For example, if a
+> > "Sequence" package gives the client a way to construct a singleton
+> > sequence, given one element, and a way to concatenate Sequences, the
+> > client ought to be able to write a constructor that takes two Elements
+> > and produces a sequence of length 2.  This is common in non-limited
+> > cases.  Why not in limited?
+>
+> I'm not completely sure what you mean. Certainly, the proposal I've put
+> forward allows setting components with a constructor (usually in an
+> aggregate). Or did you mean something else?
+
+I think one of your (or somebody's) e-mails suggested a legality rule
+along the lines of "a constructor must be a primitive operation of the
+constructed type".  What I meant was, I don't like that restriction,
+because no such restriction exists for nonlimited types, and what we're
+tring to accomplish is to initialize limited objects just like
+nonlimited ones (or as close to that as we can manage).
+
+> > This implies that constructors cannot be required to be primitive ops.
+> > And therefore that such constructors cannot see improperly initialized
+> > objects.
+>
+> I think it is important that constructors don't see *any* objects. That is,
+> constructors are creating an object -- in no sense should an object be
+> "passed in" to it. Implementations might implement them that way, but the
+> semantics certainly should not be that way.
+
+I'm not sure what you mean by that.  I was under the impression that all
+of the proposals on the table had a named object inside the function
+that acts as the function result (or constructor result or whatever),
+and that this object *is* the object at the call site that is being
+created.  Are you saying that it should be illegal to read from this
+object (as in Ada 83 'out' parameters)?
+
+I could be confused -- I admit I've lost track of the details in a
+the morrass of e-mail.
+
+*************************************************************
+
+From: Randy Brukardt
+Sent: Sunday, December 7, 2003 11:46 PM
+
+Bob said, replying to me replying to him: (See I can remember to tag
+these... :-)
+> > > This implies that constructors cannot be required to be primitive ops.
+> > > And therefore that such constructors cannot see improperly initialized
+> > > objects.
+> >
+> > I think it is important that constructors don't see *any* objects. That
+is,
+> > constructors are creating an object -- in no sense should an object be
+> > "passed in" to it. Implementations might implement them that way, but
+the
+> > semantics certainly should not be that way.
+>
+> I'm not sure what you mean by that.  I was under the impression that all
+> of the proposals on the table had a named object inside the function
+> that acts as the function result (or constructor result or whatever),
+> and that this object *is* the object at the call site that is being
+> created.  Are you saying that it should be illegal to read from this
+> object (as in Ada 83 'out' parameters)?
+>
+> I could be confused -- I admit I've lost track of the details in a
+> the morrass of e-mail.
+
+No, I was simply saying that there shouldn't be any name for the object
+being constructed until the constructor is actually ready to create it. That
+is, in the proposal I sketched out, not until the return statement is
+encountered. At that point, the object will be initialized -- but the
+constructor can control what initialization is done (with an aggregate or
+another constructor). In particular, the caller cannot and should not be
+trying to allocate anything; it has to tell the constructor how to do that
+(most likely on a secondary stack).
+
+Tucker said:
+
+> In my view, functions *are* Ada's constructors, and the fact
+> that we can name our functions anything we want is a step
+> up from C++.  The problem is that you can't write decent
+> functions for limited types.  I think "fixing" this by
+> inventing a completely new notion of a "constructor" leaves
+> the existing use of functions out in the cold.  Does this
+> mean that we should go back and change all the functions
+> we have created for non-limited types which are often
+> used as constructors, and make them "true" constructors?
+
+The few functions which are constructors (like To_Unbounded_String) should
+indeed be changed. But most things aren't constructors in the normal sense.
+
+I see a function as returning a copy or reference of an existing object. A
+constructor creates a new object. For limited types, that's in-place
+construction, and that should be true for limited types as well.
+
+In any case, if we had the will to change *all* functions this way, that
+would be fine. But that's not going to happen - it would break too many
+programs. So we have to introduce a new concept. That's the penalty for
+getting it wrong the last time.
+
+(Now, if constructors allowed "in out" parameters, then we could solve
+another problem as well. And then if functions ended up orphaned, good
+riddance. But I doubt that we have the will to do that.)
+
+*************************************************************
+
+From: Robert I. Eachus
+Sent: Sunday, December 7, 2003 11:49 PM
+
+I am more and more convinced that this will appear to most users as the
+single largest change in Ada 0Y.  I don't think the compiler work is
+going to be all that hard--after all C++ has constructors. ;-)  But  I
+think we do need to devote time to getting the rules right.  If we do
+almost all Ada programs will use this feature.   (I certainly know that
+I am spending much too much time on this topic, if it is not as
+important as I think it will be.)  -- RIE
+
+Tucker Taft wrote:
+
+>>... In my mental model, it is fine for the limited case to
+>>allocate space on the heap and the caller will deallocate the object
+>>when the scope is left.   ...
+>>
+>>
+>
+>I don't see how that works for a component.  Only the caller
+>knows exactly where the object is to be allocated.  Trying
+>to communicate that to the called routine is not trivial.
+>
+>
+I understand what you are saying, I think.  This is why having a
+separate function call stack is such a useful optimization.  If a
+procedure (or for that matter a function) wants to build an object "in
+place" then, if you can use another stack to contain the function's
+return context and any local objects, then the return value just gets
+built on top of the heap, or, if necessary in the heap.  If you can't do
+this you end up doing things like  copying the return value lower down
+the stack after the function returns.  I 've seen several variations on
+this approach.  I think  the ALS used a ping-ponging scheme, and other
+compilers  have a separate stack for function calls.  But given the way
+Ada is used, even dynamic calling depths are low, and it is possible to
+have a stack for each nesting level most of the time.
+
+Impilict heap use works, and is clearly a workable solution for most of
+the cases we have been discussing.  The only tricky part is when a
+component of a record is being created is "big" and the size is
+determined by the called function.  This is where Alsys used to go into
+all that "mutant record" stuff.  (I don't know if they still do.)  But
+this is just the nature of Ada record structures.  Most of the time it
+is possible to allocate a record in one contiguous memory space with no
+embedded pointers.  But for some of the extreme cases it is better to
+use embedded pointers.  For example, if you have several components
+whose size depends on the discriminants, you can pass thunks to
+calculate the offets the way DEC Ada did.  It is just faster (but less
+memory efficient) to have offsets or  pointers as part of the structure.
+
+But as I said in my previous message, I would rather have some
+inefficiency in the memory management in complex cases if that makes it
+easier for programmers to use limited types for the normal cases.  And
+as I see it, there are two 'normal' cases here. In the first case,  from
+the point of view of the constructor the object will have a constant
+size header, which will come from the parent (or collective ancestor)
+types, and a fixed size extension.  In the second case, the type will be
+a container type with discriminants, and a size that is computed by the
+constructor.  But the object will not normally be a component.
+
+I guess if  you really feel strongly about it based on your compiler
+implementation, it would be possible to restrict constructors to
+creating objects not components.  I just don't see it as a problem.  Of
+course, I did advocate (intentionally) that constructors need to be
+allowed in aggregates, but omitted default values in records.  You could
+add a rule that a constructor is illegal as a default value in a record
+definition, but again I don't see why.  The compiler will be creating an
+entire object containing the component, just as in the aggregate case.
+
+But I do see a big mess if a constructor can be called to change the
+contents of a limited record object.  As I said before, for me--and I
+suspect everyone--that is no way, no how what we want. Getting the
+limited case right is more important than covering the non-limited ADT
+case.  However, I don't see a problem with writing the rule so
+constructors for non-limited objects can be used to change components of
+records.  (As far as I am concerned, I would expect the compiler to use
+the constructor to create a--remember non-limited--object, then copy it
+into the actual record.  An "extra" copy in some cases, but I don't see
+it as a big deal if it is inefficient to use constructors as other than
+constructors.)
+
+*************************************************************
+
+From: Robert I. Eachus
+Sent: Monday, December 8, 2003 12:03 PM
+
+Randy Brukardt wrote:
+
+>No, I was simply saying that there shouldn't be any name for the object
+>being constructed until the constructor is actually ready to create it. That
+>is, in the proposal I sketched out, not until the return statement is
+>encountered. At that point, the object will be initialized -- but the
+>constructor can control what initialization is done (with an aggregate or
+>another constructor). In particular, the caller cannot and should not be
+>trying to allocate anything; it has to tell the constructor how to do that
+>(most likely on a secondary stack).
+>
+>
+I think Randy and I are in agreement here, even if we don't agree  on
+which is the "secondary" stack. ;-)
+
+>In any case, if we had the will to change *all* functions this way, that
+>would be fine. But that's not going to happen - it would break too many
+>programs. So we have to introduce a new concept. That's the penalty for
+>getting it wrong the last time.
+>
+>
+I don't know that we got it "wrong" last time.  I think we just didn't
+think through the need at all.  Functions returning results by reference
+is a neat trick in other circumstances, but it can't handle this
+particular problem/need/programming model.
+
+>(Now, if constructors allowed "in out" parameters, then we could solve
+>another problem as well. And then if functions ended up orphaned, good
+>riddance. But I doubt that we have the will to do that.)
+>
+The will, or the stomach?  I would rather have functions with in out
+parameters than constructors.  But the Rosen trick will work for both if
+needed, and I hope the need can be restricted to random number generator
+seeds. ;-)
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Monday, December 8, 2003  6:13 AM
+
+Robert Eachus wrote:
+>
+> Tucker Taft wrote:
+>
+> >>... In my mental model, it is fine for the limited case to
+> >>allocate space on the heap and the caller will deallocate the object
+> >>when the scope is left.   ...
+> >>
+> >>
+> >
+> >I don't see how that works for a component.  Only the caller
+> >knows exactly where the object is to be allocated.  Trying
+> >to communicate that to the called routine is not trivial.
+> >
+> >
+> I understand what you are saying, I think.  ...
+
+I'm not sure you do.  You certainly cannot assume that limited components
+will be allocated on the heap.  Yes, I suppose some compiler's,
+like RRs, allocate all nested composite objects with a level
+of indirection, but that is the exception, not the rule.  Almost
+all the other compilers go out of their way to keep records
+contiguous, whether they are limited or non-limited.
+I presume you will be able to use a call on one of these
+constructor functions to initialize a limited component of a
+limited aggregate.  If not, you haven't solved the problem
+in my view.  That is:
+
+    X : Lim2 := Lim2'(F1 => 7, F2 => Lim1_Con_Func(3,4));
+
+where Lim1_Con_Func is one of these constructor functions.
+
+> ...
+> But as I said in my previous message, I would rather have some
+> inefficiency in the memory management in complex cases if that makes it
+> easier for programmers to use limited types for the normal cases.
+
+I really don't see any compiler changing its record layout just to make
+these possible.  That would be hugely disruptive.
+
+> ...
+> I guess if  you really feel strongly about it based on your compiler
+> implementation, it would be possible to restrict constructors to
+> creating objects not components.
+
+That is a non-starter.
+
+> ...
+> But I do see a big mess if a constructor can be called to change the
+> contents of a limited record object.  ...
+
+Noone is proposing that.  Except for the oddball return-by-reference
+functions, all function calls are associated with the creation of a
+new object.  The only issue is how this is *implemented.*
+From the view of the user of the function, there is always a new object
+produced.  The procedure-renamed-as-function proposal is suggesting
+that the compiler does the allocation and basic initialization
+inline before going out-of-line to the procedure.  In the
+"return ... do ..." proposal, the allocation still needs to
+be done prior to the call.  It could be raw, uninitialized storage,
+but the space needs to exist before the call, if you are going
+to allow components to be initialized by such a call.
+
+To do the allocation, the compiler needs to know the size
+of the result, meaning in general it needs to know the
+discrminants if the result type is discriminated.  If the
+caller knows the discriminants, then we need to be sure the
+out-of-line code uses the same values for the discriminants.
+This gets tricky if there is no name that refers to the
+returned object until the out-of-line code declares one
+(e.g. via the "return Result: T := <expr> do ..." construct), since they
+can't refer to the values of the discriminants in the
+initializing expression for the return object.
+
+I suppose one way out of this conundrum is to allow references
+to discriminants of "Result" in the "<expr>".  That is not the
+way normal declarations work, but it could work here.
+
+The other problem, as others have indicated, is that sometimes
+we want the discriminants of the new object to be a function
+of the parameters.  That seems harder in the
+"return ... do ..." approach, since we would have to somehow
+communicate the values to the calling context as well, since
+they are needed *before* the object can be allocated,
+and that needs to happen before the call (because of components).
+
+> ... However, I don't see a problem with writing the rule so
+> constructors for non-limited objects can be used to change components of
+> records.
+
+I don't know what you are talking about when you say
+"change" a component.  Do you mean that for a non-limited
+type, you would allow a call on a constructor function to be
+the right hand side of an assignment, whose left-hand-side
+is a component selection (or anything else, for that matter)?
+Or do you mean the function would somehow be passed a reference
+to a preexisting object, and treat it as an IN OUT parameter?
+I don't understand what a call would look like in this latter
+case, and it is not something I have any interest in.
+
+In the procedure-renamed-as-function proposal, it is true
+the *procedure* is passed an [IN] OUT parameter, but the
+procedure is *not* the "constructor."  The constructor
+is the function defined by the renaming.  The procedure
+is just a useful hunk of code that the constructor
+function reuses.  That same procedure might be used
+for other things.  The function defined by the renaming,
+on the other hand, has the properties of a "normal" function,
+namely that it is always associated with the creation of
+a new object.  It is different in that there is enough
+information provided so that at the call-site, the compiler
+can generate the code to do allocation and basic initialization,
+and the out-of-line code need not worry about that.
+
+*************************************************************
+
+From: Pascal Leroy
+Sent: Monday, December 8, 2003  7:22 AM
+
+Randy wrote:
+
+> Let's look specifically at the "complexity" of this counter
+proposal...
+
+If we are going to add constructors to the language (and that's a big
+if, given that we are already pretty late in the game) then I strongly
+favor the Brukardt-Eachus proposal over the other ideas that have been
+floated in this thread.
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Monday, December 8, 2003  9:52 AM
+
+It would be helpful if you could augment this with some rationale,
+to help understand your view of the strength and weaknesses of the proposals.
+
+*************************************************************
+
+From: Robert I. Eachus
+Sent: Monday, December 8, 2003 11:44 AM
+
+Tucker Taft wrote:
+
+>Robert Eachus wrote:
+>
+>
+>>I understand what you are saying, I think.  ...
+>>
+>>
+>
+>I'm not sure you do.  You certainly cannot assume that limited components
+>will be allocated on the heap.  Yes, I suppose some compiler's,
+>like RRs, allocate all nested composite objects with a level
+>of indirection, but that is the exception, not the rule.  Almost
+>all the other compilers go out of their way to keep records
+>contiguous, whether they are limited or non-limited.
+>
+>
+I was not saying that a compiler should allocate all limited objects
+built by constructors on the heap.  I was saying that it is one of the
+cases that has to work:
+
+Something:  Foo_Access := new Foo'(Constructor);
+
+You are arguing for a solution where the caller allocates the space on
+the heap, and passes the address to the constructor.  Randy and I are
+heading more towards a dialog between the constructor and the caller, or
+a thunk based approach.  The space on the heap would normally be
+allocated in the return statement.
+
+If the constructor returns an object which is constrained, there is no
+problem with components, the caller can know the size of the object
+before the call.  The problem with components is exactly the case which
+Alsys called mutant records.  This is where allocating the maximum size
+of the object doesn't work.
+
+In Ada 83, many compilers used the "allocate the maximum" approach, and
+some still do.  I was saying/accepting that for some objects,
+Unbounded_String components are an excellent example, the normal case is
+going to require a level of indirection.  In both Randy's compiler and
+GNAT, that is the way Unbounded_String is declared.
+
+Note that in cases where the compiler knows a reasonable maximum at
+compile time, most compilers will, as you say, go out of their way to
+allocate the record contiguously.  A good example is the Bounded_String
+case.  For a Bounded_String I expect a compiler to allocate the maximum.
+
+Where I think our mental pictures differ is that you are thinking that
+since a limited object cannot change in size, this should never be a
+problem.   But I want constructors to be able to handle the case where
+the size of the object to be created is determined in the constructor.
+That was what the To_String_Array example was about.  Ada currently
+allows me to handle this case for non-limited objects, it should
+actually be easier for limited objects, but right now it is illegal.
+
+So my mental picture of how this works, is that there is a dialog
+between caller and constructor, probably mediated by a thunk.  When the
+constructor gets to the return statement, it says "the object I am about
+to create will be N bytes long," and the thunk responds with an address
+of the memory space to put it in.  Constructors need this hidden
+data/extra parameter, which is why I favor an explicit new subprogram
+declaration form.  What exactly it looks like is almost irrelevant to
+the implementation issues, but as a user, I think it is much nicer to
+name a constructor as a constructor.
+
+Note that the hard case is when an object has several components
+initialized by constructors, and the size of the object depends on many
+of them.  A heroic compiler could create thunks that are co-routines,
+and effectively call all the thunks in an aggregate in parallel.  I am
+not advocating that a compiler support this, which is why I pointed out
+the existance of the indirection bailout.  I would expect compilers to
+handle aggregates with one size indeterminate component, and I would
+expect aggregates that have more than one such component to be rare.
+However, given the choice of between a rule that only allows one
+constructor in an aggregate, and what Robert Dewar would call "junk
+code" when an object has more than one component which has an unbounded
+size, I'm willing to allow the junk code in the difficult case so that
+the much more common case of  types with one unbounded size component
+will be handled efficiently.
+
+Incidently, this is what I currently get in existing compilers with
+components of type Unbounded_String.  The advantage that many of us
+expect from these AI's is not to eliminate the hidden indirection in
+Unbounded_Strings, it is to eliminate the junk default initialization
+that occurs in many cases.  (If you want to read for a limited type with
+some components of type Unbounded String in the above feel free.  That
+is exactly the normal mapping for database objects that gets painful.)
+
+>I presume you will be able to use a call on one of these
+>constructor functions to initialize a limited component of a
+>limited aggregate.  If not, you haven't solved the problem
+>in my view.  That is:
+>
+>    X : Lim2 := Lim2'(F1 => 7, F2 => Lim1_Con_Func(3,4));
+>
+>where Lim1_Con_Func is one of these constructor functions.
+>
+>
+Definitely.  Although I expect the more common/necessary case to be:
+
+           X: Lim2 := Lim2'(Lim1_Con_Func(3,4) with F1 => 7);
+
+>I really don't see any compiler changing its record layout just to make
+>these possible.  That would be hugely disruptive.
+>
+>
+I don't see that either.  But I do see the compiler using exactly the
+same record layouts for limited objects as non-limited objects with
+otherwise identical declarations.  This is necessary anyway, since the
+limited type may be non-limited in part of its scope.
+
+>>From the view of the user of the function, there is always a new object
+>produced.  The procedure-renamed-as-function proposal is suggesting
+>that the compiler does the allocation and basic initialization
+>inline before going out-of-line to the procedure.
+>
+I understand that very clearly.  I think what you are missing is that it
+is what I object to about the proposal.  I showed in my complete worked
+out example the differences, and how the workarounds end up with a
+constructor split notationally into two pieces.  Tolerable if there are
+no locking constructs involved, but very painful when there are.
+
+>                                                       In the
+>"return ... do ..." proposal, the allocation still needs to
+>be done prior to the call.  It could be raw, uninitialized storage,
+>but the space needs to exist before the call, if you are going
+>to allow components to be initialized by such a call.
+>
+No, what Randy and I are proposing is that the allocation occurs at the
+point of the object creation in the return statement (possibly a "return
+... do ... end;" construct,  This requires either a thunk, or for the
+compiler to pass the address where the object is to be placed as a
+hidden parameter, and the size of the object to be an "extra" return
+parameter.  (I say "extra" because there is no need for the actual
+return value to be returned, the caller knows where it is.)  So in the
+common case where the size is known at the point of the call, no thunk
+is required, and no return value either.
+
+So back to my initial three cases:
+
+Easy cases (all objects the same size):  pass address, no thunk needed.
+Constructor initializing a constrained object:  pass address with
+constraints in place, no thunk needed.
+Constructor computes constraints:  pass thunk which can be called with
+size to get address.
+
+I personally think that given that all three cases are possibly, most
+uses will be cases one or three.  Except that it is nice to be able to
+handle cases two and three with the same constructor code:
+
+Handle all cases:  pass a constrained flag, and a thunk.
+
+The user code then needs to be able to query this flag,  my suggestion
+is that the 'Constrained attribute can be queried in the sequence of
+statements in the do..end, and if true, then the code can look in the
+memory returned by the thunk to see the discriminants.
+
+Once you realize that this model works, it becomes clear that the right
+choice from a user's point of view is to choose to permit the most
+complex case, and allow compilers to recognize the other cases and
+create more efficient code.  But the efficiency is in terms of the
+number of parameters passed.  So when I say I am willing to tolerate
+inefficiency to get full generality, the inefficiency I am talking about
+is sometimes passing one or two "extra" parameters which the compiler
+wasn't able to recognize as unnecessary.
+
+>To do the allocation, the compiler needs to know the size
+>of the result, meaning in general it needs to know the
+>discrminants if the result type is discriminated.  If the
+>caller knows the discriminants, then we need to be sure the
+>out-of-line code uses the same values for the discriminants.
+>This gets tricky if there is no name that refers to the
+>returned object until the out-of-line code declares one
+>(e.g. via the "return Result: T := <expr> do ..." construct), since they
+>can't refer to the values of the discriminants in the
+>initializing expression for the return object.
+>
+>I suppose one way out of this conundrum is to allow references
+>to discriminants of "Result" in the "<expr>".  That is not the
+>way normal declarations work, but it could work here.
+>
+>
+Actually now that we are down to "bit fiddling", better IMHO is to allow
+assignment to T in the sequence of statements following the do.  Then
+the combined case above can be handled elegantly as:
+
+        ...
+        return Result: Object_Type
+        do
+            if Result'Constrained
+            then
+                Result :=  (Disc1 => Result.Disc1, Disc2 =>
+Result.Disc2,...);
+            else
+                Result :=  Default_Constructor(Param1, Param2);
+            end if;
+         end;
+
+Of course, when the Object_Type is only limited by fiat, this is
+possible for constructors declared in the same package as Object_Type
+(or in a child package).  The case where the parent type is limited
+should be covered by the aggregate rules so that constructors for types
+derived from Limited_Controlled can be written this way.
+
+>The other problem, as others have indicated, is that sometimes
+>we want the discriminants of the new object to be a function
+>of the parameters.  That seems harder in the
+>"return ... do ..." approach, since we would have to somehow
+>communicate the values to the calling context as well, since
+>they are needed *before* the object can be allocated,
+>and that needs to happen before the call (because of components).
+>
+>
+I know that it how you (Tucker) are thinking, but I am going further and
+allowing the discriminants to be something that doesn't depend on the
+parameters in an obvious way.  The classic case would be a data entry
+system.  The constructor may return one of many variants of a record
+based on the data entered by the user's interaction with the
+constructor.  I am not oblivious to the cost of getting all this
+"right,"  which is why I have been indicating a willingness to subset
+the functionality for now.
+
+The problem I have with the renaming approach is that it is not obvious,
+perhaps not possible, to extend it later to cover all the useful cases.
+Originally I was proposing to cover a different subset of the useful
+cases than the renaming approach, but Randy and I have converged on a
+solution (actually with some help from Dan Eilers) that covers all cases.
+
+>>... However, I don't see a problem with writing the rule so
+>>constructors for non-limited objects can be used to change components of
+>>records.
+>>
+>>
+>
+>I don't know what you are talking about when you say
+>"change" a component.  Do you mean that for a non-limited
+>type, you would allow a call on a constructor function to be
+>the right hand side of an assignment, whose left-hand-side
+>is a component selection (or anything else, for that matter)?
+>
+Yes.
+
+>Or do you mean the function would somehow be passed a reference
+>to a preexisting object, and treat it as an IN OUT parameter?
+>I don't understand what a call would look like in this latter
+>case, and it is not something I have any interest in.
+>
+>
+Ah, but it is something that you do have an interest in. ;-)  This is
+the exactly the case that is described by your renamed procedure
+approach.   As explained above, the constructor approach handles that
+case as well.  To handle all cases when the caller doesn't know whether
+the particular constructor called will expect existing discriminants or
+not requires passing a constrained flag and a thunk.  And of course,
+this will generally be the case in Ada, since the body of the package
+containing the constructor will not be visible at the point of the
+call.   However, the compiler can choose a more optimal calling sequence
+when it knows that the object passed has no discriminants.
+
+>In the procedure-renamed-as-function proposal, it is true
+>the *procedure* is passed an [IN] OUT parameter, but the
+>procedure is *not* the "constructor."  The constructor
+>is the function defined by the renaming.  The procedure
+>is just a useful hunk of code that the constructor
+>function reuses.  That same procedure might be used
+>for other things.  The function defined by the renaming,
+>on the other hand, has the properties of a "normal" function,
+>namely that it is always associated with the creation of
+>a new object.  It is different in that there is enough
+>information provided so that at the call-site, the compiler
+>can generate the code to do allocation and basic initialization,
+>and the out-of-line code need not worry about that.
+>
+>
+ Understood.  Incidently please don't think of what is going on here as
+just an argument, or a bunch of people ganging up on Tucker.  What is
+really going on is that there are two proposals on the table, and they
+have different properties.  I am willing to accept the extra parameters
+that the constructor approach requires in some cases to get the
+additional functionality it allows.  Tucker's approach is not as
+general, but it is somewhat more efficient.  I would argue that the
+inefficiency of my approach is not inherent, but depends on the (often
+private) type declaration.  There will be cases where Tuckers approach
+is clearly more efficient, but not catastrophically so.
+
+So neither solution dominates the other.  The choice becomes a normative
+one, depending on how you weight different considerations.  And as I
+said earlier, the I think the time spent on examining both alternatives
+is worth it, since I expect the result to be one of the most heavily
+used features of Ada 0Y.
+
+*************************************************************
+
+[Editor's note: Additional discussion on this topic can be found in AI-318.]
+
+*************************************************************
+

Questions? Ask the ACAA Technical Agent