* Proposal: Constructors, Assignment [LONG] @ 2002-12-24 11:16 Dmitry A. Kazakov 2002-12-26 22:11 ` Nick Roberts 0 siblings, 1 reply; 20+ messages in thread From: Dmitry A. Kazakov @ 2002-12-24 11:16 UTC (permalink / raw) The proposal possibly solves the problems: 1. Constructors with parameters for all user types 2. Initialization of limited types 3. Assignments for all user types 4. Ability to do things like stream attributes 5. Expressions for discriminants hidden within a constructor The general idea is to split constructor (and assignment) into two separate subroutines. One is used to get constraints, another is to finish construction. The subroutine for getting constraints returns them as an anonymous record type (an attribute is used to denote it) with the discriminants set to the constraints. Example1: type Socket (Host_Name_Length : Positive) is tagged limited record Host_Name : String (1..Host_Name_Length); . . .; -- Other components end record; function Get_Constraint (Host_Name : String) return Socket'Constraint; for Socket'Get_Constraints use Get_Constraint; procedure Initialize (Object : in out Socket; Host_Name : String); for Socket'Initialize use Initialize; [ Maybe to prevent "syntactic sugar diabetes", we could allow to override these and all others subroutine attributes just like: function Socket'Get_Constraints (Host_Name : String) return Socket'Constraint; ] ... function Get_Constraint (Host_Name : String) return Socket'Constraint is begin return (Host_Name => Host_Name'Length); end Get_Constraint; ... procedure Initialize (Object : in out Socket; Host_Name : String) is begin Object.Name := Host_Name; Open_Connection (Host_Name); end Initialize; ... Connection : Socket ("127.0.0.1"); Example2: type Char_String is array (Integer range <>) of Unsigned_8; function Get_Bounds (Text : String) return Char_String'Constraints; for Char_String'Get_Constraints use Get_Bounds; procedure Set (Object : in out Char_String; Text : String); for Char_String'Initialize use Set; ... function Get_Bounds (Text : String) return Char_String'Constraints is begin return (First_1 => 1, Last_1 => Text'Length); end Get_Bounds; ... procedure Set (Object : in out Char_String; Text : String) is To : Integer := Object'First; begin for From in Text'Range loop Object (To) := Unsigned_8 (Character'Pos (Text (From))); end loop; end Set; ... X : Char_String ("some text"); -- Constructor Y : Char_String := "some text"; -- Assignment More formal 1. Attribute S'Constraints. For any subtype S there is an attribute S'Constraints, defined as an anonymous record type: type S'Constraints [(<the-list-of-constraints>)] is null record; [VARIANT for any tagged type] 1.1 For a constrained type <the-list-of-constraints> is empty. 1.2 For an unconstrained array type it is the array bounds (named in some nice way). 1.3 For an unconstrained discriminated record, task, protected type it is the discriminant part of the type. 1.4 For a class-wide type it is Tag. For example: type User (Name_Length : Positive) is tagged record Name : String (1..Name_Length); ID : User_ID; end record; User'Constraint is type * (Name_Length : Positive) is null record; User'Class'Constraint is type * (Type_Tag : Tag) is null record; 2. Attribute S'Get_Constraints denotes a set of overloaded functions having the following parameter profiles: function S'Get_Constraints [(<parameter-list>)] return S'Constraints; 2.1 For a constrained type: function S'Get_Constraints return S'Constraints; is defined and returns null record; 2.2 For an unconstrained array type: function S'Get_Constraints (<bounds-list>) return S'Constraints; is defined and returns S'Constraints with the discriminants set to the bounds from the list. 2.3 For an unconstrained discriminated type function S'Get_Constraints (<discriminant-list>) return S'Constraints; is defined and returns S'Constraint with discriminants set from the list. 2.4. For a class-wide type it is not defined. It can be defined (S'Class'Get_Constraint), then S'Get_Constraints and S'Initialize have to be defined as well. Additionally for non-limited types: function S'Get_Constraints (Source : S) return S'Constraints; is defined and returns S'Constraints with the discriminants extracted from the constraints of Source When a derived type overrides discriminants, it has to override S'Get_Constraints as well. 3. Attribute S'Initialize denotes a set of overloaded procedures: procedure S'Initialize (Object : in out S [; (<parameter-list>)]); 4. Constructors 4.1 Specific types. A constructor is defined if a pair S'Get_Constraints, S'Initialize is defined such that they have conformant parameter lists, the first parameter of S'Initialize is not counted. For a specific subtype S the constructor works as follows: 1. S'Get_Constraint is called to get all necessary constraints 2. Memory is allocated according to the constraints 3. S'Initialize is called to finish construction 4.2 Class-wide types. A constructor is defined if S'Class'Get_Constraints is defined (and thus S'Get_Constraints and S'Initialize are defined for all descendants of S). For a class-wide subtype S'Class the constructor works as follows: 1. S'Class'Get_Constraint is called to get tag 2. Dispatching call to S'Get_Constraint [to get the constraints] 3. Memory is allocated according to the constraints 4. Dispatching call to S'Initialize [to finish construction] 4.3 Any constructor of a subtype S can be used in: Object_Of_S : S (<parameter-list>); new S ((<parameter-list>); subtype Subtype_Of_S is S ((<parameter-list>); 5. Assignment A constructor of S with the parameter list consisting of single parameter [VARIANT having the type S] is called copy-constructor. For a non-limited type for each copy-constructor an assignment is generated: 5.1 Specific types 1. S'Get_Constraint (Source) is called to get new constraints 2. New constraints are checked against the constraints of Target 3. Target is finalized 4. Constraints are overridden 5. S'Initialize (Target, Source) is called 5.2 Class-wide types 1. S'Class'Get_Constraint (Source) is called to get new tag 2. The new tag is checked against Target'Tag 3. S'Get_Constraint (Source) is called to get new constraints 4. New constraints are checked against the constraints of Target 5. Target is finalized 6. Tag and constraints are overridden 7. S'Initialize (Target, Source) is called -- Regards, Dmitry A. Kazakov www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2002-12-24 11:16 Proposal: Constructors, Assignment [LONG] Dmitry A. Kazakov @ 2002-12-26 22:11 ` Nick Roberts 2002-12-27 17:43 ` Dmitry A. Kazakov 0 siblings, 1 reply; 20+ messages in thread From: Nick Roberts @ 2002-12-26 22:11 UTC (permalink / raw) Forgive me for not quoting Dmitry's post. I take the view that the 'object constructor problem' that Ada is sometimes accused of suffering from is nearly always solved (in a better way than languages which have special constructor functions) by the old chestnut of: declaring the type as private (and indefinite) in its own package specification; providing whichever constructor functions as primitive operations of this type are appropriate. The implementation of the type must use an unsophisticated discriminant scheme, but this is hidden, and the constructor functions translate from the appropriate parameters of construction to the implementational ones. The fact that the result of a function is permitted to be of an indefinite type allows the user of the package to declare an object of the type without a constraint, and initialise it using the appropriate function (the result of which provides the constraint). I'm pretty sure Dmitry is well aware of this technique. However, I think many members of the ARG would feel that the proposer of a functional construction scheme (for the next language revision) needs to demonstrate some pretty significant advantage the proposal has over the above general approach. Example 1 that Dmitry gave might be rehashed thus: package Session_Management is type Internet_Session(<>) is private; function Open (Host: in String) return Internet_Session; ... private type Internet_Session ( Host_Name_Length: Natural Traceback_Length: Natural) is new Ada.Finalization.Controlled with record Host_Name: String(1..Host_Name_Length); Traceback: ?.Node_Array(1..Traceback_Length); Connection: ?.IP_Connection ... end record; end; package body Session_Management is function Open (Host: in String) return Internet_Session; begin Open_Connection(Session.Connection,Host); declare Result: Internet_Session := ( Host'Length, Get_Node_Count(Session.Connection), ... ); begin ... return Result; end; exception ... end Open; ... end Session_Management; I've indulged myself in adding a detail -- the storage of a node traceback -- to illustrate the idea that not just the parameters of a constructor function can directly affect the structure of the type, but also any arbitrary circumstances of its construction (at the time it is constructed). I've also indulged myself in changing the names of things a bit. These are only a minor touches. Possibly the most relevant point is that the type (Internet_Session) is not limited, and cannot be (because an initialisation is required and limited types cannot be initialised). If a limited type is required (and it probably is, realistically, for this example), the typical technique is to make the type's implementation an access value (referencing a totally private 'payload' type which actually contains the relevant data, and which can be indefinite). The public specification of the type can then be definite, so that the user can declare an object of the type without having to initialise it, and then maybe call a procedure to get it into a proper (initial) state (which may well involve allocating an object of the payload type). Of course, this is usually precisely the situation for the Ada.*_IO.File_Type types. It could be argued that the indirection of access this causes is inefficient, but not (or very rarely) realistically, in my opinion. Hope this all makes sense! -- Nick Roberts ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2002-12-26 22:11 ` Nick Roberts @ 2002-12-27 17:43 ` Dmitry A. Kazakov 2002-12-27 20:17 ` Randy Brukardt 0 siblings, 1 reply; 20+ messages in thread From: Dmitry A. Kazakov @ 2002-12-27 17:43 UTC (permalink / raw) Nick Roberts wrote: > Forgive me for not quoting Dmitry's post. > > I take the view that the 'object constructor problem' that Ada is > sometimes accused of suffering from is nearly always solved (in a better > way than languages which have special constructor functions) by the old > chestnut of: declaring the type as private (and indefinite) in its own > package specification; providing whichever constructor functions as > primitive operations of this type are appropriate. > > The implementation of the type must use an unsophisticated discriminant > scheme, but this is hidden, and the constructor functions translate from > the appropriate parameters of construction to the implementational ones. > The fact that the result of a function is permitted to be of an indefinite > type allows the user of the package to declare an object of the type > without a constraint, and initialise it using the appropriate function > (the result of which provides the constraint). > > I'm pretty sure Dmitry is well aware of this technique. However, I think > many members of the ARG would feel that the proposer of a functional > construction scheme (for the next language revision) needs to demonstrate > some pretty significant advantage the proposal has over the above general > approach. > > Example 1 that Dmitry gave might be rehashed thus: > > > package Session_Management is > > type Internet_Session(<>) is private; > > function Open (Host: in String) return Internet_Session; > > ... > > private > > type Internet_Session ( > Host_Name_Length: Natural > Traceback_Length: Natural) is > new Ada.Finalization.Controlled with > record > Host_Name: String(1..Host_Name_Length); > Traceback: ?.Node_Array(1..Traceback_Length); > Connection: ?.IP_Connection > ... > end record; > > end; > > package body Session_Management is > > function Open (Host: in String) return Internet_Session; > begin > Open_Connection(Session.Connection,Host); > declare > Result: Internet_Session := ( > Host'Length, > Get_Node_Count(Session.Connection), > ... ); > begin > ... > return Result; > end; > exception > ... > end Open; > > ... > > end Session_Management; > > > I've indulged myself in adding a detail -- the storage of a node > traceback -- to illustrate the idea that not just the parameters of a > constructor function can directly affect the structure of the type, but > also any arbitrary circumstances of its construction (at the time it is > constructed). I've also indulged myself in changing the names of things a > bit. These are only a minor touches. > > Possibly the most relevant point is that the type (Internet_Session) is > not limited, and cannot be (because an initialisation is required and > limited types cannot be initialised). > > If a limited type is required (and it probably is, realistically, for this > example), the typical technique is to make the type's implementation an > access value (referencing a totally private 'payload' type which actually > contains the relevant data, and which can be indefinite). The public > specification of the type can then be definite, so that the user can > declare an object of the type without having to initialise it, and then > maybe call a procedure to get it into a proper (initial) state (which may > well involve allocating an object of the payload type). Of course, this is > usually precisely the situation for the Ada.*_IO.File_Type types. > > It could be argued that the indirection of access this causes is > inefficient, but not (or very rarely) realistically, in my opinion. The first objection is that this schema requires maintenance of two unrelated (to the compiler) hierarchies of types: the implementation types and the handles to them. (*) So, let we want to derive from Internet_Session, say, My_Proprietary_Protocol_Session? -- The idea that a constructor is just a function is IMO badly wrong. Limited types is just one example why. Another example is class-wide types. How to construct a class-wide object from scratch? To write a class-wide function? But then it will never ever dispatch. A dispatching one? But the type is unknown and there is no object. This is why Ada's stream attributes are made hard-wired. This is also why assignment is a problem. And of course, an unnecessary language complexity. This and that work-arounds for initialization, construction, assignment etc, very often lead a programmer to unexpected and undesired side effects to the design. ------------------ (*) Ada offers no tools to ease this task, when the handle type is not an anonymous access type. [which BTW is not allowed to be OUT, INOUT or result] It is definitely necessary to have something to solve this, but no language I know addresses this. And it is not very clear how to do it anyway. -- Merry Christmas, Dmitry A. Kazakov www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2002-12-27 17:43 ` Dmitry A. Kazakov @ 2002-12-27 20:17 ` Randy Brukardt 2002-12-29 13:43 ` Dmitry A. Kazakov 0 siblings, 1 reply; 20+ messages in thread From: Randy Brukardt @ 2002-12-27 20:17 UTC (permalink / raw) I think that for this proposal to get serious consideration, you'll have to give rules to plug the many holes in it: -- You'd need to carefully explain why solutions based on functions (as Nick Roberts described) are not sufficient; -- You'll need to explain how this works for controlled types (an incompatibility there is simply not going to be tolerated); -- You'll need to determine the rules for these in generics. Your current description would be a giant contract model violation, and that needs to be fixed. In particular, for a generic formal private type: generic type Priv is private; what 'Get_Constructor and 'Initialize routines can be called? The fact that these are different in profile is simply not going to fly here (and I doubt that banning the use of constructors in generic bodies is going to be an acceptable solution). -- You'll need to explain how this works in the face of disappearing components on an assignment. That has been the problem that has killed all previous proposals like this one. See http://www.adaic.org/standards/95aarm/html/AA-7-6.html, paragraphs 17.a-17.h for a discussion. I'd prefer making the function solution work. There is a proposal to make it possible to initialize limited objects with a function call, which would eliminate that objection. Your other objection: >The idea that a constructor is just a function is IMO badly wrong. Limited >types is just one example why. Another example is class-wide types. How to >construct a class-wide object from scratch? To write a class-wide function? >But then it will never ever dispatch. A dispatching one? But the type is >unknown and there is no object. This is why Ada's stream attributes are >made hard-wired. This is also why assignment is a problem. The bug, IMHO, is that you can't write T'Class'Input in Ada. Its terrible that magic is needed there, because its clear that there are other cases where that sort of code would be useful. We investigated some solutions to that problem in the past. The primary problem is how to get the object created for a particular value of Ada.Tags.Tag. A direct approach doesn't work, because you don't know how to initialize the discriminants (they can be different for extensions than for the base type). However, an approach based on using the tag value to control dispatching of a function (rather than a result object) does work. And it is very simple to implement (as compilers are essentially dispatching on a value of Ada.Tags.Tag anyway). However, most people thought that inventing syntax to solve this problem was too heavy, and there really isn't any alternative. So nothing much has been done on this problem. The basic idea is to somehow figure a value of Ada.Tags.Tag, then use it to control dispatching on a function that returns the correct kind of object. This requires a special call: Obj : T'Class := Func (<args>) use Tag_Value; With these two capabilities, I believe that functions provide appropriate constructors, and you don't necessarily need to use a 'handle' implementation. (I suspect that in practice, you often will want to use a handle implementation anyway, but I certainly agree that you shouldn't be forced into it.) Leveraging existing language features is far preferable to inventing a whole new mechanism. Randy Brukardt. ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2002-12-27 20:17 ` Randy Brukardt @ 2002-12-29 13:43 ` Dmitry A. Kazakov 2002-12-29 18:45 ` Nick Roberts 2003-01-01 0:54 ` Randy Brukardt 0 siblings, 2 replies; 20+ messages in thread From: Dmitry A. Kazakov @ 2002-12-29 13:43 UTC (permalink / raw) Randy Brukardt wrote: Thank you for the response. > I think that for this proposal to get serious consideration, you'll have > to give rules to plug the many holes in it: > > -- You'd need to carefully explain why solutions based on functions (as > Nick Roberts described) are not sufficient; They cannot work when no assignment exists. Otherwise one should invent a sort of pickwickian assignment. > -- You'll need to explain how this works for controlled types (an > incompatibility there is simply not going to be tolerated); I do not think that there is any incompatibility. Initialize can be treated as an overriding of 'Initialize. > -- You'll need to determine the rules for these in generics. Your > current description would be a giant contract model violation, and that > needs to be fixed. In particular, for a generic formal private type: > generic > type Priv is private; > what 'Get_Constructor and 'Initialize routines can be called? The fact > that these are different in profile is simply not going to fly here (and > I doubt that banning the use of constructors in generic bodies is going > to be an acceptable solution). It is not a ban. It is just a contract. Priv is private, as such it has only the standard copy-constructor (because there is an assignment). No more. Why should we expect that every user-defined operation on any possible actual for Priv should be visible in the generic body? If that is an intent then one should do: generic type Priv is new Type_That_Has_Interesting_Constructors with private; Here Priv can use all primitive operations defined on Type_That_Has_Interesting_Constructors and user-defined constructors too. I suppose the fact that this works for tagged types only is not a fault of my proposal. (:-)) An we still can have nasty, but working: generic type Priv is private; with function My_Favorite_Operation (X : Priv) is <>; ... with function Priv'Get_Constraints (...) return ... is <>; with procedure Priv'Initialize (...) is <>; > -- You'll need to explain how this works in the face of disappearing > components on an assignment. That has been the problem that has killed > all previous proposals like this one. See > http://www.adaic.org/standards/95aarm/html/AA-7-6.html, paragraphs > 17.a-17.h for a discussion. It works same way it works for the controlled types. Compare: A. Controlled type assignment: A.1 Check constraints (obtained from source) A.2 Finalize target A.3 Copy bit-wise A.4 Call Adjust B. Assignment generated from a constructor B.1 Get constraints from the parameters (call to 'Get_Constraints) B.2 Check constraints B.3 Finalize target B.5 Call 'Initialize My point is that one can always generate an assignment from a constructor. I do not propose to allow user-defined assignments written in a form a subroutine. I propose that all assignments be generated out of constructors, user-defined ones inclusive. > I'd prefer making the function solution work. There is a proposal to > make it possible to initialize limited objects with a function call, > which would eliminate that objection. Yes it definitely would, because a function which result initialises an unconstructed object is just another name for constructor. However there would be also questions: 1. Will you use ":=" for limited types within a declaration with a problem to explain what is that, or just "rename"? 2. What to do with "new" for limited types, when a constructor have to be called? 3. I suppose that constructors will be allowed for Limited_Controlled only? So arrays, untagged types, tasks and protected object will remain out of reach. > Your other objection: > >>The idea that a constructor is just a function is IMO badly wrong. > Limited >>types is just one example why. Another example is class-wide types. How > to >>construct a class-wide object from scratch? To write a class-wide > function? >>But then it will never ever dispatch. A dispatching one? But the type > is >>unknown and there is no object. This is why Ada's stream attributes are >>made hard-wired. This is also why assignment is a problem. > > > The bug, IMHO, is that you can't write T'Class'Input in Ada. Its > terrible that magic is needed there, because its clear that there are > other cases where that sort of code would be useful. > > We investigated some solutions to that problem in the past. The primary > problem is how to get the object created for a particular value of > Ada.Tags.Tag. A direct approach doesn't work, because you don't know how > to initialize the discriminants (they can be different for extensions > than for the base type). However, an approach based on using the tag > value to control dispatching of a function (rather than a result object) > does work. And it is very simple to implement (as compilers are > essentially dispatching on a value of Ada.Tags.Tag anyway). However, > most people thought that inventing syntax to solve this problem was too > heavy, and there really isn't any alternative. So nothing much has been > done on this problem. > > The basic idea is to somehow figure a value of Ada.Tags.Tag, then use it > to control dispatching on a function that returns the correct kind of > object. This requires a special call: > > Obj : T'Class := Func (<args>) use Tag_Value; The question is, will dispatching without an object be useful in cases other than construction? This proposal requires new syntax. Mine Obj : T'Class (<args>); requires only new attributes. And gives new T'Class (<args>); subtype Specific_T is T (<args>); almost for free. > With these two capabilities, I believe that functions provide > appropriate constructors, and you don't necessarily need to use a > 'handle' implementation. (I suspect that in practice, you often will > want to use a handle implementation anyway, but I certainly agree that > you shouldn't be forced into it.) Leveraging existing language features > is far preferable to inventing a whole new mechanism. Though it is another question, I think that there is a need to have something like user-defined anonymous access types, with the operations becoming primitive for the pointed type. But it is probably far more difficult than just constructors. -- Happy New Year, Dmitry A. Kazakov www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2002-12-29 13:43 ` Dmitry A. Kazakov @ 2002-12-29 18:45 ` Nick Roberts 2002-12-30 12:23 ` Dmitry A. Kazakov 2003-01-01 0:54 ` Randy Brukardt 1 sibling, 1 reply; 20+ messages in thread From: Nick Roberts @ 2002-12-29 18:45 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:aumu4o$76buu$1@ID-77047.news.dfncis.de... > > -- You'd need to carefully explain why solutions based on functions (as > > Nick Roberts described) are not sufficient; > > They cannot work when no assignment exists. Otherwise one should invent a > sort of pickwickian assignment. Yes, Dmitry, but it is this Pickwickian assignment that has been mooted, and it is necessary for you to demonstrate that your proposal is superior. To be precise, interpreting Randy's suggestion, my suggestion would be for (Ada be changed in the next revision so that) an object declaration which includes a subtype indication that denotes a class-wide type whose root type is limited (tagged) to be permitted to include an (explicit) initialisation expression, in which case special rules would apply to the dynamic semantics for the elaboration of the declaration: (1) the subtype inidication is elaborated; (2) the expression is evaluated and converted to the nominal subtype; (3) the object is created (constrained according to the result from (2)); (4) the value of the result of (2) is copied (bitwise) into the object (no call to Initialize or Adjust will be made). For example: package Session_Management is type Internet_Session is abstract tagged limited private; function Open (Host: in String) return Internet_Session'Class; ... private type Internet_Session is abstract new Ada.Finalization.Limited_Controlled with null record; end; package body Session_Management is type Simple_Session ( Host_Name_Length: Natural Traceback_Length: Natural) is new Internet_Session with record Host_Name: String(1..Host_Name_Length); Traceback: ?.Node_Array(1..Traceback_Length); Connection: ?.IP_Connection ... end record; function Open (Host: in String) return Internet_Session'Class; begin Open_Connection(Session.Connection,Host); declare Result: Simple_Session := ( Host'Length, Get_Node_Count(Session.Connection), ... ); begin ... return Result; end; exception ... end Open; ... end Session_Management; ... My_Sess: Internet_Session'Class := Open("ftp.cdrom.com"); The advantages of this idea, I think, are: it is a relatively simple change to the Ada standard (although it is still not entirely simple); a function such as 'Open' (call it a 'constructor function' if you wish, but it is really a normal function) can be used to create temporary objects (that are a part of a bigger expression) as well as for intialising objects; it seems to me to be a natural and readily understood way of solving the problem (the only twist being the change in the rules for a limited tagged type); (in common with your propoal) all existing legal Ada source text will remain totally unaffected. It seems to bear some similarility to the new rule for aggregates. My example above also demonstrates the use of an abstract type, which has the advantage that the implementation (of the 'Open' function) can return any type it likes (derived from Internet_Session). The user is happily oblivious to which actual type is returned; the implementation can be changed to return (further) different types according to new requirements without disturbing the user (recompilation or relinking will be required, but no changes to the user's source text). The type could be made concrete, and more details about the type could then be revealed to the user (e.g. the discriminants, the other record components), should that be considered more appropriate. > > I'd prefer making the function solution work. There is a proposal to > > make it possible to initialize limited objects with a function call, > > which would eliminate that objection. > > Yes it definitely would, because a function which result initialises an > unconstructed object is just another name for constructor. However there > would be also questions: > > 1. Will you use ":=" for limited types within a declaration with a problem > to explain what is that, or just "rename"? The use of ":=" would be my preference, since it does not require any change to the syntax of object declarations (RM95 3.3.1). > 2. What to do with "new" for limited types, when a constructor have to be > called? There was never any problem with allocator initialisation, because the result is of an access type (which is always definite). > 3. I suppose that constructors will be allowed for Limited_Controlled only? > So arrays, untagged types, tasks and protected object will remain out of > reach. My suggestion is that the new rules should only apply to object declarations for limited class-wide (tagged) subtypes, because they are illegal now, so we can be certain that existing legal Ada source text will not be affected by the new rules. It may be convenient, but it is not really necessary for protected or task types to be included, because a tagged (record) type can always include a component of the requisite protected or task type. The question does not arise for array types, because they are not (able to be) limited (and so always have assignment defined). Any definite type can be initialised by a procedure to which it is passed as an 'in out' parameter. I think there may be a case for the inclusion of indefinite limited untagged types. Now, what am I missing? :-) -- Nick Roberts ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2002-12-29 18:45 ` Nick Roberts @ 2002-12-30 12:23 ` Dmitry A. Kazakov 2002-12-30 15:14 ` Robert A Duff 0 siblings, 1 reply; 20+ messages in thread From: Dmitry A. Kazakov @ 2002-12-30 12:23 UTC (permalink / raw) Nick Roberts wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:aumu4o$76buu$1@ID-77047.news.dfncis.de... > >> > -- You'd need to carefully explain why solutions based on functions (as >> > Nick Roberts described) are not sufficient; >> >> They cannot work when no assignment exists. Otherwise one should invent a >> sort of pickwickian assignment. > > Yes, Dmitry, but it is this Pickwickian assignment that has been mooted, > and it is necessary for you to demonstrate that your proposal is superior. That's what I am trying to do. (:-)) The problem is not only the limited types. The problem is more general. It is user-construction/destruction for ALL user-defined types. It is user-assignment for ALL non-limited user-defined types. I do not think that people are very happy with Ada.Finalization. It looks like a hack. However, it is an excellent hack because it lets almost everything open for any better solution, should we find it, of course. (:-)) > To be precise, interpreting Randy's suggestion, my suggestion would be for > (Ada be changed in the next revision so that) an object declaration which > includes a subtype indication that denotes a class-wide type whose root > type is limited (tagged) to be permitted to include an (explicit) > initialisation expression, in which case special rules would apply to the > dynamic semantics for the elaboration of the declaration: (1) the subtype > inidication is elaborated; (2) the expression is evaluated and converted > to the nominal subtype; (3) the object is created (constrained according > to the result from (2)); (4) the value of the result of (2) is copied > (bitwise) into the object (no call to Initialize or Adjust will be made). 1. Your proposal is based on a bitwise copy[-constructor] (4). This means that you have to ensure that ANY [limited tagged] object CAN be copied this way. What will you do with the following: type Internet_Session is abstract tagged limited private; type Internet_Session_Ptr is access all Internet_Session'Class; private type Internet_Session is abstract new Ada.Finalization.Limited_Controlled with record Previous : Internet_Session_Ptr; -- In the global list Next : Internet_Session_Ptr; -- of sessions end record; The construction of an Internet_Session inserts it in a global double-linked list of all sessions. Such an object cannot be copied bitwise. To solve this and similar cases you have either to construct objects in-place (my proposal) or to provide Adjust, i.e. a full-sized assignment. 2. Why is it allowed for class-wide types only? What should I do to initialize a specific limited type? 3. Should specific types be allowed to be initialized, what will you do with composite types of them? For instance, let an array of limited types be initialized? It is limited, but alas untagged, so any kind of initialization is impossible, right? > The advantages of this idea, I think, are: it is a relatively simple > change to the Ada standard (although it is still not entirely simple); a > function such as 'Open' (call it a 'constructor function' if you wish, but > it is really a normal function) can be used to create temporary objects > (that are a part of a bigger expression) as well as for intialising > objects; it seems to me to be a natural and readily understood way of > solving the problem (the only twist being the change in the rules for a > limited tagged type); (in common with your propoal) all existing legal Ada > source text will remain totally unaffected. > It seems to bear some similarility to the new rule for aggregates. Is there an AI for the rule? > My example above also demonstrates the use of an abstract type, which has > the advantage that the implementation (of the 'Open' function) can return > any type it likes (derived from Internet_Session). The user is happily > oblivious to which actual type is returned; the implementation can be > changed to return (further) different types according to new requirements > without disturbing the user (recompilation or relinking will be required, > but no changes to the user's source text). The type could be made > concrete, and more details about the type could then be revealed to the > user (e.g. the discriminants, the other record components), should that be > considered more appropriate. > >> > I'd prefer making the function solution work. There is a proposal to >> > make it possible to initialize limited objects with a function call, >> > which would eliminate that objection. >> >> Yes it definitely would, because a function which result initialises an >> unconstructed object is just another name for constructor. However there >> would be also questions: >> >> 1. Will you use ":=" for limited types within a declaration with a >> problem to explain what is that, or just "rename"? > > The use of ":=" would be my preference, since it does not require any > change to the syntax of object declarations (RM95 3.3.1). > >> 2. What to do with "new" for limited types, when a constructor have to be >> called? > > There was never any problem with allocator initialisation, because the > result is of an access type (which is always definite). The problem is that either T'Class'Input or your construction-function creates a stack allocated object. So to allow heap objects as well, the developer of a limited type have to provide two different construction-functions: one to return T, another to return access T. This definitely breaks Ada's symmetry in object allocation models and is a burden for developers. >> 3. I suppose that constructors will be allowed for Limited_Controlled > only? >> So arrays, untagged types, tasks and protected object will remain out of >> reach. > > My suggestion is that the new rules should only apply to object > declarations for limited class-wide (tagged) subtypes, because they are > illegal now, so we can be certain that existing legal Ada source text will > not be affected by the new rules. > > It may be convenient, but it is not really necessary for protected or task > types to be included, because a tagged (record) type can always include a > component of the requisite protected or task type. It is arguable. Access discriminats are very frequent for tasks and protected objects. A construction/destruction of tasks and protected objects could provide a nice way to create the objects referenced by a task during task construction and delete them upon destruction. This would be much more better design than to pack the task into a record. Another advantage would be that the entry points of a task could be exposed as entry points, not as proxy subroutine calls, so a user of a task might directly use timed entry calls etc on them. > The question does not > arise for array types, because they are not (able to be) limited (and so > always have assignment defined). Array of limited components is limited. > Any definite type can be initialised by a > procedure to which it is passed as an 'in out' parameter. I think there > may be a case for the inclusion of indefinite limited untagged types. You also can view it another way: we can initialize by either a function [+copying] or a procedure [in-place]. For functions we have a way to do it within a declaration: X : My_Type := expression (<args>); For procedures we have nothing (except the predefined constructor with the values of the discriminants as the parameters). You can consider my proposal as an attempt to close this gap: X : My_Type (<args>); -- Happy New Year, Dmitry A. Kazakov www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2002-12-30 12:23 ` Dmitry A. Kazakov @ 2002-12-30 15:14 ` Robert A Duff 2002-12-31 13:02 ` Dmitry A. Kazakov 0 siblings, 1 reply; 20+ messages in thread From: Robert A Duff @ 2002-12-30 15:14 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: [various stuff about constructors] You should look at AI-287, which says that aggregates are allowed for limited types. Also, AI-318 which allows constructor functions for limited types. I don't see any reason to add a new feature. It seems to me that the problem is that you can't write a constructor function for a limited type (i.e., a function that creates a new object local to itself, and returns it), and you can't initialize limited objects. So the solution is simply to remove these restrictions. To build on your example: type Internet_Session is abstract new Ada.Finalization.Limited_Controlled with record Count: Integer; Previous : Internet_Session_Ptr; -- In the global list Next : Internet_Session_Ptr; -- of sessions end record; type Session_Ptr is access Internet_Session; function Make_Session(Count: Integer) return Internet_Session is Result: Internet_Session := (Count => Count, Previous | Next => <>); -- use default "null" begin Result.Previous := ...; ... return Result; end Make_Session; Then clients could initialize stack and heap objects by calling the constructor function: This_Session: Internet_Session := Make_Session(Count => 123); That_Session: Session_Ptr := new Internet_Session'(Make_Session(123)); The above aggregate is currently illegal, and the "return Result" won't work because it violates accessibility rules. And the two initial values are currently illegal. The two AI's would allow the above. I'm glossing over some details that are probably documented either in the AI's, or in the minutes of some ARG meeting. The main point is that limited types are *too* limited. Their purpose is to prevent *copying* (assignment statements). But initialization is *not* like an assignment, it need not involve a copy, and it should be allowed for limited types. > The problem is not only the limited types. The problem is more general. It > is user-construction/destruction for ALL user-defined types. I don't understand that. Functions work fine as constructors for non-limited types. The problem is for limited types. As for destructors, well Ada.Finalization works OK, although it has some flaws. Those flaws seem unrelated to the constructor-for-limited-type issue. >...It is > user-assignment for ALL non-limited user-defined types. That seems like a totally unrelated issue. > 1. Your proposal is based on a bitwise copy[-constructor]... The whole point of limited types is that they *cannot* be copied. Access discriminants don't work if copies are made. Locks in protected objects don't work. Various data structures involving pointers don't work, as your example showed. Etc. It is important that the Result object of Make_Session above be built in place, in its final destination. It cannot be built on the local stack frame and then moved elsewhere. This requires that the compiler pass in a Storage_Pool object telling it where to allocate. A special Storage_Pool could mean "on the stack", but in the That_Session initialization above, the object must be allocated in the storage pool belonging to Session_Ptr, which could be user defined. - Bob ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2002-12-30 15:14 ` Robert A Duff @ 2002-12-31 13:02 ` Dmitry A. Kazakov 2003-01-01 0:28 ` Randy Brukardt 0 siblings, 1 reply; 20+ messages in thread From: Dmitry A. Kazakov @ 2002-12-31 13:02 UTC (permalink / raw) Robert A Duff wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes: > > [various stuff about constructors] > > I don't see any reason to add a new feature. It seems to me that the > problem is that you can't write a constructor function for a limited > type (i.e., a function that creates a new object local to itself, and > returns it), and you can't initialize limited objects. So the solution > is simply to remove these restrictions. To build on your example: > > type Internet_Session is > abstract new Ada.Finalization.Limited_Controlled with > record > Count: Integer; > Previous : Internet_Session_Ptr; -- In the global list > Next : Internet_Session_Ptr; -- of sessions > end record; > > type Session_Ptr is access Internet_Session; > > function Make_Session(Count: Integer) return Internet_Session is > Result: Internet_Session > := (Count => Count, > Previous | Next => <>); -- use default "null" > begin > Result.Previous := ...; > ... > return Result; > end Make_Session; > > Then clients could initialize stack and heap objects by calling the > constructor function: > > This_Session: Internet_Session := Make_Session(Count => 123); > That_Session: Session_Ptr := new Internet_Session'(Make_Session(123)); > > The above aggregate is currently illegal, and the "return Result" won't > work because it violates accessibility rules. And the two initial > values are currently illegal. The two AI's would allow the above. I do not understand how could this work. According to Nick Roberts the result of Make_Session is copied as-is. But the object returned by Make_Session has to be pointed from OUTSIDE: function Make_Session (Count: Integer) return Internet_Session is Result : Internet_Session (Count); begin -- -- Place the new object in front of the list -- Result.Previous := Global_List_Header'Unchecked_Access; Result.Next := Global_List_Header.Next; Global_List_Header.Next := Result'Unchecked_Access; Global_List_Header.Next.Previous := Result'Unchecked_Access; return Result; end Make_Session; How to fix this? > I'm glossing over some details that are probably documented either in > the AI's, or in the minutes of some ARG meeting. > > The main point is that limited types are *too* limited. Their purpose > is to prevent *copying* (assignment statements). But initialization is > *not* like an assignment, it need not involve a copy, and it should be > allowed for limited types. Absolutely. The rest is easy, it is just to explain why "not-copying" have to be spelled as copying: X : Obj := Func (<args>); Isn't it misleading? We already have functions that are not functions. Now we will have even more non-functions, and also non-results of non-assignments. I wonder why all this is not counted as a "new feature"? >> The problem is not only the limited types. The problem is more general. >> It is user-construction/destruction for ALL user-defined types. > > I don't understand that. Functions work fine as constructors for > non-limited types. The problem is for limited types. There should be a regular way to construct/destruct ANY user-defined type including class-wides. There should be a way to ensure that only the user-defined constructors are applied. The proposal does not respond this. (<>) as the discriminants does not work for arrays, simple types. It also has nasty consequences. Class-wide objects cannot be constructed. Destruction is impossible as before if the type was not derived from a controlled type. If I derive from Root_Stream_Type then I cannot define neither initialization nor finalization for my stream object. > As for destructors, well Ada.Finalization works OK, although it has some > flaws. Those flaws seem unrelated to the constructor-for-limited-type > issue. > >>...It is >> user-assignment for ALL non-limited user-defined types. > > That seems like a totally unrelated issue. My proposal was to provide true constructors. If you have constructors, assignment comes for free. Limited types was just an example. >> 1. Your proposal is based on a bitwise copy[-constructor]... > > The whole point of limited types is that they *cannot* be copied. > Access discriminants don't work if copies are made. Locks in protected > objects don't work. Various data structures involving pointers don't > work, as your example showed. Etc. > > It is important that the Result object of Make_Session above be built in > place, in its final destination. It cannot be built on the local stack > frame and then moved elsewhere. This requires that the compiler pass in > a Storage_Pool object telling it where to allocate. A special > Storage_Pool could mean "on the stack", but in the That_Session > initialization above, the object must be allocated in the storage pool > belonging to Session_Ptr, which could be user defined. So it seems that your view differs from Nick's one. OK. 1. How will the complier solve this puzzle: function Make_Session (Count: Integer) return Internet_Session is Result1 : Internet_Session (Count); Result2 : Internet_Session (Count); begin ... -- construct both -- now drop one of them if <something> then return Result1; else return Result2; end if; end Make_Session; Then from your description follows that functions returning limited objects will be generated in some special way. All of them? What if I rename the result of such Pickwickean function? Where will it be allocated? 2. How a task type will be constructed? 3. Let Internet_Session is a component of another limited type with another construction function. How do I call Make_Session from that function? -- Happy New Year, Dmitry A. Kazakov www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2002-12-31 13:02 ` Dmitry A. Kazakov @ 2003-01-01 0:28 ` Randy Brukardt 2003-01-01 14:13 ` Dmitry A. Kazakov 0 siblings, 1 reply; 20+ messages in thread From: Randy Brukardt @ 2003-01-01 0:28 UTC (permalink / raw) Dmitry A. Kazakov wrote in message ... >I do not understand how could this work. According to Nick Roberts the >result of Make_Session is copied as-is. But the object returned by >Make_Session has to be pointed from OUTSIDE: Right. That's the "new feature" in the proposal. And it's relatively cheap, since compilers typically handle composite functions by creating memory at the point of the call and passing it in to the function with a hidden parameter. Might as well make that explicit. >function Make_Session (Count: Integer) return Internet_Session is > Result : Internet_Session (Count); >begin >-- >-- Place the new object in front of the list >-- > Result.Previous := Global_List_Header'Unchecked_Access; > Result.Next := Global_List_Header.Next; > Global_List_Header.Next := Result'Unchecked_Access; > Global_List_Header.Next.Previous := Result'Unchecked_Access; > return Result; >end Make_Session; > >How to fix this? The syntax of the proposal wasn't determined, but the basic idea was something like: function Make_Session (Count: Integer) return Internet_Session is Result : return Internet_Session (Count); begin -- -- Place the new object in front of the list -- Result.Previous := Global_List_Header'Unchecked_Access; Result.Next := Global_List_Header.Next; Global_List_Header.Next := Result'Unchecked_Access; Global_List_Header.Next.Previous := Result'Unchecked_Access; return Result; end Make_Session; With this declaration, "Result" is passed in implicitly from the caller for a limited type, and it must be returned. Returning some other object or none at all is an error. There can be only one such object (only one object can be passed in). >... >Absolutely. The rest is easy, it is just to explain why "not-copying" have >to be spelled as copying: > >X : Obj := Func (<args>); > >Isn't it misleading? We already have functions that are not functions. Now >we will have even more non-functions, and also non-results of >non-assignments. I wonder why all this is not counted as a "new feature"? Because we already have it in Ada 95 for aggregates (see AI-83 and the corrigendum). So we're just extending what's already available. >... >Class-wide objects cannot be constructed. I don't get this at all. What's wrong with: A : T'Class := Construct_Class (...); where the definition of Construct_Class uses some mechanism to dispatch properly. (Note that you virtually always end up with a case statement in Construct_Class, because you somehow have to get from a set of arbitrary parameter values to some tag. There is no automatic way to do that in any programming language that I know of. If you're really clever, you can use T'Class'Input to do this, but its a real hack.) Randy. ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2003-01-01 0:28 ` Randy Brukardt @ 2003-01-01 14:13 ` Dmitry A. Kazakov 2003-01-02 19:44 ` Randy Brukardt 0 siblings, 1 reply; 20+ messages in thread From: Dmitry A. Kazakov @ 2003-01-01 14:13 UTC (permalink / raw) Randy Brukardt wrote: > Dmitry A. Kazakov wrote in message ... >>I do not understand how could this work. According to Nick Roberts the >>result of Make_Session is copied as-is. But the object returned by >>Make_Session has to be pointed from OUTSIDE: > > Right. That's the "new feature" in the proposal. And it's relatively > cheap, since compilers typically handle composite functions by creating > memory at the point of the call and passing it in to the function with a > hidden parameter. Might as well make that explicit. > >>function Make_Session (Count: Integer) return Internet_Session is >> Result : Internet_Session (Count); >>begin >>-- >>-- Place the new object in front of the list >>-- >> Result.Previous := Global_List_Header'Unchecked_Access; >> Result.Next := Global_List_Header.Next; >> Global_List_Header.Next := Result'Unchecked_Access; >> Global_List_Header.Next.Previous := Result'Unchecked_Access; >> return Result; >>end Make_Session; >> >>How to fix this? > > The syntax of the proposal wasn't determined, but the basic idea was > something like: > > function Make_Session (Count: Integer) return Internet_Session is > Result : return Internet_Session (Count); > begin > -- > -- Place the new object in front of the list > -- > Result.Previous := Global_List_Header'Unchecked_Access; > Result.Next := Global_List_Header.Next; > Global_List_Header.Next := Result'Unchecked_Access; > Global_List_Header.Next.Previous := Result'Unchecked_Access; > return Result; > end Make_Session; > > With this declaration, "Result" is passed in implicitly from the caller > for a limited type, and it must be returned. Returning some other object > or none at all is an error. There can be only one such object (only one > object can be passed in). I see, the old FORTRAN's idea to expose the result as a variable! I am very impressed, that such a thing managed to come through. (:-)) I suppose it will be allowed for all types? Then it would be interesting to know how it will work with by-value types: function Set_Bit (Value : Boolean) return Boolean is Result : return Boollean := Value; begin return Result; end Set_Bit; ... provided that the result is an element of a packed array. Somehow, somewhere the "in-place" requirement have to be relaxed. I wonder how many pages one will add to RM. Then there will be much work required to deal with things like: function Make_Session (Count: Integer) return Internet_Session is begin declare Result1 : return Internet_Session (24); begin if ... then return Result1; end if; end; declare Result2 : return Internet_Session (1000); - another size begin if ... then return Result2; end if; end; declare Result3 : Internet_Session (10); -- I forgot "return" begin return Result3; end; end Make_Session; I am not sure, but to determine whether there is only one result, could be equivalent to halting problem. So some sort of run-time support will be well required to determine this. Or will it be classified as one more bound error? >>... >>Absolutely. The rest is easy, it is just to explain why "not-copying" > have >>to be spelled as copying: >> >>X : Obj := Func (<args>); >> >>Isn't it misleading? We already have functions that are not functions. > Now >>we will have even more non-functions, and also non-results of >>non-assignments. I wonder why all this is not counted as a "new > feature"? > > Because we already have it in Ada 95 for aggregates (see AI-83 and the > corrigendum). So we're just extending what's already available. > >>... >>Class-wide objects cannot be constructed. > > I don't get this at all. What's wrong with: > > A : T'Class := Construct_Class (...); > > where the definition of Construct_Class uses some mechanism to dispatch > properly. > > (Note that you virtually always end up with a case statement in > Construct_Class, because you somehow have to get from a set of arbitrary > parameter values to some tag. There is no automatic way to do that in > any programming language that I know of. If you're really clever, you > can use T'Class'Input to do this, but its a real hack.) Yes, this is why my proposal offers a way to write things working like T'Class'Input. When T'Class is created it (1) calls a user-defined function to determine Tag, then (2) it dispatches to a user-defined function to get constraints and finally (3) it dispatches to a user-defined Initialize. As I understood from your and other's responses the new call syntax "Func (...) use Tag" would give 1. 2-3 would be covered by new "FORTRAN" functions and an ability to "assign" what cannot be assigned. So there indeed will be a way to write Construct_Class without a case statement: function Construct_Class (...) return T'Class is begin -- figure out the tag declare Result : return T'Class := Construct_Object (...) use <tag>; begin retrun Result; end; end Construct_Class; function Construct_Object (...) return T is begin -- figure out the discriminants declare Result : return T := T (<discriminants>); begin -- initialize other Result's fields retrun Result; end; end Construct_Object; Formally it should work. (?) -- Regards, Dmitry A. Kazakov www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2003-01-01 14:13 ` Dmitry A. Kazakov @ 2003-01-02 19:44 ` Randy Brukardt 2003-01-03 13:21 ` Dmitry A. Kazakov 2003-01-03 20:50 ` Robert A Duff 0 siblings, 2 replies; 20+ messages in thread From: Randy Brukardt @ 2003-01-02 19:44 UTC (permalink / raw) Dmitry A. Kazakov wrote in message ... >Randy Brukardt wrote: >I suppose it will be allowed for all types? Then it would be interesting to >know how it will work with by-value types: > >function Set_Bit (Value : Boolean) return Boolean is > Result : return Boollean := Value; >begin > return Result; >end Set_Bit; > >... provided that the result is an element of a packed array. Somehow, >somewhere the "in-place" requirement have to be relaxed. I wonder how many >pages one will add to RM. I don't know, it wasn't my idea. I'd suspect that by-copy types would be handled by-copy, thus you couldn't tell if it was in-place or not. >Then there will be much work required to deal with things like: > >function Make_Session (Count: Integer) return Internet_Session is >begin > declare > Result1 : return Internet_Session (24); > begin > if ... then > return Result1; > end if; > end; > declare > Result2 : return Internet_Session (1000); - another size > begin > if ... then > return Result2; > end if; > end; > declare > Result3 : Internet_Session (10); -- I forgot "return" > begin > return Result3; > end; >end Make_Session; No, this is illegal. There can only be one result object declared. And if there is one, returning anything else is illegal. So the declaration of Result2 and the return of Result3 are illegal. >I am not sure, but to determine whether there is only one result, could be >equivalent to halting problem. So some sort of run-time support will be >well required to determine this. Or will it be classified as one more bound >error? No, these are all compile-time checks. No run-time penalty at all. Indeed, this is cheaper than Ada 95, where there is a run-time check that the result of a function is "accessible". (And that check prevents doing much useful with the function.) Randy. ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2003-01-02 19:44 ` Randy Brukardt @ 2003-01-03 13:21 ` Dmitry A. Kazakov 2003-01-03 19:29 ` Randy Brukardt 2003-01-03 20:50 ` Robert A Duff 1 sibling, 1 reply; 20+ messages in thread From: Dmitry A. Kazakov @ 2003-01-03 13:21 UTC (permalink / raw) Randy Brukardt wrote: > No, this is illegal. There can only be one result object declared. And > if there is one, returning anything else is illegal. So the declaration > of Result2 and the return of Result3 are illegal. > >>I am not sure, but to determine whether there is only one result, could >>be equivalent to halting problem. So some sort of run-time support will >>be well required to determine this. Or will it be classified as one more >>bound error? > > No, these are all compile-time checks. No run-time penalty at all. And this? function Make_Session (Count: Integer) return Internet_Session is begin for Index in Integer'Range loop declare Result : return Internet_Session (Index); begin if HALT (x) then return Result; end if; end; end loop; end Make_Session; I really dislike this construction. The single possible advantage of this feature I see, is that maybe, it could allow results for entry points, maybe not. -- Regards, Dmitry A. Kazakov www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2003-01-03 13:21 ` Dmitry A. Kazakov @ 2003-01-03 19:29 ` Randy Brukardt 0 siblings, 0 replies; 20+ messages in thread From: Randy Brukardt @ 2003-01-03 19:29 UTC (permalink / raw) Dmitry A. Kazakov wrote in message ... >Randy Brukardt wrote: > >> No, this is illegal. There can only be one result object declared. And >> if there is one, returning anything else is illegal. So the declaration >> of Result2 and the return of Result3 are illegal. >> >>>I am not sure, but to determine whether there is only one result, could >>>be equivalent to halting problem. So some sort of run-time support will >>>be well required to determine this. Or will it be classified as one more >>>bound error? >> >> No, these are all compile-time checks. No run-time penalty at all. > >And this? > >function Make_Session (Count: Integer) return Internet_Session is >begin > for Index in Integer'Range loop > declare > Result : return Internet_Session (Index); > begin > if HALT (x) then > return Result; > end if; > end; > end loop; >end Make_Session; That would raise Program_Error if Halt was never true, because there was no return in the function. That of course is the same as the current Ada rule. Randy. ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2003-01-02 19:44 ` Randy Brukardt 2003-01-03 13:21 ` Dmitry A. Kazakov @ 2003-01-03 20:50 ` Robert A Duff 2003-01-04 12:53 ` Dmitry A. Kazakov 1 sibling, 1 reply; 20+ messages in thread From: Robert A Duff @ 2003-01-03 20:50 UTC (permalink / raw) > Dmitry A. Kazakov wrote in message ... > >function Make_Session (Count: Integer) return Internet_Session is > >begin > > for Index in Integer'Range loop > > declare > > Result : return Internet_Session (Index); > > begin > > if HALT (x) then > > return Result; > > end if; > > end; > > end loop; > >end Make_Session; There were various rules, such as disallowing the "return object" in a nested block. I don't remember what they all were. Look at the AI and/or ARG minutes if you care. The idea is basically just like in Pascal, where there's a special return object, conceptually declared as a local of the function. To specify what to return, assign into this special object. Or, if limited, *initialize* this object. Various syntaxes were discussed for defining the special object -- including allowing it to be implicitly declared and/or denoting it via an attribute. Pascal uses the name of the function to denote this special object. That's a bad idea, because it's ambiguous with a function call of zero parameters. IMHO, having a special object is better than the traditional Ada way (a return statement), and I wish the language had used this mechanism in the first place. This is because a return statement causes a transfer of control. It's not quite as bad as a goto, but it seems to me that one should not be required to do a jump when it's not needed. The usual (and easiest to understand) case is when the "return" is actually at the end of execution anyway, and no jump is needed. When a jump-to-end *is* needed, that case should look "special" in the code. Combining "set result" with "jump to end" is a bad idea. At least, it's a bad idea if you're forced to use it. I wouldn't mind making "return X" be a shorthand for "<result obj> := X; return;". Also, I find myself declaring a "Result" object anyway, quite often. It would be convenient to get it for free. - Bob ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2003-01-03 20:50 ` Robert A Duff @ 2003-01-04 12:53 ` Dmitry A. Kazakov 0 siblings, 0 replies; 20+ messages in thread From: Dmitry A. Kazakov @ 2003-01-04 12:53 UTC (permalink / raw) Robert A Duff wrote: >> Dmitry A. Kazakov wrote in message ... > >> >function Make_Session (Count: Integer) return Internet_Session is >> >begin >> > for Index in Integer'Range loop >> > declare >> > Result : return Internet_Session (Index); >> > begin >> > if HALT (x) then >> > return Result; >> > end if; >> > end; >> > end loop; >> >end Make_Session; > > There were various rules, such as disallowing the "return object" in a > nested block. I don't remember what they all were. Look at the AI > and/or ARG minutes if you care. > > The idea is basically just like in Pascal, where there's a special > return object, conceptually declared as a local of the function. We will have numerous problems with combining variable-sized objects and by-reference parameter passing. This only would add unnecessary complexity to the language. It also would raise a question, why OUT parameters of procedures and entry points cannot act this way. > To specify what to return, assign into this special object. > Or, if limited, *initialize* this object. The problem is to ensure that this would happen exactly once, for *some* of result types. > Various syntaxes were discussed for defining the special object -- > including allowing it to be implicitly declared and/or denoting it via > an attribute. > > Pascal uses the name of the function to denote this special object. > That's a bad idea, because it's ambiguous with a function call > of zero parameters. > > IMHO, having a special object is better than the traditional Ada way (a > return statement), and I wish the language had used this mechanism in > the first place. This is because a return statement causes a transfer > of control. But exactly this ensures that you cannot do it twice. > It's not quite as bad as a goto, but it seems to me that > one should not be required to do a jump when it's not needed. The usual > (and easiest to understand) case is when the "return" is actually at the > end of execution anyway, and no jump is needed. When a jump-to-end *is* > needed, that case should look "special" in the code. Combining "set > result" with "jump to end" is a bad idea. I disagree. IMO it is the basic idea of a subroutine having a result. If you want to separate "set" and "return", there are procedures for this. > At least, it's a bad idea if you're forced to use it. I wouldn't mind > making "return X" be a shorthand for "<result obj> := X; return;". But in a comparable case: "X := Y;" is a shorthand for "Finalize (X); Copy_constructor (X, Y);" the decision made is directly opposite! > Also, I find myself declaring a "Result" object anyway, quite often. > It would be convenient to get it for free. -- Regards, Dmitry A. Kazakov www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2002-12-29 13:43 ` Dmitry A. Kazakov 2002-12-29 18:45 ` Nick Roberts @ 2003-01-01 0:54 ` Randy Brukardt 2003-01-01 14:13 ` Dmitry A. Kazakov 1 sibling, 1 reply; 20+ messages in thread From: Randy Brukardt @ 2003-01-01 0:54 UTC (permalink / raw) Dmitry A. Kazakov wrote in message ... >Randy Brukardt wrote: >> The basic idea is to somehow figure a value of Ada.Tags.Tag, then use it >> to control dispatching on a function that returns the correct kind of >> object. This requires a special call: >> >> Obj : T'Class := Func (<args>) use Tag_Value; > >The question is, will dispatching without an object be useful in cases other >than construction? Yes, I think so. Two other examples have come up: -- Calling the parent operation (which Ada does not have any convinient syntax for - currently you have to name the parent explicitly, which is a source of bugs); -- Handling "least common denominator" comparison. These aren't wildly compelling, admittedly. > This proposal requires new syntax. Mine > > Obj : T'Class (<args>); > >requires only new attributes. Adding syntax to a modern compiler is cheap. Either its table driven or recursive descent; both are quite simple. OTOH, semantics still have to be implemented by hand. The concern about syntax comes from the cost of modifying other tools. If there is a way to avoid new syntax without changing the semantics much, then that is to be preferred. But if we're talking about implementing simple syntax and semantics compared to implementing complex semantics only, I'll take a simple syntax change any day. And your proposal comes under complex semantics, and would be very expensive to implement in Janus/Ada. Take your "only attributes" claim. In Janus/Ada (and probably other compilers), attributes are completely manually implemented. Every attribute is a bundle of special case code. It's especially expensive for attributes which can be specified. For instance, stream attributes (which these resemble) have dedicated components in type records which hold the current definition. Type records are initialized in a large variety of places in the compiler, and all of these would need to be updated (Ada 2005 will help that somewhat, but of course the compiler is implemented in Ada 95). So your "only new attributes" proposal would take many times more work to implement. Indeed, it would be impossible to implement without a wholesale restructuring of the compiler. Secondly, you are suggesting that somehow: Obj : T'Class (<args>); is somehow the same as Obj : T'Class (<discriminants>); Essentially, you are saying that a discriminant constraint has the same semantics as a parameter list. But that clearly isn't true in the RM as written. The rules for these are defined in different places and are subtly different. Changing them to be the same would be very difficult without introducing dangerous incompatibilities. While that arguably might be better for new users, it could be a disaster for existing Ada users (and code). And it certainly would be a heck of a lot of work. >Though it is another question, I think that there is a need to have >something like user-defined anonymous access types, with the operations >becoming primitive for the pointed type. But it is probably far more >difficult than just constructors. Well, there is a proposal for "named anonymous access types" (AI-230). But the whole idea of a named anonymous type is an oxymoron, and that turns off a lot of people. And there are quite a few semantic problems with the proposal. So I wouldn't expect that proposal to be included in Ada 2005. Randy. ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2003-01-01 0:54 ` Randy Brukardt @ 2003-01-01 14:13 ` Dmitry A. Kazakov 2003-01-02 19:36 ` Randy Brukardt 0 siblings, 1 reply; 20+ messages in thread From: Dmitry A. Kazakov @ 2003-01-01 14:13 UTC (permalink / raw) Randy Brukardt wrote: > Dmitry A. Kazakov wrote in message ... >>Randy Brukardt wrote: >>> The basic idea is to somehow figure a value of Ada.Tags.Tag, then use > it >>> to control dispatching on a function that returns the correct kind of >>> object. This requires a special call: >>> >>> Obj : T'Class := Func (<args>) use Tag_Value; >> >>The question is, will dispatching without an object be useful in cases > other >>than construction? Why this sort of syntax was chosen? I think more consequently in Ada's spirit would be sort of: Obj : T'Class (Tag_Value) := Func (<args>); Isn't tag a natural constraint/discriminant of T'Class? > Yes, I think so. Two other examples have come up: > -- Calling the parent operation (which Ada does not have any > convinient syntax for - currently you have to name the parent > explicitly, which is a source of bugs); Yes it would be nice. However you will need something to get a parent's type tag from the type tag. > -- Handling "least common denominator" comparison. BTW. Why not to allow tag comparisons >, >=, <, <=? I mean: function ">" (Left, Right : Tag) return Boolean; A>B if B is a descendant of A, but not of A type. > These aren't wildly compelling, admittedly. > >> This proposal requires new syntax. Mine >> >> Obj : T'Class (<args>); >> >>requires only new attributes. > > Adding syntax to a modern compiler is cheap. Either its table driven or > recursive descent; both are quite simple. OTOH, semantics still have to > be implemented by hand. The concern about syntax comes from the cost of > modifying other tools. If there is a way to avoid new syntax without > changing the semantics much, then that is to be preferred. But if we're > talking about implementing simple syntax and semantics compared to > implementing complex semantics only, I'll take a simple syntax change > any day. And your proposal comes under complex semantics, and would be > very expensive to implement in Janus/Ada. > > Take your "only attributes" claim. In Janus/Ada (and probably other > compilers), attributes are completely manually implemented. Every > attribute is a bundle of special case code. It's especially expensive > for attributes which can be specified. For instance, stream attributes > (which these resemble) have dedicated components in type records which > hold the current definition. Type records are initialized in a large > variety of places in the compiler, and all of these would need to be > updated (Ada 2005 will help that somewhat, but of course the compiler is > implemented in Ada 95). So your "only new attributes" proposal would > take many times more work to implement. Indeed, it would be impossible > to implement without a wholesale restructuring of the compiler. You know it better, of course. However, in a long term perspective most of attributes have to become just dispatching operations. Should all types be tagged in the sense that T'Class exists and one can derive with overriding its operations, then implementing of attributes would be much more easier. > Secondly, you are suggesting that somehow: > Obj : T'Class (<args>); > is somehow the same as > Obj : T'Class (<discriminants>); > > Essentially, you are saying that a discriminant constraint has the same > semantics as a parameter list. We could turn it another way: the semantics of the parameter list in a constraint is same as one of a discriminant constraint. > But that clearly isn't true in the RM as > written. The rules for these are defined in different places and are > subtly different. Changing them to be the same would be very difficult > without introducing dangerous incompatibilities. While that arguably > might be better for new users, it could be a disaster for existing Ada > users (and code). And it certainly would be a heck of a lot of work. > >>Though it is another question, I think that there is a need to have >>something like user-defined anonymous access types, with the operations >>becoming primitive for the pointed type. But it is probably far more >>difficult than just constructors. > > Well, there is a proposal for "named anonymous access types" (AI-230). > But the whole idea of a named anonymous type is an oxymoron, and that > turns off a lot of people. And there are quite a few semantic problems > with the proposal. So I wouldn't expect that proposal to be included in > Ada 2005. Sigh. This could be a real breakthrough for OO programming and also an excellent response to GC-obsessed folks. -- Regards, Dmitry A. Kazakov www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2003-01-01 14:13 ` Dmitry A. Kazakov @ 2003-01-02 19:36 ` Randy Brukardt 2003-01-03 13:20 ` Dmitry A. Kazakov 0 siblings, 1 reply; 20+ messages in thread From: Randy Brukardt @ 2003-01-02 19:36 UTC (permalink / raw) Dmitry A. Kazakov wrote in message ... >Randy Brukardt wrote: >> Dmitry A. Kazakov wrote in message ... >>>Randy Brukardt wrote: >>>> The basic idea is to somehow figure a value of Ada.Tags.Tag, then use >> it >>>> to control dispatching on a function that returns the correct kind of >>>> object. This requires a special call: >>>> >>>> Obj : T'Class := Func (<args>) use Tag_Value; >>> >>>The question is, will dispatching without an object be useful in cases other >>>than construction? > >Why this sort of syntax was chosen? I chose it only because it has a natural, cheap, and obvious implementation. I was hoping someone would have a better suggestion (but that turned out to be "kill the idea"). >I think more consequently in Ada's >spirit would be sort of: > > Obj : T'Class (Tag_Value) := Func (<args>); > >Isn't tag a natural constraint/discriminant of T'Class? Yes, but this doesn't work, because you don't know the discriminants for the object. Indeed, you don't even know how many there are or their types (since you can change the discriminants on extensions, including adding or removing them), so you can't even give them if you want to. People would have prefered this sort of solution, but it just doesn't work. >> Yes, I think so. Two other examples have come up: >> -- Calling the parent operation (which Ada does not have any >> convinient syntax for - currently you have to name the parent >> explicitly, which is a source of bugs); > >Yes it would be nice. However you will need something to get a parent's type >tag from the type tag. I proposed an attribute for that purpose: T'Parent_Tag. For Janus/Ada, the parent tag is stored in every tag, so this operation is very cheap. However, there were concerns about the fact that there are potentially two (related) parents for every type: the parent of the partial view and the parent of the full view. I think the attribute has to go to the parent of the full view, and since it is dynamic, the "privateness breaking" of that is not a real concern. >> -- Handling "least common denominator" comparison. > >BTW. Why not to allow tag comparisons >, >=, <, <=? I mean: > > function ">" (Left, Right : Tag) return Boolean; > >A>B if B is a descendant of A, but not of A type. These certainly are allowed if you write them yourself. Even if you had them, you still need a way to control the dispatching based on the result (once you've figured out the LCD). Randy. ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Proposal: Constructors, Assignment [LONG] 2003-01-02 19:36 ` Randy Brukardt @ 2003-01-03 13:20 ` Dmitry A. Kazakov 0 siblings, 0 replies; 20+ messages in thread From: Dmitry A. Kazakov @ 2003-01-03 13:20 UTC (permalink / raw) Randy Brukardt wrote: > Dmitry A. Kazakov wrote in message ... >>Randy Brukardt wrote: >>> Dmitry A. Kazakov wrote in message ... >>>>Randy Brukardt wrote: >>>>> The basic idea is to somehow figure a value of Ada.Tags.Tag, then > use >>> it >>>>> to control dispatching on a function that returns the correct kind > of >>>>> object. This requires a special call: >>>>> >>>>> Obj : T'Class := Func (<args>) use Tag_Value; >>>> >>>>The question is, will dispatching without an object be useful in > cases other >>>>than construction? >> >>Why this sort of syntax was chosen? > > I chose it only because it has a natural, cheap, and obvious > implementation. I was hoping someone would have a better suggestion (but > that turned out to be "kill the idea"). > >>I think more consequently in Ada's >>spirit would be sort of: >> >> Obj : T'Class (Tag_Value) := Func (<args>); >> >>Isn't tag a natural constraint/discriminant of T'Class? > > Yes, but this doesn't work, because you don't know the discriminants for > the object. I need not know them. The call to Func has to be dispatching, so the function will return all discriminants. It should work exactly as Obj : T'Class := Func (<args>) use Tag_Value; > Indeed, you don't even know how many there are or their > types (since you can change the discriminants on extensions, including > adding or removing them), so you can't even give them if you want to. Yes, so the type T'Class (Tag_Value) should be made indefinite to prevent object declarations like: Obj : T'Class (Tag_Value); Or do you think that things like: procedure Foo1 (X : T'Class) is procedure Foo2 (Y : T'Class (X'Tag)) is ... could become a problem? >>BTW. Why not to allow tag comparisons >, >=, <, <=? I mean: >> >> function ">" (Left, Right : Tag) return Boolean; >> >>A>B if B is a descendant of A, but not of A type. > > These certainly are allowed if you write them yourself. How? I think they should be predefined for all tags along a path in a type tree. If they of different paths, Constraint_Error have to propagate. > Even if you had > them, you still need a way to control the dispatching based on the > result (once you've figured out the LCD). Not always. I have in mind simulation of multiple dispatch for binary operations. I could declare one as: function "+" (Left : T; Right : T'Class) return T'Class; Should I have "<=" for tags to figure out which argument is closer to T an implementation could be very straightforward: type New_T is new T with private; function "+" (Left : New_T; Right : T'Class) return T'Class is begin if Right'Tag <= New_T'Tag then -- Right is closer to T than Left or same as Left declare Result : New_T := ...; begin ... -- Sum a New_T and one of its ancestors return Result; end; else -- Right is more closer to T than Left return Right = Left; -- Dispatch on Right end if; end "="; There is a work-around using "in", but it is nasty because it requires a hidden parameter to prevent infinite recursion. -- Regards, Dmitry A. Kazakov www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2003-01-04 12:53 UTC | newest] Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2002-12-24 11:16 Proposal: Constructors, Assignment [LONG] Dmitry A. Kazakov 2002-12-26 22:11 ` Nick Roberts 2002-12-27 17:43 ` Dmitry A. Kazakov 2002-12-27 20:17 ` Randy Brukardt 2002-12-29 13:43 ` Dmitry A. Kazakov 2002-12-29 18:45 ` Nick Roberts 2002-12-30 12:23 ` Dmitry A. Kazakov 2002-12-30 15:14 ` Robert A Duff 2002-12-31 13:02 ` Dmitry A. Kazakov 2003-01-01 0:28 ` Randy Brukardt 2003-01-01 14:13 ` Dmitry A. Kazakov 2003-01-02 19:44 ` Randy Brukardt 2003-01-03 13:21 ` Dmitry A. Kazakov 2003-01-03 19:29 ` Randy Brukardt 2003-01-03 20:50 ` Robert A Duff 2003-01-04 12:53 ` Dmitry A. Kazakov 2003-01-01 0:54 ` Randy Brukardt 2003-01-01 14:13 ` Dmitry A. Kazakov 2003-01-02 19:36 ` Randy Brukardt 2003-01-03 13:20 ` Dmitry A. Kazakov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox