CVS difference for ais/ai-00325.txt

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

--- ais/ai-00325.txt	2003/10/29 22:54:13	1.2
+++ ais/ai-00325.txt	2003/12/07 04:54:13	1.3
@@ -163,7 +163,2277 @@
 the syntax for "access_definition" is generalized (see ai-231) to allow
 control over nullness, and constantness of the designated object.
 
-[See AI body for current proposal - Ed.]
+[This is version /01 of the AI - Ed.]
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Saturday, November 15, 2003  10:35 PM
+
+I am at least partly responsible for a couple of AIs (AI-318 and
+AI-325) that suggest supporting functions returning (new) limited objects,
+and functions returning anonymous access types.
+
+These two are pretty closely related, can be used to solve
+similar problems, and have some of the same issues.
+
+So this is a bit of a personal brain storming on this topic,
+which will be turned into AI discussion depending on the feedback
+received from other ARG members (so speak up!).
+
+First let me acknowledge some of the comments already received:
+
+1) Robert Dewar made the point that the function-returning-new-limited-object
+proposal (AI-318), if there is nothing on the function declaration indicating
+it is one of these, would require a new run-time model for calling
+*all* functions returning limited types, including those that were
+allowed by Ada 95 (return-by-reference of *existing* limited objects).
+
+This kind of argument is not very convincing if it just applies to
+one particular implementation approach, but it seems that this
+will be true pretty much for *all* implementation approaches.
+This implies that all existing code will need to be recompiled if
+a compiler were to start supporting this new kind of function,
+and existing code would almost certainly slow down.
+
+Hence, I accept Robert's point as establishing an additional relatively
+important criteria for evaluating these proposals, namely they should
+not require a significant run-time model change for *existing* code.
+
+2) Dan Eilers suggested that we consider allowing a function to be declared
+as a renaming of a procedure with a single [in-]out parameter.
+This is an interesting idea, but it has some tricky implications.
+First, I would suggest some alternative syntax for the renaming
+to make it clear what is happening, since otherwise the overload
+resolution algorithm would have no clue to look at procedures
+when working on a function renaming.  Hence, I might suggest:
+
+    function Ret_Limited(X : Integer) return Lim_Type
+      renames procedure Init_Limited;
+
+presuming a directly visible declaration of:
+
+    procedure Init_Limited(X : Integer; LT : [in] out Lim_Type);
+
+The presence of the keyword "procedure" would cause overload
+resolution to consider procedures rather than functions, and
+match any procedure with one additional parameter of type Lim_Type,
+be it first, last, or somewhere in the middle.
+
+A second issue here is that the "call" on Ret_Limited would
+have to be transformed into a call on Init_Limited.  This would
+require that the target object of the function call be identified,
+and that it be already created and initialized (presumably by default),
+since we don't want the code of Init_Limited to be manipulating an
+uninitialized object.  This implies that if the result subtype of the
+function is indefinite, the context of the call has to provide
+discriminants or bounds so the target object can be created and
+default initialized.  This means that calling such a function would
+be analogous to using an aggregate with "others," the context
+would need to provide the constraint on the result.
+
+This would also imply that whether a given context was allowed
+for calling such a function would depend on whether it was a renaming
+of a procedure or not, meaning that this renaming could not be
+in the private part acting as completion for some visible function
+declaration.  It would have to be visible to the callers.
+This also implies some generic contract model issues that might
+be quite nasty.
+
+Finally, if the function result subtype is "definite" but not the same as the
+corresponding procedure parameter subtype, one would have to
+decide which one would be relevant; normally the subtypes used in
+the profile of a subprogram renaming are irrelevant.  Here it might
+be nice if the procedure parameter subtype were unconstrained,
+the result subtype of the function could be used to specify the
+constraints for the target object, but if the target object had
+its own constraints specified, would they have to agree with that
+of the function result subtype?
+
+3) Steve Baird made the point that if for either the limited
+or the anonymous-access-type function we need to pass in a
+parameter representing a "storage pool" or equivalent, this
+would be the first situation where the storage pool is not
+known statically at the point of allocation.  Of course the
+shared-generics folks could scoff at this kind of belly-aching ;-),
+but it does seem to be a relevant consideration.
+
+4) For these kinds of functions, it is valuable to be able
+to have a name for the object that is going to be returned
+from the function.  We have proposed various ways to do so.
+One proposal involved adding a "do...end" clause to a return
+statement.  A second proposal involved declaring a variable
+using the keyword "return" analagous to how "constant" is used.
+The return statement must return this object if one is in scope.
+
+Pascal has indicated a preference for the do...end approach, because
+it links the special semantics more closely to the return
+statement, rather than having to check every return statement
+to see whether it happens to return the value of a specially
+marked variable.
+
+-------------------
+
+All of this discussion to some extent begs the question of
+what problem are we really trying to solve.
+
+Here are various problems that we might or might not
+be hoping to solve with these "funky" functions:
+
+  - Limited types are hard to use.  Allowing aggregates
+    of a limited type can provide some "completeness" checks,
+    but doesn't help cases where the limited type is private.
+    Allowing functions to specify the initial value of a limited
+    object could make limited types safer, by ensuring all the
+    needed initialization took place at the point of declaration,
+    and generally make them more like non-limited types in the
+    paradigms of use.
+
+  - Anonymous access types help prevent proliferation of named access
+    types (particularly relevant in the context of the "limited with"
+    proposal).  However, having anonymous access types only as parameters
+    doesn't address the whole problem, since declaring a function
+    returning an access value will necessitate declaring a named access type.
+
+  - It is sometimes desirable to create a single constructor operation that
+    can be used to create an object either in the heap or as a local
+    variable.  As things are now, you generally have to choose between
+    producing a procedure for initializing a large or limited object,
+    a function for returning a pointer to a heap object, or a function
+    for returning a constructed value of a smaller, non-limited object.
+    It would be preferable to have to single paradigm for creating
+    a "constructor" operation that would work for small and large
+    objects, for limited and non-limited types, and for stack-resident,
+    heap-resident, and component objects.
+
+  - It is not straightforward to write a function that allocates in a
+    caller-determined storage pool.   Generally it requires
+    declaring a new access type, associating the storage pool
+    with that access type, doing the allocator, converting
+    the result to some second named access type, and then having
+    the caller convert yet again to the ultimate desired access type.
+    Alternatively, each caller could instantiate a generic function
+    and then call it.
+
+With regard to returning a limited object without copying it, there are
+various possibilities:
+
+  - The *caller* might want to determine the constraints of the created
+    object, in which case there needs to be some way for the constraints
+    to be provided to the called routine.  In this case, it is probably
+    natural for the caller to preallocate the space for the result,
+    even if it doesn't initialize it in any way.  (This case maps
+    fairly well to a procedure renamed as a function, except that
+    the renaming approach would require the object to be default initialized
+    before whatever further initialization is performed out-of-line.)
+
+  - Alternatively, the *called* routine might determine the constraints,
+    perhaps based on parameters passed in, in which case the best the caller
+    could do is provide some kind of storage pool or area in which the
+    called routine does the allocation, and then it would have to effectively
+    rename the result whose address would be returned.  For "stack" objects,
+    this would be similar to functions returning objects of unknown size,
+    except that no copying of the result would be permitted.  The caller
+    would have to use it where the called routine allocated it, which
+    might imply leaving holes in the (secondary) stack.  (This approach
+    maps fairly well to an approach based on an "extended" return statement
+    or a specially marked "return" object, where the declaration of the
+    object to be returned can (or must?) include discriminants.)
+
+  - There is presumably still some need for the existing capability, where
+    the called routine returns a reference to a preexisting object, and the
+    caller either passes it on to some other subprogram, or renames it
+    for repeated local use.  (To avoid the run-time model change
+    discussed above, some alternative syntax in the function declaration
+    would seem to be necessary to distinguish the new-object case from
+    this existing return-by-reference case.)
+
+Much of the complexity associated with returning limited types arises
+from cases where the result type is unconstrained, the worst being
+the case where it is also indefinite (i.e. no defaulted discriminants).
+
+It is therefore important to decide how valuable is the ability to
+support returning limited objects where the size of the returned object is
+not known just from the result subtype.
+
+-----------------------------------------------------------------
+------------ Procedure-renamed-as-function Redux ----------------
+-----------------------------------------------------------------
+
+In fact, if we restrict ourselves to cases where the result subtype is
+*definite*, then the procedure-renamed-as-function solution for "returning"
+limited objects looks more attractive.  The caller would always be able
+to create the object using the result subtype if the "context" didn't
+provide constraints, so the call would be permitted in any context,
+eliminating the generic contract model problem and the need for new
+legality rules resembling those for "others" in array aggregates.
+The renaming could be in the private part as well (it could not
+be postponed to the body, since the convention of such a function
+would necessarily be intrinsic).
+This also eliminates all the complexity associated with the caller
+providing a storage pool or equivalent.  Instead, they just pass
+in a reference to an already created and default-initialized object.
+The procedure renamed as a function can treat it like any other
+[in]-out parameter of a limited type.
+
+It is somewhat interesting to note that the "Initialize" procedure
+of limited controlled types can be thought of like one of these
+procedures renamed as functions, where the default initialization
+for controlled types is roughly equivalent to ":= Initialize;"
+presuming:
+
+   function Initialize return Lim_Cntrl_Type renames procedure Initialize;
+
+In other words, the Initialize procedure for a controlled type is
+called at the same point that one of these procedures-renamed-as-function
+would be called for a limited object initialized by a call on
+such a function.
+
+Presumably if the limited object is both controlled
+and has an initialization specified by a function call, then Initialize
+is called immediately prior to calling the explicitly specified
+procedure-renamed-as-function, to ensure the object gets properly
+default initialized before being manipulate by "user" code.
+
+--------------------------------------------------------------------------
+------------ What about returning anonymous access types? ----------------
+--------------------------------------------------------------------------
+
+There is some advantage in having a name for the result object
+being returned from a function returning an anonymous access
+type, which gets us into the "extended" return statement.
+However, most of the advantage comes in the case where you
+are returning an allocator for an anonymous access-to-limited type,
+and there is no way to write anything but an aggregate (if that) as the
+intializer for the allocator.  However, if we posit the existence
+of the above procedure-renamed-as-function as a solution for the
+limited type problem, then we can call such a function in our initialized
+allocator.
+
+Given a reduced need for the extended return statement, then we can
+go to a relatively simple proposal for returning anonymous access types,
+namely require that the caller pass in a storage pool and an associated
+accessibility level, which would be used only if the expression of a
+return statement is an allocator, or a call on another such function.
+An accessibility level would be included with the returned object.
+
+The "only" new syntax for this would be allowing an access_definition to
+appear for the result subtype of a function.
+
+--------------------------
+
+So... my brainstorming has led to the following:
+
+   1) allow renaming a procedure with one [in] out parameter
+      as a function, provided the [in] out parameter's nominal
+      subtype is a definite subtype.  Require the use of
+      a syntax like "function blah(...) return foo renames procedure blurfo;"
+      to make it clear to the reader and the compiler that a procedure is
+      being renamed.  If the parameter type is limited, then
+      such a function can only be called where we have proposed to
+      allow limited aggregates (i.e. initializing a declared object,
+      initializing a component of an aggregate, a default expression
+      for a component_definition, in an initialized allocator,
+      as an actual IN parameter or parameter default, as an actual
+      formal IN object or formal IN default).
+
+   2) allow an access_definition for a function result subtype.
+      If the expression of a return statement in such a function
+      is an allocator (or a call on another such function), a storage
+      pool and accessibility level provided by the caller is used
+      (or passed on to the called function).
+
+Any and all comments, flames, better brains, are welcomed...
+
+I will wait a week or two and write up the results in time
+for the San Diego meeting.
+
+*************************************************************
+
+From: Robert Dewar
+Sent: Sunday, November 16, 2003  4:10 AM
+
+> This kind of argument is not very convincing if it just applies to
+> one particular implementation approach
+
+
+For the record, this is a *very* convincing argument if the one implementation
+approach that it applies to is in significant use today. If Ada 0Y (what a
+terrible name) requires major changes in implementation approaches at any
+point, the corresponding features will simply be ignored. The best hope for
+seeing anyone implement these features is to make sure that they are not
+disruptive in this sense.
+
+I realize that Tuck goes on to say that this is not the case hee, but it
+is still an important point for the record.
+
+I would guess that the GNAT situation is a typical one. We are indeed forging
+ahead implementing many of the new proposals, but at this stage, that is only
+practical if they do not involve any major shift in impoementation (which
+to a remarkable extent is the case with many/most/all? proposals so far).
+
+After all, we avoided downward closures based on concerns from just a
+couple of implementors in the Ada 9X process. The situation now even
+more requires no major shifts.
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Sunday, November 16, 2003  7:50 AM
+
+My real point was that any vendor could torpedo any
+proposal they don't like by saying that it would require
+changing their run-time model for existing code.
+
+I feel that if we can suggest reasonable alternative approaches
+that don't require changing the run-time model, then that
+should counteract some of the concern.  However, if it seems that there
+really is no way to avoid changing the run-time model of
+existing code, then that is a significant problem.
+And it seems pretty clear that having to support both return-by-reference
+and return-new-object semantics with identical syntax for
+the function declaration is such a case.
+
+And if you promise not to overuse the argument, I'll promise
+to take each one you identify seriously ;-).
+
+*************************************************************
+
+From: Robert Dewar
+Sent: Sunday, November 16, 2003  8:36 AM
+
+> My real point was that any vendor could torpedo any
+> proposal they don't like by saying that it would require
+> changing their run-time model for existing code.
+
+Basically I would agree they can. After all if you don't have the major
+vendors on board for a change at this stage, you might as well forget it.
+In fact I think you can expect vendors to operate in good faith (otherwise
+the whole process is broken).
+
+> And if you promise not to overuse the argument, I'll promise
+> to take each one you identify seriously ;-).
+
+exactly :-) That's really the way things work.
+
+An interesting question, in retrospect, did we really make the right
+decision to accomodate use of a display, when in practice for Ada 95
+static chains make more sense anyway? We certainly paid a price for this
+accomodation!
+
+Going back to the first paragraph, any vendor can torpedo any proposal
+by simply not implementing it :-)
+
+Yes, there will be some competitive pressure, which may be relevant for
+some customers, but I would not count on this as a major factor.
+
+That being said, I note again, that in the case of GNAT we are implementingf
+away, and have a lot of the new proposals working in our latest builds.
+
+*************************************************************
+
+From: Robert A. Duff
+Sent: Sunday, November 16, 2003  7:00 AM
+
+Robert Dewar says:
+
+> An interesting question, in retrospect, did we really make the right
+> decision to accomodate use of a display, when in practice for Ada 95
+> static chains make more sense anyway? We certainly paid a price for this
+> accomodation!
+
+Well, *my* opinion on that point has not changed in 20 years. ;-)
+
+The one case where Ada is clearly inferior to Pascal...
+
+(I never found the "display" argument compelling, either, since
+I know of at least one Pascal compiler that used displays,
+and implemented procedural parameters properly.  Not the *easiest*
+way to do it, but certainly doable.)
+
+> Going back to the first paragraph, any vendor can torpedo any proposal
+> by simply not implementing it :-)
+
+Indeed.  This is an important point.
+
+*************************************************************
+
+From: Robert Dewar
+Sent: Sunday, November 16, 2003  7:10 PM
+
+> The one case where Ada is clearly inferior to Pascal...
+
+Of course in GNAT, one has Unrestricted_Access to redress the balance, but
+it is hardly elegant :-)
+
+Unrestricted_Access is a real language extension. Are we going to fix this
+in Ada0Y
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Sunday, November 16, 2003  9:46 PM
+
+The AI on anonymous access-to-subprogram parameters has been approved
+for intent by the ARG.  Unfortunately, it isn't quite ready
+for forwarding to the WG9.
+
+*************************************************************
+
+From: John Barnes
+Sent: Monday, November 17, 2003  1:42 AM
+
+I just sent a new version to Randy to put on the database.
+
+*************************************************************
+
+From: Robert I. Eachus
+Sent: Sunday, November 16, 2003  10:06 AM
+
+Tucker Taft wrote:
+
+>Any and all comments, flames, better brains, are welcomed...
+>
+>
+I don't know about the better brains, and I'll try and avoid flames.
+
+As far as I am concerned we are trying to create these special
+subprograms that are neither fish nor fowl to solve a real problem in
+Ada.  Why don't we focus on what the problem is, how to solve it
+technically, and then invent a notation that works.  Yes, I said invent
+a notation.  This is a language revision and if there is something
+missing from the language--and I think we have all concluded that there
+is--we need to come up with a language change that does minimal violence
+to existing code and implementations while solving the real problem in a
+way that is acceptable to users.  It will be nice if it doesn't look
+like a kludge, but that is a goal, not a necessity.
+
+So what problem are we trying to solve?  Primarily constructors for
+limited types.  There are also issues for functions returning anonymous
+access types, but I suspect that if we get the allocators right, that
+will fall out along the way.  The hard problems seem to be the same. And
+as far as I see, if we get allocators right, the need for functions
+which return the problem access types goes down.
+
+So what is the problem? We need to define something that looks
+suspiciously like an assignment operator, except that it can only be
+used with new objects. (Don't worry I am going to try to avoid anything
+that looks like procedure ":=".  That is a can of worms I don't want to
+touch.)  Now I am going to make a cut.  There are three cases to be
+dealt with:
+
+Easy:  The object and constructor have identical static bounds,
+discriminants, and/or other constraints, and there are no run-time issues.
+
+Constrained object:  The object is defined as constrained, the
+constructor may take its constraints from the actual object.  (This is
+the case that definitely, IMHO, needs new syntax--if we allow it.)
+
+Constrained constructor:  The constructor allocates the object and
+returns it.  The object is created by the constructor and that
+determines any constraints necessary on the object.
+
+(For the anonymous access type cases, add "the subtype designated by"
+where necessary.)
+
+Do we need to support the constrained object case?  Notice that the
+rules allow constraints on the target object in the other cases.  The
+difference is that is what I am calling the constrained object case, the
+constraints back propagate into the constructor function.  In the easy
+and constrained constructor case, there may be a constraint check at
+compile time or after the object is constructed, and an error if it
+fails.  I think that this case is a "nice to have" and as I said above
+will require new syntax to avoid massive work in existing compilers.
+What do others think?
+
+Next, in what I call the constrained constructor case, the issues are
+that the size of the returned object is determined by the constructor,
+and that the object must be constructed in place.  I can come up with
+several notational models for how this works, but they all come back to
+"hidden" parameters.  The compiler has to pass the information on where
+the object is to be created to the constructor, OR the constructor gets
+a "thunk" as a parameter that it can call with the size of the object to
+be created, and then later return the address of the created object.  (I
+deliberately said address.  This may be an object of an anonymous in
+some of the cases of interest, and in others not.  It is really a return
+"by reference" in the constructor case.)
+
+As I see it we are back to the need for a new syntax, but with one
+difference worth noting.  I don't see any simple way to combine the
+constrained constructor and constrained object from the compilers point
+of view.  I also don't see any reason to confuse users by trying to mix
+the two cases.
+
+So as I see it, we can:
+
+1) go with the easy cases and let make (or keep) the other cases illegal.
+2) allow the constrained object case with new syntax.
+3) allow the constrained constructor case with new syntax..
+4) allow both of the above with different syntax.
+
+I guess I favor 3), with a syntax something like:
+
+constructor <parameter list> returns <subtype indication>;
+
+We can discuss where constructors can appear separately, but I think it
+is pretty obvious:  in an object declaration after the :=,  and in an
+allocator after new.
+
+That limits the compiler work, and makes it clear that these subprograms
+are special.
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Sunday, November 16, 2003  9:50 PM
+
+I will admit it is a bit frustrating that most ARG notes
+end up getting more comments off-topic than on.  ;-)
+I sent three messages over the weekend.  There has
+been only one truly on-topic response, I believe.
+And that one was essentially a completely new
+proposal. Oh well.  I'm as guilty as anyone...
+
+*************************************************************
+
+From: Robert I. Eachus
+Sent: Monday, November 17, 2003  4:32 AM
+
+If you are referring to my post, I guess you are right.  I was trying to
+evaluate your proposal (renaming of procedures as functions) and thought
+the wider context discussion was implictly opened by your proposal, and
+we should resolve the issue.  As far as I am concerned, you tabled a
+proposal that solved what I called the "constrained object" case, and
+either ignored or implictly ruled the "constrained constructor" case out
+of bounds.  (The intersection of the two, the "easy" cases, should fall
+out of any workable solution.)
+
+As part of drawing this distinction, I thought it was a good idea to
+have a proposed notation on the floor for the "constrained constructor"
+case as well.  So I put up a stalking horse. All I was really trying to
+do was to make it clear that, in your proposal, the constraints on the
+created object would be passed into the constructor function through the
+out parameter.  I certainly like your mapping of the constrained object
+case to something that current compilers support, so it is mostly
+front-end work for compiler developers.  However, I do think that in a
+final proposal, that mapping would be better left implicit.  I think
+that the "extra" work that compilers would have to do to allow these
+special-purpose procedures to be called AS procedures (with an already
+initialized object) would not be doing users any favors.
+
+But current compilers also support the case (for non-limited types) of
+constructor functions where the constructor determines the bounds of the
+object.  It would be nice to "open a window" so that when the full type
+is non-limited constructor functions can be made visible.  For compilers
+that currently support creating the return value "in place" such a
+solution would work for types with say task components as well.
+
+There were constrained constructor proposals discussed previously with
+respect to AI-318, so I didn't see any new ground being opened.  And I
+hope it was clear that I think it will place an undue burden on current
+compilers unless any solution to the constrained object or constrained
+constructor cases uses new syntax so that the implementor is clued in to
+what is going on as early as possible.
+
+*************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, December 4, 2003  8:22 PM
+
+In order to give Tucker the technical comment he craves:
+
+He suggested 4 problems:
+
+   - Limited types are hard to use.
+   - It is sometimes desirable to create a single constructor operation
+Sometimes? If it was possible, it would always be desirable. Certainly, that is
+how Janus/Ada works internally (every record type has a single constructor
+"thunk"), and its nasty that users can't do that as well. Anyway, these two are
+the same, given that these items would only be allowed in "constructor"
+locations. This is the problem we're trying to solve.
+
+   - Anonymous access types help prevent proliferation of named access types
+True, but only worthwhile if there are no complications. AI-230 only got
+finished once all of the complications were removed. The same holds here.
+   - It is not straightforward to write a function that allocates in a
+     caller-determined storage pool.
+True, but is this a real problem? And if it is, I'd prefer to fix it with
+better support for pools in generics, not messing around here.
+
+> In fact, if we restrict ourselves to cases where the result subtype is
+> *definite*, then the procedure-renamed-as-function solution for
+> "returning" limited objects looks more attractive.
+
+OK by me.
+
+> It is somewhat interesting to note that the "Initialize" procedure
+> of limited controlled types can be thought of like one of these
+> procedures renamed as functions, where the default initialization
+> for controlled types is roughly equivalent to ":= Initialize;"
+> presuming:
+>
+>    function Initialize return Lim_Cntrl_Type renames procedure Initialize;
+>
+> In other words, the Initialize procedure for a controlled type is
+> called at the same point that one of these procedures-renamed-as-function
+> would be called for a limited object initialized by a call on
+> such a function.
+>
+> Presumably if the limited object is both controlled
+> and has an initialization specified by a function call, then Initialize
+> is called immediately prior to calling the explicitly specified
+> procedure-renamed-as-function, to ensure the object gets properly
+> default initialized before being manipulate by "user" code.
+
+Umm, no. These ought to work like aggregates, and those don't call Initialize.
+We certainly want to be able to replace an aggregate with a constructor
+function, and vice versa.
+
+So if you have a user-defined constructor, it is the complete constructor;
+Initialize is called only for default initialized objects.
+
+...
+> Given a reduced need for the extended return statement, then we can
+> go to a relatively simple proposal for returning anonymous access types,
+> namely require that the caller pass in a storage pool and an associated
+> accessibility level, which would be used only if the expression of a
+> return statement is an allocator, or a call on another such function.
+> An accessibility level would be included with the returned object.
+>
+> The "only" new syntax for this would be allowing an access_definition to
+> appear for the result subtype of a function.
+
+I don't like the idea that a lot of runtime expense and compiler complication
+is hidden behind the use of a single keyword ("access") which normally is quite
+cheap. And I don't see the need (I don't see the need for any access
+parameters, when you get right down to it.)
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Thursday, December 4, 2003  8:48 PM
+
+Randy, Thanks for the feedback.
+
+> ...
+> > In fact, if we restrict ourselves to cases where the result subtype is
+> > *definite*, then the procedure-renamed-as-function solution for
+> > "returning" limited objects looks more attractive.
+>
+> OK by me.
+
+Good.
+
+> ...
+> > Presumably if the limited object is both controlled
+> > and has an initialization specified by a function call, then Initialize
+> > is called immediately prior to calling the explicitly specified
+> > procedure-renamed-as-function, to ensure the object gets properly
+> > default initialized before being manipulate by "user" code.
+>
+> Umm, no. These ought to work like aggregates, and those don't call
+> Initialize. We certainly want to be able to replace an aggregate with a
+> constructor function, and vice versa.
+>
+> So if you have a user-defined constructor, it is the complete constructor;
+> Initialize is called only for default initialized objects.
+
+I don't think this really works.  If the type is limited private,
+and there is an Initialize procedure, it should definitely be called
+to initialize the object properly.  You can't rely on some arbitrary
+user-written procedure to do the right thing as far as maintaining
+reference counts, etc.  It is *not* the same case as with
+an aggregate.  Those are only permitted on non-private types,
+and hence can be treated as an "inside the abstraction" operation,
+which can be relied upon to initialize reference counts, etc.,
+properly.
+
+The procedure-as-function can be declared anywhere, and so
+must be treated as an "outside the abstraction" operation,
+which cannot be relied on to preserve the invariant required
+by Initialize/Finalize.
+
+>
+> ...
+> > Given a reduced need for the extended return statement, then we can
+> > go to a relatively simple proposal for returning anonymous access types,
+> > namely require that the caller pass in a storage pool and an associated
+> > accessibility level, which would be used only if the expression of a
+> > return statement is an allocator, or a call on another such function.
+> > An accessibility level would be included with the returned object.
+> >
+> > The "only" new syntax for this would be allowing an access_definition to
+> > appear for the result subtype of a function.
+>
+> I don't like the idea that a lot of runtime expense and compiler
+> complication is hidden behind the use of a single keyword ("access") which
+> normally is quite cheap.
+
+
+This doesn't seem like a lot more overhead than other functions.
+functions that return composite objects typically have implicit
+parameters, unconstrained array parameters have implicit parameters,
+and access parameters have implicit parameters, so
+it seems not so big a surprise than anonymous access returns require
+an implicit parameter.  This implicit parameter would presumably
+be a reference to a storage pool and an accessibility level.
+This can often be a statically initialized data structure, since
+most storage pools are declared at the library level.
+
+> ... And I don't see the need (I don't see the need for
+> any access parameters, when you get right down to it.)
+
+Well if you don't see the need for access parameters, then
+obviously you don't see the need for access returns.  But
+if you have access parameters, which we already have and
+which are the basis for the whole AI on anonymous access types, and that
+adds access components and access renames, then it seems to
+leave a language hole to not include access returns.
+
+>             Randy.
+
+(I suspect this will get more juices flowing, so thanks
+for starting off the discussion.)
+
+*************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, December 4, 2003  9:09 PM
+
+> The procedure-as-function can be declared anywhere, and so
+> must be treated as an "outside the abstraction" operation,
+> which cannot be relied on to preserve the invariant required
+> by Initialize/Finalize.
+
+Humm. It's true that such a constructor function can be declared anywhere, but
+I don't think that is a problem. The only thing that such a function (procedure
+really) could is to pass the object on to another function or procedure to
+construct it. The *real* constructor function/procedure would have to be
+declared inside of the abstraction, which of course could do all of the needed
+initialization.
+
+If this is a real issue, then Robert Eachus's solution of an explicitly
+declared constructor is a better idea, because that could be required to be a
+primitive of the type, and thus would have to be in the same place as
+Initialize and any aggregates.
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Thursday, December 4, 2003  9:59 PM
+
+I still don't think this works.  Let's take an example:
+
+     type Handle is new Limited_Controlled with record
+         Ptr : Ptr_Type;
+         X : Integer := 0;
+     end record;
+
+     procedure Initialize(H : in out Handle);
+     procedure Finalize(H : in out Handle);
+     procedure Some_Op(H : in out Handle);
+     procedure Share_Object(Existing : in Handle; New_Ref : in out Handle);
+
+As things stand now, any primitive operation of the
+type Handle other than Initialize can assume
+that Initialize will have been called (let's presume
+it initializes Ptr to point to some heap object,
+and initializes the reference count of that to one).
+
+These operations should still be able to make the
+same assumption after this change.
+
+If someone should rename Some_Op or Share_Object to be
+a function, or declare their own procedure and rename
+it to be a function, then that shouldn't change the fact
+that Some_Op and Share_Object can safely assume that Ptr
+is non-null in all objects.
+
+I presume we all agree that they can all presume that
+the X component has been initialized to zero.  It seems
+like the call on Initialize goes along with that.
+The analogy with an aggregate doesn't work.  With
+an aggregate, we know that the programmer is forced to
+specify a value (possibly the "default" value) for
+every component.  With the procedure-as-function
+proposal, all we are saying is that given "L : Lim := Func;"
+we know the object L is passed to the procedure renamed
+as Func, before proceeding to the next declaration.
+This by no means gurantees that all fields are properly
+initialized, unlike an aggreate.  The renamed procedure
+might just return immediately, or might just set one
+component to have some special value.
+
+
+>
+> If this is a real issue, then Robert Eachus's solution of an explicitly
+> declared constructor is a better idea, because that could be required to be
+> a primitive of the type, and thus would have to be in the same place as
+> Initialize and any aggregates.
+
+Just making it a primitive doesn't fix the problem.  If you create
+some special thing called a constructor, then presumably it can
+only be called as part of a declaration, and when called it
+must presume what?  The object is totally uninitialized?
+The default initializations have occurred but Initialize has
+not been called?  Pointers are nulled out but other default
+initializations have not occurred?  I think you are wading into
+a morass.
+
+I believe this whole proposal flies only if it is kept very
+simple, with no complicated new semantics.  I think
+specifying that default initializations, including a call
+on Initialize if appropriate, have occurred is quite reasonable.
+
+A useful model is T'Input.  This is a function which
+is effectively a rename of the procedure T'Read for
+a type with a constrained first subtype.  And the default implementation
+of T'Input must clearly default initialize the object before calling a
+user-provided T'Read procedure, because we don't want user-written
+code being handed an improperly initialized object.
+
+So I think default initialization is the right model for these other
+proposed cases of procedures renamed as functions.
+
+*************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, December 4, 2003 11:03 PM
+
+> If someone should rename Some_Op or Share_Object to be
+> a function, or declare their own procedure and rename
+> it to be a function, then that shouldn't change the fact
+> that Some_Op and Share_Object can safely assume that Ptr
+> is non-null in all objects.
+
+But you can't do that! It isn't reasonable for some random procedure to be
+used as a constructor. Only a purpose-built routine that initializes
+everything can be used as one, and it would never make sense to actually
+call it as a procedure. So this again argues for the Eachus solution.
+
+> I presume we all agree that they can all presume that
+> the X component has been initialized to zero.  It seems
+> like the call on Initialize goes along with that.
+> The analogy with an aggregate doesn't work.  With
+> an aggregate, we know that the programmer is forced to
+> specify a value (possibly the "default" value) for
+> every component.  With the procedure-as-function
+> proposal, all we are saying is that given "L : Lim := Func;"
+> we know the object L is passed to the procedure renamed
+> as Func, before proceeding to the next declaration.
+> This by no means gurantees that all fields are properly
+> initialized, unlike an aggreate.  The renamed procedure
+> might just return immediately, or might just set one
+> component to have some special value.
+
+I like this less and less. A constructor has to set everything, somehow.
+Anything else is madness.
+
+The problem seems to be that these aren't really constructors, they just can
+be used as them.
+
+You're saying that the default initializer does everything, and then the
+constructor has to come along an undo it. Consider a constructor for
+unbounded strings:
+
+    function "+" (V : in String) return Unbounded_String....
+
+(This is suspiciously similar to something that's been removed from AI-301.)
+
+with the actual type being:
+    type Unbounded_String is new Ada.Finalization.Controlled with record
+        Str : String_Access := new String'("");
+    end record;
+
+With your semantics, this could be a rename of Set_Unbounded_String. But in
+that case, there is no advantage to having it (or Set_Unbounded_String, for
+that matter) - because the default sized memory string has been allocated.
+"+" would have to deallocate the existing memory, then allocate a new one of
+the right size. That doesn't sound like a constructor to me; we'd be doing
+an extra allocation and deallocation every time. Might as well have just
+used a regular function.
+
+A real constructor would get the uninitialized object, examine its
+parameters, then allocate the proper sized string. Once. No deallocation
+involved.
+
+> > If this is a real issue, then Robert Eachus's solution of an explicitly
+> > declared constructor is a better idea, because that could be required to be
+> > a primitive of the type, and thus would have to be in the same place as
+> > Initialize and any aggregates.
+>
+> Just making it a primitive doesn't fix the problem.  If you create
+> some special thing called a constructor, then presumably it can
+> only be called as part of a declaration, and when called it
+> must presume what?  The object is totally uninitialized?
+> The default initializations have occurred but Initialize has
+> not been called?  Pointers are nulled out but other default
+> initializations have not occurred?  I think you are wading into
+> a morass.
+
+Been there, done that. This is precisely how Janus/Ada initializes all
+objects; there's no problem. The constructor gets discriminants and bounds,
+and everything else is uninitialized. This isn't a "morass", its how
+constructors work. For Janus/Ada, we have to do this to allocate memory for
+any dynamically sized components (since we always allocate them to size).
+
+There currently are the standard three cases: default construction (which
+default initializes everything), aggregates (which of course do their own
+initialization, bypassing the top-level constructor, but components use the
+copy constructor), and (for non-limited types) copy construction (which gets
+the values from the copied object).
+
+Thinking about that, I don't immediately see how to implement either of
+these proposals. I guess your proposal would do the full default
+construction followed by Initialize before even calling the constructor.
+That seems both limiting and time consuming (you're doing everything twice
+including the allocation/deallocation of memory, and you're stuck with the
+initial bounds/discriminants in all cases). The Eachus proposal doesn't
+leave any place to allocate the memory - you can't do it until you know the
+bounds/discriminants, but by the time that you do, you're in user-defined
+code and it is too late to do anything. I suppose that means that we really
+do have to limit ourselves to cases where the constraint is known. Then, the
+old (now abandoned) "make" constructor (which only allocated memory) would
+do the trick. The make constructor was abandoned because it didn't work for
+components with mutable discriminants: the memory to allocate can only be
+determined on the assignment. We had hacked around that for years, but I
+finally got fed up with it, and blew it away completely last year. One point
+for Tuck. :-)
+
+> I believe this whole proposal flies only if it is kept very
+> simple, with no complicated new semantics.  I think
+> specifying that default initializations, including a call
+> on Initialize if appropriate, have occurred is quite reasonable.
+
+I think that we are talking inconsistent new semantics no matter what we
+choose. Just because it is simple to describe to an implementor doesn't mean
+that it makes much sense.
+
+Consider a non-limited constructor:
+
+    package P is
+        type T is new Ada.Finalization.Controlled with ...;
+        procedure Tuck_Constructor (Obj : in out T);
+        function New_Constructor return T renames procedure Tuck_Constructor;
+        function Old_Constructor return T;
+        procedure Initialize (Obj : in out T);
+        procedure Adjust (Obj : in out T);
+    end P;
+
+    O1 : T; -- Calls Initialize.
+    O2 : T := (Controlled with ...); -- Calls nothing.
+    O3 : T := Old_Constructor; -- Calls Adjust.
+    O4 : T := New_Constructor; -- Calls Initialize???
+
+O3 and O4 sure look the same; it doesn't look good to have them do vastly
+different things.
+
+But of course, we can't have O4 call Adjust (in part because if we change T
+to be limited, there is no Adjust). Besides, Adjust would do the wrong
+thing, because it is expecting a fully initialized object.
+
+I don't think that there can be a solution to this confusion (unless we
+abandon the ":=" notation for constructors, but that seems to be too large a
+change).
+
+> A useful model is T'Input.  This is a function which
+> is effectively a rename of the procedure T'Read for
+> a type with a constrained first subtype.  And the default implementation
+> of T'Input must clearly default initialize the object before calling a
+> user-provided T'Read procedure, because we don't want user-written
+> code being handed an improperly initialized object.
+
+See, I don't agree with this at all. A T'Read that doesn't initialize all of
+the non-discriminant components of whatever it is handed is just plain
+wrong. It certainly shouldn't be depending on anything it is handed. Now, it
+is true that the language cannot enforce a requirement that T'Read or a
+constructor actually initializes everything, or that it doesn't read
+anything it didn't set, but that is what they're intended to do.
+
+T'Read is just a specific constructor, and it shouldn't be doing expensive
+default initialization which will immediately be overwritten.
+
+> So I think default initialization is the right model for these other
+> proposed cases of procedures renamed as functions.
+
+I won't argue that from an implementation perspective, but I think it would
+be confusing as heck to users.
+
+*************************************************************
+
+From: Robert I. Eachus
+Sent: Thursday, December 4, 2003  11:48 PM
+
+Tucker Taft wrote:
+
+> Randy Brukardt wrote:
+>
+>> If this is a real issue, then Robert Eachus's solution of an explicitly
+>> declared constructor is a better idea, because that could be required
+>> to be
+>> a primitive of the type, and thus would have to be in the same place as
+>> Initialize and any aggregates.
+>
+I am beginning to accept that you are right.  The renaming of procedures
+as functions looks cleaner to start with, but it still requires syntax
+changes.  But worse, you do have this issue that when the function body
+is being compiled, there will be no explicit checks done. Users could
+call other operations that assumed that the object had already been
+initialized, with potentially damaging results.  We could just say that
+the programmers should be careful in this case, but that is not the
+normal Ada approach.
+
+> Just making it a primitive doesn't fix the problem.  If you create
+> some special thing called a constructor, then presumably it can
+> only be called as part of a declaration, and when called it
+> must presume what?  The object is totally uninitialized?
+> The default initializations have occurred but Initialize has
+> not been called?  Pointers are nulled out but other default
+> initializations have not occurred?  I think you are wading into
+> a morass.
+
+I don't see a morass.  The constructor would create a value of the
+limited type, but the object could not be accessed until after the
+constructor returned.  Inside a function you have a value that you will
+return 'in-place'.  In the case of a constructor, that would be the only
+use allowed.  During the call to the constructor, the object being
+initialized cannot be referenced, either by the constructor or
+otherwise.  You can certainly create an object of the type inside the
+constructor, and it will get default initialization.  But that object is
+not the object being initialized.
+
+With Tucker's renaming of a procedure, inside the body the out parameter
+has a name, and can be accessed.  This one the major difference between
+the two approaches.  (Or better, it is a side-effect of the fact that in
+the renamed procedure case, the bounds and other attributes can come
+from the target object, in my approach the object is only namable after
+the constructor returns.)
+
+As I said before, the two approaches are different in what they allow.
+I personally can live with either set of constraints.  But I think this
+discussion has shown a serious problem with the renamed procedure
+approach.  We would need special rules to cover what can be done with
+the out parameter inside the procedure to be renamed, or special
+semantics for references to that object.  But allowing it to be passed
+as a parameter to some other subprogram would seem to open an entire
+Pandora's box.
+
+Saying that some default initialization for the object occurs before the
+user-defined doesn't solve anything, or creates a new set of issues.
+Let's imagine a limited type that by default allocates some space on the
+heap.  You want to extend this type, and create an explicit constructor
+for it.  If the 'default' initialization occurs, and then you are going
+to stick another value in, you are going to have to deallocate the
+already assigned memory (or reuse it).
+
+But the extension type may not be able to see all the fields of the
+parent.  Take Limited_Controlled for a horrible example.  I keep coming
+back to the idea that in the 'normal' constructor case, if there is such
+a thing, the programmer will create an object of some ancestor type 'in
+place' to do some of the initialization, and then do whatever special
+bit fiddling is required in the parts of the type that he or she can name.
+
+But which ancestor type needs to be used is a decision that needs to be
+made by the programmer, not by the language. (For a type derived from
+Limited_Controlled, this type might be Limited_Controlled, but it is
+much more likely to be the parent, or possibly a grandparent.)   The
+return statement of the constructor should not be required to be an
+extension aggregate, but in most cases it will be anyway.
+
+*************************************************************
+
+From: Pascal Leroy
+Sent: Friday, December 5, 2003  8:59 AM
+
+> If this is a real issue, then Robert Eachus's solution of an
+> explicitly declared constructor is a better idea, because
+> that could be required to be a primitive of the type, and
+> thus would have to be in the same place as Initialize and any
+> aggregates.
+
+If we think that adding constructors to the language is important, then,
+yes, let's do something like what Eachus suggested.  I really don't like
+Tucker's proposal which looks like a wart (or many warts) to me.  The
+renaming of a procedure as a function is weird, and the dynamic
+semantics of the constructor being called after initialization has taken
+place doesn't make any sense to me, as the constructor would have to
+undo the initialization.
+
+I agree with Randy that constructors should in general work like
+aggregates.
+
+*************************************************************
+
+From: Robert I. Eachus
+Sent: Thursday, December 4, 2003  11:48 PM
+
+>But you can't do that! It isn't reasonable for some random procedure to be
+>used as a constructor. Only a purpose-built routine that initializes
+>everything can be used as one, and it would never make sense to actually
+>call it as a procedure. So this again argues for the Eachus solution.
+
+Up until now, I have been viewing my role here as helping to define the
+problem.  But now I think that I see a way to handle both difficult (and
+of course more useful) cases.  First go with a special name/syntax for
+constructors. Second, insist that constructors can only be used to
+create objects.  It may be useful to permit a constructor to appear as
+an (in) parameter in a call, but I really don't see that as all that
+important or useful.  But the cases that have to be outlawed are the
+uses of a constructor in the prefix of a name.
+
+Next within a constructor, allow attributes of "return" to be queried.
+(Or you can use some other name, for example the name of the constructor
+itself, which does less damage to existing parsers, so I'll use that in
+my examples.)
+
+Now I can write (using String to make a point):
+
+    constructor Blanks(Length: Integer := 0) return String is
+    begin
+         if Blanks'Constrained
+         then
+            declare
+                Result: String(Blanks'Range)  :=  (others => ' ');
+             begin
+                return Result;
+             end;
+         else
+             declare
+                Result: String(1..Length) := (others => ' ');
+             begin
+                 return Result;
+          end if;
+     end Blanks;
+
+The amount of work in the semantic phase of compilers would be
+significant, but it has many advantages from a language point of view:
+
+      Constructors are special and can be declared as such.
+      Functions can serve as constuctors where the special features are not
+          needed.  (I could write a Blanks function that handled the
+          unconstrained case and a parameterless constructor for the
+          constrained case.  In fact, in this case I probably would, but
+          I wrote this as an example of the syntax and semantics.)
+      Programmers don't have to learn any 'weird' new syntax or
+          semantics.  The only two things that are not intuitive about this
+          proposal are the name to use as the prefix of attributes and
+          components, and the new reserved word "constructor."
+
+>I like this less and less. A constructor has to set everything, somehow.
+>Anything else is madness.
+>
+>The problem seems to be that these aren't really constructors, they just can
+>be used as them.
+
+I am in complete agreement.  I think it is possible to do constructors
+right.  Whether the amount of work involved in doing them right can be
+justified in this language revision is a different question.  I think
+the answer is in the affirmative.  But there is no justification for
+adding something that looks like a kludge to programmers and doesn't
+solve the whole problem.  The renaming of a procedure as a function as
+such probably doesn't reach the kluge level.  But the things we are now
+discussing to make it 'work' certainly do.  It may be that there is a
+better fix to be found, but I think allowing attributes of the result to
+be used in constructors looks like the most elegant solution.
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Friday, December 5, 2003  5:03 PM
+
+I think you are definitely killing this proposal with complexity.
+
+Let's look at typical ways of writing "constructor" functions:
+
+   function make_blah1(x, y : params) return blah is
+      Result : blah;
+   begin
+      Result.x := x;
+      Result.y := y;
+      Massage(Result);
+      return Result;
+   end make_blah1;
+
+   function make_blah2(x, y : params) return blah is
+      Result : blah := (x => x, y => y, z => 0);
+   begin
+      Massage(Result);
+      return Result;
+   end make_blah2;
+
+   function make_blah3(x, y : params) return blah is
+      Result : blah := some_other_func(x);
+   begin
+      Result.y := y;
+      Massage(Result);
+      return Result;
+   end make_blah3;
+
+   function make_blah4(x, y : params) return blah is
+   begin
+       return (x => x, y => y, z => 0);
+   end make_blah4;
+
+I would tend to use make_blah1 if there are a lot of components,
+and the default initial value of most of the components is fine.
+I would tend to use make_blah2 if there are relatively few components,
+and I want to be sure that if a new component is added, I am forced
+to update my code.
+I would tend to use make_blah3 if there is an existing constructor
+that does about the right thing, but I want to tweak the result a bit.
+I would tend to use make_blah4 in the same circumstances as make_blah2,
+when there is no complex work to be done to build up the desired value.
+
+Of course as things exist now, none of these functions could be
+used with limited types.  For limited types we are forced to
+use procedures to do all "construction", or possibly use discriminants
+as effectively parameters to the Initialize procedure.
+In all these cases, the object will undergo most if not all default initialization
+before we get our hands on it.  Ada 2005 will hopefully allow us to
+use aggregates, but that is no use if the limited type is also private,
+which is exactly when you want to be able to have "constructor" operations.
+
+So as things are now, programmers are used to the idea that limited types
+get default initialized, and then you have to be sure to call the
+constructing "procedure" as soon as possible so that no access to
+the object occurs before it is initialized as intended.
+
+What this proposal was trying to create was a situation where the
+desired initialization can be specified at the point of creation, to
+ensure no inappropriate "early" access is possible.  This proposal
+becomes even more important if we add limited aggregates, because
+having to insert a call on a separate initialization procedure for each
+of the limited components of a limited aggregate is going to be a real
+pain.  We really want to have some way of specifying the initialization
+at the place of the component in the aggregate.  Similarly, an initialized
+allocator for limited types is very limiting if the only thing we can
+use is an aggregate.  Something with the syntax of a function call would
+be very useful, because it could be placed in all the contexts where
+we want to specify the (extra) initialization that is to be performed
+before further access is permitted.
+
+Note that one of the things that makes a limited type different from
+a non-limited type is the sense that it has identity, and it is connected
+to perhaps some entity that can't easily be represented by a few bits
+in a record (e.g. a thread of control, or a mutex, or some external
+resource like a window on a screen).  These kinds of limited objects
+must undergo their "default" initialization, before the programmer
+gets their hands on them, and clearly the programmer isn't going to override
+all of the state of the component.  They are just going to "tweak" the
+state of the component in some way, perhaps, and a procedure is
+just the thing for doing this.  E.g., an initialization procedure-as-function
+for a task might call an entry with some initializing values.  It certainly
+can't "fully initialize" the task.  That is not meaningful for limited
+objects in general.
+
+So... I really think the idea of default-initialization-plus-user-specified
+procedure-to-tweak-the-initial-state is exactly what we want for limited
+types.  I think we should allow these procedures renamed as functions
+for non-limited types as well (contract model and all that), but clearly
+they would normally only be used if the intended constructor was similar to
+make_blah1 or make_blah3 when some_other_func was a similar constructor.
+In that case, writing make_blah1, make_blah3, and some_other_func as procedures,
+and then renaming them as functions, could produce identical semantics.
+
+In any case, so long as it is clear that the semantics of a procedure
+renamed as a function is that the returned object is the result
+of applying the procedure to a default-initialized object, the semantics
+are well-defined and easy to explain.  Having to define a new kind
+of program unit that via data flow rules or whatever we make sure that
+no component is referenced before it is properly initialized, reminds me
+of the "out" parameter morass of Ada 83, on steroids.
+
+For aggressive optimizers, when a procedure is renamed as a function, it
+could actually generate the function with a default-initialized "result"
+object, and then inline the call on the procedure, and then proceed to remove
+all redundant default initializations of the fields of the object.
+
+So back to my initial point.  If we want to provide the ability to
+specify how a limited object should be initialized at its point of
+creation, let's not kill it with kindness and complexity.
+The semantics of a function result being equivalent to applying
+a procedure to a default-initialized object are well-defined,
+no worse than what is available today for limited types, appropriate
+to the underlying principle of limited types as having some amount
+of unalterable state bound up with its identity, and
+safer and friendlier for the user than forcing a separation between
+creation and initialization.
+
+*************************************************************
+
+From: Dan Eilers
+Sent: Friday, December 5, 2003  5:42 PM
+
+Robert Eachus wrote:
+
+> Now I can write (using String to make a point):
+>
+>     constructor Blanks(Length: Integer := 0) return String is
+>     begin
+>          if Blanks'Constrained
+>          then
+>             declare
+>                 Result: String(Blanks'Range)  :=  (others => ' ');
+>              begin
+>                 return Result;
+>              end;
+>          else
+>              declare
+>                 Result: String(1..Length) := (others => ' ');
+>              begin
+>                  return Result;
+>           end if;
+>      end Blanks;
+
+
+It seems to me that in most cases, the bounds of the return type are
+determined by the constructor's input parameters.  This permits always
+allocating space for the return object prior to the call, even when the
+constructor is used in a larger expression.
+
+Your example of Blanks is such a constructor, as is concatenation,
+matrix multiply, etc.
+
+If the syntax of contructors allowed the return type to be constrained
+by the parameters, then we could simplify your example to:
+
+     constructor Blanks(Length: Integer := 0) return String(1..length) is
+     begin
+         return (others => ' ');
+     end Blanks;
+
+or for "&",
+
+    constructor "&"(x,y: string) return result: String(1..x'length+y'length) is
+    begin
+        result(1..x'length) := x;
+        result(x'length+1..result'last) := y;
+        return result;
+    end "&";
+
+*************************************************************
+
+From: Robert A. Duff
+Sent: Friday, December 5, 2003  7:04 PM
+
+> It seems to me that in most cases, the bounds of the return type are
+> determined by the constructor's input parameters.  This permits always
+> allocating space for the return object prior to the call, even when the
+> constructor is used in a larger expression.
+
+This is something I've been wanting for years.
+
+*************************************************************
+
+From: Randy Brukardt
+Sent: Friday, December 5, 2003  8:02 PM
+
+> I think you are definitely killing this proposal with complexity.
+
+I don't know who "you" refers to here, but I don't think the proposal is that
+complex. Moreover, if it isn't complex enough to solve the problem effectively,
+it isn't worth doing. If we're not willing to adopt AI-318 as it currently
+stands (meaning the change in calling conventions), then I think we need to
+step back and take a good look at the problem.
+
+We started by thinking we wanted limited functions. But those make no sense
+unless they are a constructor. They're a standard ADT feature that is poorly
+supported in Ada, even for non-limited types.
+
+So it makes good sense to look at this as a constructor. The problem is that
+all Ada types give very little control over initialization. I'd like to be able
+to write a version of Unbounded_Strings that doesn't repeatedly create useless
+junk (or have distributed overhead to avoid the useless junk).
+
+> So as things are now, programmers are used to the idea that limited types
+> get default initialized, and then you have to be sure to call the
+> constructing "procedure" as soon as possible so that no access to
+> the object occurs before it is initialized as intended.
+
+What programmers are used to is the idea that limited types sound good in
+theory, but are useless in practice. That's one of the reasons that windows in
+Claw are non-limited, even though it would make more sense for them to be
+limited.
+
+I don't think I've even declared a single limited type since Claw, since it is
+so clear that they're useless for ADTs. If you have to trust the user of the
+ADT to do something, either the language or the ADT (or both) is broken. That's
+precisely the model we need to get away from.
+
+Let's look specifically at the "complexity" of this counter proposal.
+
+It needs a new form of subprogram, the constructor. This is clearly not a
+function or procedure. But it reuses most of the syntax and rules from those.
+Is this a big deal? I don't think so: AI-348 adds just such a program unit, the
+null procedure. (Which is definitely not a "normal" procedure - it is an
+entirely new kind of unit, at least as the rules are written. Just as an
+abstract subprogram was in Ada 95 - which didn't cause an uproar.)
+
+It might make sense to limit constructors to be primitive operations of the
+result type, but I'm not certain that is necessary.
+
+The constructor would use the return do construct proposed in AI-318. This
+looks like:
+
+     return identifier : subtype_indication [:= expr]
+         [do
+             handled_sequence_of_statements;
+          end [identifier]];
+
+This would only be allowed in a constructor. Initialization (default or
+explicit) would take place when this (compound) statement is evaluated (not
+before). Since AI-287 gives us not only limited aggregates, but default
+initialized components in those aggregates, it is possible to use an aggregate
+for any type if we need to avoid default initialization.
+
+[Aside: it would make sense to allow (others => <>) for any object, even for
+private types. That would give a way to explicitly default initialize a
+component without having to do specify all of the other components as well.]
+
+The sequence of statements would allow later massaging.
+
+I'd prefer that if the initializing expression is omitted, top-level default
+initializations are also omitted. But this isn't essential (and it does
+complicate things a bit). I'm sure Tucker will feel better if it is avoided.
+
+Calls on constructors would be limited to places where aggregates are allowed.
+
+If we allowed this in the general case, then some memory allocation would have
+to be supported at the point of the return statement. (This is a certainty for
+Janus/Ada, no matter what.) That means that we'd probably need to pass in a
+storage pool or some other representation, as previously outlined. But since
+this is a new feature, there cannot be a compatibility problem. We could
+mitigate this problem somewhat by limiting the result subtype to be constrained
+(it would still allow everything if a wrapper record was used), but I doubt
+that would help enough to justify the oddity.
+
+> Let's look at typical ways of writing "constructor" functions:
+
+OK, let's:
+
+    constructor make_blah1(x, y : params) return blah is
+    begin
+        return Result : blah := (others => <>) do -- I'm explicitly showing the default
+                                                -- initialization here, but that's not
+                                                -- necessary.
+           Result.x := x;
+           Result.y := y;
+           Massage(Result);
+        end Result;
+    end make_blah1;
+
+    constructor make_blah2(x, y : params) return blah is
+    begin
+        return Result : blah := (x => x, y => y, z => 0) do
+            Massage(Result);
+        end Result;
+    end make_blah2;
+
+   constructor make_blah3(x, y : params) return blah is
+   begin
+        return Result : blah := some_other_func(x) do -- Some_other_func(x) better be a
+                                                      -- constructor, at least if blah
+                                                      -- is limited.
+            Result.y := y;
+            Massage(Result);
+        end Result;
+    end make_blah3;
+
+   constructor make_blah4(x, y : params) return blah is
+   begin
+      return Result : blah := (x => x, y => y, z => 0);
+   end make_blah4;
+
+These all look good to me. Moreover, the example I gave last night would work
+without double construction:
+
+    type Unbounded_String is new Ada.Finalization.Controlled with record
+        Str : String_Access := new String'("");
+    end record;
+
+    constructor "+" (V : in String) return Unbounded_String is
+    begin
+       return Result : Unbounded_String :=
+           (Ada.Finalization.Controlled with Str => new String'(V);
+    end "+";
+
+> What this proposal was trying to create was a situation where the
+> desired initialization can be specified at the point of creation, to
+> ensure no inappropriate "early" access is possible.  This proposal
+> becomes even more important if we add limited aggregates, because
+> having to insert a call on a separate initialization procedure for each
+> of the limited components of a limited aggregate is going to be a real
+> pain.  We really want to have some way of specifying the initialization
+> at the place of the component in the aggregate.  Similarly, an initialized
+> allocator for limited types is very limiting if the only thing we can
+> use is an aggregate.  Something with the syntax of a function call would
+> be very useful, because it could be placed in all the contexts where
+> we want to specify the (extra) initialization that is to be performed
+> before further access is permitted.
+
+Exactly. That's what constructors are for. But they're clearly not functions
+(at least not "normal" functions), and they certainly aren't procedures.
+
+> Note that one of the things that makes a limited type different from
+> a non-limited type is the sense that it has identity, and it is connected
+> to perhaps some entity that can't easily be represented by a few bits
+> in a record (e.g. a thread of control, or a mutex, or some external
+> resource like a window on a screen).  These kinds of limited objects
+> must undergo their "default" initialization, before the programmer
+> gets their hands on them, and clearly the programmer isn't going to override
+> all of the state of the component.  They are just going to "tweak" the
+> state of the component in some way, perhaps, and a procedure is
+> just the thing for doing this.  E.g., an initialization procedure-as-function
+> for a task might call an entry with some initializing values.  It certainly
+> can't "fully initialize" the task.  That is not meaningful for limited
+> objects in general.
+
+Of course it is. There are some limited objects/components which have to be
+default initialized. We've got a syntax for doing that; there is no problem.
+
+Let me say right now that I'm really only interested in constructors for ADTs.
+And I think that virtually all ADTs ought to be controlled. Given how
+controlled works in Ada 95, that means I'm only interested in types that are
+(ultimately) derived from one of the types in Ada.Finalization. Whatever we
+come up with ought to make some sort of sense for other types, but it not at
+all important that it is useful. (Which is why I don't care if constraints
+work; those shouldn't generally be visible anyway.)
+
+In any case, what you described would be written as:
+
+    constructor Run_It (My_Id : in String) return Tucks_Tasks is
+    begin
+       return Result : Tucks_Tasks := (other => <>) do
+           Result.Start_Up (My_Id);
+       end Result;
+    end "+";
+
+> So back to my initial point.  If we want to provide the ability to
+> specify how a limited object should be initialized at its point of
+> creation, let's not kill it with kindness and complexity.
+
+No, but we at least had better be able to do the job. If we cannot avoid
+default initialization,  ADTs will have to have a convoluted design internally
+to avoid excessive costs -- but that adds a distributed overhead to the use of
+them which may be unacceptable.
+
+> The semantics of a function result being equivalent to applying
+> a procedure to a default-initialized object are well-defined,
+
+Sure.
+
+> no worse than what is available today for limited types,
+
+Boy, this is a strong endorsement. :-) I want to waste time implementing
+something that is "no worse than what we have today".
+
+> appropriate to the underlying principle of limited types as having some amount
+> of unalterable state bound up with its identity,
+
+Any proposal I've seen meets this requirement.
+
+> and safer and friendlier for the user than forcing a separation between
+> creation and initialization.
+
+Huh? That's precisely what we're trying to avoid with constructors: to be able
+to eliminate the separation between creation and initialization. Certainly,
+calling a separate procedure call at some unspecified later time is a horrible
+separation. And doing the default initialization when it is inappropriate is
+also a unnecessary separation.
+
+*************************************************************
+
+From: Jean-Pierre Rosen
+Sent: Friday, December 5, 2003  3:59 PM
+
+From: "Robert I. Eachus" <rieachus@comcast.net>
+>[...]
+>          and the new reserved word "constructor."
+We could call this "limited function" and save a keyword...
+Seems to carry the spirit.
+
+*************************************************************
+
+From: Robert A. Duff
+Sent: Saturday, December 6, 2003  9:01 AM
+
+Randy, in reply to Tuck, writes:
+
+> > I think you are definitely killing this proposal with complexity.
+>
+> I don't know who "you" refers to here,
+
+I didn't understand that either.  Because Tuck didn't say "... writes".
+You (Randy) followed suit.  ;-)
+
+> What programmers are used to is the idea that limited types sound good in
+> theory, but are useless in practice.
+
+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?
+
+(I also have limited types with default initialization, and/or kludgy
+discriminant hackery, where constructor functions of some sort would be
+cleaner.)
+
+>... 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.
+
+You can stick to controlled types if you like, but I think it's a bad
+idea to assume (for the language design) that the only types that need
+constructors are controlled.  I have lots of limited types in my current
+project, and lots of types that *would* be limited if only I could
+initialize them nicely.
+
+But I have very few controlled types; they're simply too inefficient.
+(Actually, I guess I should say I have very few controlled *objects*.
+That is, if I have a type where I'm creating and destroying thousands or
+millions of objects of the type, I can't make it controlled, because
+it's too slow.  The number of *types* is irrelevant to this efficiency
+issue.)
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Saturday, December 6, 2003  9:39 AM
+
+> Randy, in reply to Tuck, writes:
+>
+> > > I think you are definitely killing this proposal with complexity.
+> >
+> > I don't know who "you" refers to here,
+>
+> I didn't understand that either.  Because Tuck didn't say "... writes".
+> You (Randy) followed suit.  ;-)
+
+I thought it was obvious I was talking about Randy and Robert Eachus,
+who were proposing a completely new kind of program unit, namely a constructor.
+
+I could see adding a reserved word to make it clear that a given
+function was designed to create a new object, and the caller must
+allocate space for the object and initialize it at least to
+some extent before the call.  I wouldn't limit the contexts in
+which such a function could be called.
+
+> > What programmers are used to is the idea that limited types sound good in
+> > theory, but are useless in practice.
+>
+> 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.  ...
+
+The big question for me is then whether you feel the admittedly
+limited (;-) capability provided by procedures renamed as functions
+would be adequate.
+
+*************************************************************
+
+From: Robert A. Duff
+Sent: Saturday, December 6, 2003  2:05 PM
+
+Tuck says:
+
+> > Randy, in reply to Tuck, writes:
+> >
+> > > > I think you are definitely killing this proposal with complexity.
+> > >
+> > > I don't know who "you" refers to here,
+> >
+> > I didn't understand that either.  Because Tuck didn't say "... writes".
+> > You (Randy) followed suit.  ;-)
+>
+> I thought it was obvious I was talking about Randy and Robert Eachus,
+> who were proposing a completely new kind of program unit, namely a
+> constructor.
+
+For those of us who might read these things out of order,
+or weeks/months/years later, a line at the front saying
+"so-and-so said, ..." would be useful.
+
+> I could see adding a reserved word to make it clear that a given
+> function was designed to create a new object, and the caller must
+> allocate space for the object and initialize it at least to
+> some extent before the call.  I wouldn't limit the contexts in
+> which such a function could be called.
+>
+> > > What programmers are used to is the idea that limited types sound good in
+> > > theory, but are useless in practice.
+> >
+> > 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.  ...
+>
+> The big question for me is then whether you feel the admittedly
+> limited (;-) capability provided by procedures renamed as functions
+> would be adequate.
+
+Sorry, but I don't understand the details well enough to be sure.
+I don't understand the limitations.  I've lost track.  I just went back
+and re-read AI's 318 and 325, but they seem to have obsolete proposals.
+I also re-read your original e-mail on this subject, and lots of others,
+but I still don't have a good understanding of what all the proposals
+are, in detail.
+
+I suspect the answer is, "Yes, the func-renames-proc thing is good
+enough".  But I don't understand the details well enough to distinguish
+it from the "constructor(...) return ..." syntax.  Is it just an
+argument as to which syntax is more sugary, and which more sour?
+
+Despite my current ignorance, I'll offer some comments:
+
+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?
+
+This implies that constructors cannot be required to be primitive ops.
+And therefore that such constructors cannot see improperly initialized
+objects.
+
+----
+
+I like the idea that these new constructor functions are recognized
+somehow by the *spec*.
+
+----
+
+One way to write "bullet proof" abstractions is to forbid clients from
+creating uninitialized objects, by saying "type T(<>) is limited private".
+It would be nice if the abstraction could then export constructor
+functions, and clients could compose them, and/or call them.
+Clients cannot write "X: T;", so they ought to be able to write
+"X: T := Primitive_Constructor(...);", or declare their own composed
+constructor, and use that.  Either way, the package itself has total
+control over object creation.  The unknown discriminants do not
+*necessarily* mean the thing has truly unknown size.
+I'm not sure how important this is.
+
+----
+
+I like Dan Eiler's idea, of allowing a function result to be definite,
+but dependent on parameters, as in "function F(X: String) return
+String(1..X'Length)".  It seems related to all this limited-type stuff,
+in that it allows the caller to know sizes of function results that
+would otherwise be "return String".
+
+*************************************************************
+
+From: Robert I. Eachus
+Sent: Saturday, December 6, 2003  3:21 PM
+
+Tucker Taft wrote:
+
+>I thought it was obvious I was talking about Randy and Robert Eachus,
+>who were proposing a completely new kind of program unit, namely a constructor.
+>
+>I could see adding a reserved word to make it clear that a given
+>function was designed to create a new object, and the caller must
+>allocate space for the object and initialize it at least to
+>some extent before the call.  I wouldn't limit the contexts in
+>which such a function could be called.
+>
+>
+If you think the limitation I proposed--that a constructor cannot be
+used as a prefix of a name is a big deal, fine.  But procedures can't be
+used in that context either, so I think that with the renaming approach
+you probably need that restriction too.
+
+To me the constructor proposal without the attributes is slightly
+simpler than your (Tucker's) renaming proposal, from a compiler
+viewpoint.  But they cover different cases of constructors.  The
+renaming approach covers cases where the constraints on the object being
+initialized come from the target, where in the new subprogram type
+approach, the case where the bounds are determined by the constructor is
+easily handled.  Adding the attributes, perhaps only specific
+attributes, to the new subprogram approach covers all constructor cases,
+but is probably more work than the renaming approach.  So, with my
+implementor hat on, I'd definitely want the context where constructors
+can appear limited.  This fits nicely with using the constructor name in
+attributes.  If the name always refers to the containing instance, then
+it is pretty clear that allowing the name as a call in prefixes would
+cause ambiguity.
+
+As a user, I very much like the idea of covering all the cases with one
+new construct.  And I really think it is worth calling it "constructor"
+not "limited function."  I also like the idea of adding the "return
+Result: whatever do .. end; construct.  It is clear to me that in most
+constructors you will need a nested scope anyway, unless the constructor
+is equivalent to assigning default values to fields.
+
+As for the brouhaha about initialization, I definitely see a use for
+constructors in replacing "junk" initializations.  But allowing the
+default initializations to occur (unless there is an attribute
+assignment) is no big deal.  The solution for the problems where the
+initial values are not wanted will be to not provide the defaults, just
+an Initialized: Boolean := False; if there is a potential problem.  The
+Limited_Controlled example is perfect.  You want/need the
+implementation's default initialization of any implementation specific
+fields of objects derived from Controlled or Limited_Controlled.  But if
+decent constructors are available, the creator of the type extension can
+just use constructors instead of default values for the difficult stuff.
+
+This does have one implication that needs watching though--it is not a
+problem, but should be explicitly discussed.  What happens if someone
+tries to assign a constructor to an already existing limited object?
+The three reasonable answers are that it is illegal, Program_Error is
+raised, and that the current value is finalized (if it is controlled)
+and a new value/object is created.  I favor making it illegal for a
+limited object, but Tucker seems to want something else.  (If it is
+illegal, then any discussion of whether the object is initialized is out
+of bounds.)
+
+>The big question for me is then whether you feel the admittedly
+>limited (;-) capability provided by procedures renamed as functions
+>would be adequate.
+>
+At this point my answer is no.  I think that the constructors without
+the attribute support are simpler to implement than the renaming
+approach, and much easier on the user.  The big issue to me is whether
+we need to support constructors that take their bounds from the target
+object.  At this point I feel that it is worth doing, but a close call.
+However, the partial functionality of constructors without the
+attributes is to me significantly better than the renaming approach.  It
+can end up requiring slightly more writing and overhead than the renamed
+procedure approach to create a constrained object of an unconstrained
+type with discriminants (and without a default for the discriminants):
+
+     Fubar: Foo(Bar) := Make_Foo(Bar);
+
+(You would have to explicitly repeat the discriminants once on the
+object, once as parameters to the constructor.)  However, the renaming
+approach does not handle the case where the constructor deterimines the
+constraints at all.   To give an example I recently ran into, think
+about a constructor that converts a linked list into an array.  For a
+non-limited type, no problem at all, create a temporary array object,
+and walk the list, if you run out of room in the temporary object, recur:
+
+  type String_Array is array (Positive range <>) of Unbounded_String;
+
+  type String_List is private;
+...
+private
+  type String_Pointer is access String_List;
+
+type String_List is record
+   Value: Unbounded_String;
+   Next: String_Pointer
+end record;
+...
+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;
+
+When writing this I was seriously concerned about the number of
+Unbounded_String objects being created and assigned, so this is probably
+a good test case for arguments about initializations.  (It is possible
+to walk the list once counting and create the array "in place" if we add
+a way to do that.)
+
+*************************************************************
+
+From: Robert I. Eachus
+Sent: Saturday, December 6, 2003  3:52 PM
+
+Robert A Duff wrote:
+
+>Sorry, but I don't understand the details well enough to be sure.
+>I don't understand the limitations.  I've lost track.  I just went back
+>and re-read AI's 318 and 325, but they seem to have obsolete proposals.
+>I also re-read your original e-mail on this subject, and lots of others,
+>but I still don't have a good understanding of what all the proposals
+>are, in detail.
+>
+>I suspect the answer is, "Yes, the func-renames-proc thing is good
+>enough".  But I don't understand the details well enough to distinguish
+>it from the "constructor(...) return ..." syntax.  Is it just an
+>argument as to which syntax is more sugary, and which more sour?
+>
+>
+No, in the cases where there are no discriminants involved either
+proposal suffices.  In the renaming approach the object has to be
+created before the constructor is called, which means that the bounds
+have to be known before (or during) the call.   I just sent in an
+example where this is not really possible.  In the new subprogram
+approach, we can either allow attributes to query the target, or the
+constructor will construct a value and there may be a Constraint_Check
+after the call.  This particular constraint check is ugly since it has
+to be made after the object is created, and it works best if there is an
+explicit copy of the result into the object.  This is why I like the
+return...do construct.  Clearly the constraint check will occur inside
+the constructor, at the point where the return value is created but
+before it can be changed by the do...end; block.
+
+>This implies that constructors cannot be required to be primitive ops.
+>
+I'm not sure what you are saying here.  Constructors should be allowed
+to be primitive, and we will have to argue about whether they become
+abstract when inherited unless overridden in any case.  But I don't feel
+strongly about whether they can be other than primitive operations.
+
+>And therefore that such constructors cannot see improperly initialized
+>objects.
+>
+>
+I tend to agree.  The best arguement in favor of this position is that
+of type extensions.  In the package that creates an extension, you may
+not be able to see into the parent part of the object.  So its
+initialization has to be done implicitly or by a call to a constructor
+for the parent type.  As I said in my previous post, this is not too
+onerous.  If you are the author of a type, you can decide to put any
+initialization in the constructors instead of as initial values.
+
+>I like the idea that these new constructor functions are recognized
+>somehow by the *spec*.
+>
+>
+>
+I think it is necessary.  Otherwise we have a fairly heavy distributed
+overhead.  Right now both approaches, satisfy this requirement.
+
+>One way to write "bullet proof" abstractions is to forbid clients from
+>creating uninitialized objects, by saying "type T(<>) is limited private".
+>It would be nice if the abstraction could then export constructor
+>functions, and clients could compose them, and/or call them.
+>Clients cannot write "X: T;", so they ought to be able to write
+>"X: T := Primitive_Constructor(...);", or declare their own composed
+>constructor, and use that.  Either way, the package itself has total
+>control over object creation.  The unknown discriminants do not
+>*necessarily* mean the thing has truly unknown size.
+>I'm not sure how important this is.
+>
+>
+You may have just killed the renaming approach.  I had forgotten about
+declaring a type with unknown discriminants as it is not too useful
+currently.  With the renaming approach you can't use such a type.  With
+the constructor as new subprogram approach, even without the attibutes,
+such types will be used all over the place.  As you say, the only way to
+for a user to create one will be to call a constructor.
+
+>I like Dan Eiler's idea, of allowing a function result to be definite,
+>but dependent on parameters, as in "function F(X: String) return
+>String(1..X'Length)".  It seems related to all this limited-type stuff,
+>in that it allows the caller to know sizes of function results that
+>would otherwise be "return String".
+>
+I like it too.  But I think it is more of a "nice to have" for other
+reasons than a solution to this problem.
+
+*************************************************************
+
+From: Randy Brukardt
+Sent: Saturday, December 6, 2003 10:09 PM
+
+Bob Duff wrote:
+
+> Randy, in reply to Tuck, writes:
+>
+> > > I think you are definitely killing this proposal with complexity.
+> >
+> > I don't know who "you" refers to here,
+>
+> I didn't understand that either.  Because Tuck didn't say "... writes".
+> You (Randy) followed suit.  ;-)
+
+I usually answer a number of messages at once (otherwise I have to file a lot
+more messages). That makes it hard to do and still be understandable.
+
+> > What programmers are used to is the idea that limited types sound good in
+> > theory, but are useless in practice.
+>
+> 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. 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 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.
+
+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.
+
+> >... 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.
+
+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. That's down
+in the noise for virtually all uses. Compilers that use mapping solutions
+should cost even less (if not, they shouldn't bother with them!!).
+
+The space overhead is more of an issue. I agree that if you have very small
+data types, and you need very many objects, then there may be an issue. But
+there are very few such types in any project by definition.
+
+> You can stick to controlled types if you like, but I think it's a bad
+> idea to assume (for the language design) that the only types that need
+> constructors are controlled.  I have lots of limited types in my current
+> project, and lots of types that *would* be limited if only I could
+> initialize them nicely.
+
+No, I said the only types that need constructors are ADTs. And all ADTs should
+be controlled (but not necessarily in the way that Ada 95 does controlled).
+
+> But I have very few controlled types; they're simply too inefficient.
+> (Actually, I guess I should say I have very few controlled *objects*.
+> That is, if I have a type where I'm creating and destroying thousands or
+> millions of objects of the type, I can't make it controlled, because
+> it's too slow.  The number of *types* is irrelevant to this efficiency
+> issue.)
+
+I was worried about the efficiency of my spam filter, because it stores
+everything in Unbounded_Strings -- which are just a ball of heap operations and
+finalizations. But it turns out that the big expense is actually loading the
+patterns - the actual filtering operations are down in the noise.
+
+If you're creating and destroying millions of objects, you have an efficiency
+problem from doing that. Whether the objects are controlled or just integers
+doesn't matter at all. Heap operations are so much slower than type creation
+that they are the bounding factor -- you'll need very careful caching to make
+the thing usable at all.
+
+Anyway, we have completely different philosophies of programming, so I'm not
+surprised that our results differ. What I find important is that we solve this
+problem in a way that works for as many users as possible.
+
+*************************************************************
+
+From: Randy Brukardt
+Sent: Saturday, December 6, 2003 10:25 PM
+
+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?
+
+> 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 like the idea that these new constructor functions are recognized
+> somehow by the *spec*.
+
+Good. :-)
+
+> One way to write "bullet proof" abstractions is to forbid clients from
+> creating uninitialized objects, by saying "type T(<>) is limited private".
+> It would be nice if the abstraction could then export constructor
+> functions, and clients could compose them, and/or call them.
+> Clients cannot write "X: T;", so they ought to be able to write
+> "X: T := Primitive_Constructor(...);", or declare their own composed
+> constructor, and use that.  Either way, the package itself has total
+> control over object creation.  The unknown discriminants do not
+> *necessarily* mean the thing has truly unknown size.
+> I'm not sure how important this is.
+
+Well, if you want to do this, I think Tucker's proposal is a non-starter.
+(Robert Eachus explained why).
+
+----
+
+A few other observations on the (rough) proposal I put out yesterday:
+
+The proposal is very similar to the AI-318 discussed at the Sydney meeting. The
+only real difference is the use of the keyword "constructor". That means that
+the implementation issues discussed for that proposal would pretty much hold.
+
+Constructors would be allowed exactly where aggregates are allowed, with the
+same semantics. That means a non-limited constructor used in a regular
+assignment would create a temporary object, then assign it. (Obviously, an
+implementation could optimize that in some cases.)
+
+The AARM points out in several places that := used for initialization is very
+different than := used for assignment. What this proposal is really doing is
+allowing users to write their own := initialization operations. That's a
+capability currently absent in Ada (you can get parts of it, but not the whole
+thing).
+
+One additional concern about Tucker's proposal. Many times in the past, we've
+used the invariant that you can't call a procedure in a declarative part to
+prove that some rule or other can't cause a problem (usually with "in out"
+parameters). If that is no longer true, there are a number of rules that would
+need to be revisited (freezing, incomplete types, who knows how many others). I
+don't look forward to finding new holes caused that eliminating that.
+
+*************************************************************
+
+From: Tucker Taft
+Sent: Saturday, December 6, 2003 10:19 PM
+
+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*.
+
+For example:
+
+   type Lim(F1 : Integer) is limited private;
+
+   procedure P(Y : Integer; Out_Parm : out 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 => <>));
+
+The renamed thing can be simply the name of a procedure as in the
+earlier proposal, in which case it is equivalent to providing a default
+initialized object as the actual for the [IN] OUT parameter of the procedure,
+with the other function parameters passed to the corresponding
+(by position) procedure IN parameters.
+
+Alternatively, the renamed thing can be a procedure *call,* with the [IN]
+OUT parameter given an initializing expression rather than a variable
+as the actual.  The other parameters (which would have to be IN parameters)
+would be given expressions of the approriate type.
+
+In both cases, the "result" of calling the "function" is the
+final value of the [IN] OUT parameter.
+
+This latter form would clearly give a lot of flexibility, but
+the *caller* would still be able to create the object (on the
+stack, in the heap, as a component, etc.), perform
+the specified initialization (rather than always performing default
+initialization), register it for task waiting and/or
+finalization, etc., as appropriate, and then call the designated
+procedure.
+
+This has a lot of nice properties.  The caller is still in charge
+of getting the object ot a state at which it can be safely registered
+for task waiting and/or finalization, and it is still in charge of
+allocation.  The discriminants of the object can be determined
+by other parameters to the "function."  And the out-of-line
+procedure can still whatever it needs to to finish the desired
+initialization/construction.
+
+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.
+
+Anyway, this might be a way to kill several birds with one
+relatively straightforward stone...
+
+*************************************************************
+
+From: Randy Brukardt
+Sent: Saturday, December 6, 2003 10:19 PM
+
+Tucker said:
+
+> 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.
+
+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 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 latter form would clearly give a lot of flexibility, but
+> the *caller* would still be able to create the object (on the
+> stack, in the heap, as a component, etc.), perform
+> the specified initialization (rather than always performing default
+> initialization), register it for task waiting and/or
+> finalization, etc., as appropriate, and then call the designated
+> procedure.
+
+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).
+
+I realize other implementors mileage may differ somewhat, but they've
+already got to face the problem of deferring finalization of the return
+object.
+
+> This has a lot of nice properties.  The caller is still in charge
+> of getting the object ot a state at which it can be safely registered
+> for task waiting and/or finalization, and it is still in charge of
+> allocation.  The discriminants of the object can be determined
+> by other parameters to the "function."  And the out-of-line
+> procedure can still whatever it needs to to finish the desired
+> initialization/construction.
+
+I really see no benefit to having the caller do that. That said, I don't see
+much harm in it either.
+
+> 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.
+
+Yes, it certainly would be more consistent that way.
+
+> Anyway, this might be a way to kill several birds with one
+> relatively straightforward stone...
+
+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.
 
 *************************************************************
 

Questions? Ask the ACAA Technical Agent