From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.9 required=5.0 tests=BAYES_00,FORGED_GMAIL_RCVD, FREEMAIL_FROM autolearn=no autolearn_force=no version=3.4.4 X-Google-Thread: 103376,61e9062c1f23b9d5,start X-Google-Attributes: gid103376,public,usenet X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!postnews.google.com!i38g2000prf.googlegroups.com!not-for-mail From: Maciej Sobczak Newsgroups: comp.lang.ada Subject: Reconsidering assignment Date: Wed, 06 Jun 2007 14:33:50 -0700 Organization: http://groups.google.com Message-ID: <1181165630.012508.55290@i38g2000prf.googlegroups.com> NNTP-Posting-Host: 85.1.248.150 Mime-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" X-Trace: posting.google.com 1181165630 30383 127.0.0.1 (6 Jun 2007 21:33:50 GMT) X-Complaints-To: groups-abuse@google.com NNTP-Posting-Date: Wed, 6 Jun 2007 21:33:50 +0000 (UTC) User-Agent: G2/1.0 X-HTTP-UserAgent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4,gzip(gfe),gzip(gfe) Complaints-To: groups-abuse@google.com Injection-Info: i38g2000prf.googlegroups.com; posting-host=85.1.248.150; posting-account=Ch8E9Q0AAAA7lJxCsphg7hBNIsMsP4AE Xref: g2news1.google.com comp.lang.ada:16081 Date: 2007-06-06T14:33:50-07:00 List-Id: My discussion with Dmitry in another thread looked as if completely abandoned by everybody, so I decided to start a new thread. As a short recap, my point was that assignment of T'Class should be banned. Dmitry has challenged me with analogies to regular unconstrained types where my construct fell apart. After some rethinking of the whole issue I believe I have something reasonably consistent. Here it goes. First of all, the motivation. The motivation is to create (at least in theory) a type system where CONSTRAINT_ERROR can be raised only in those places where explicit conversions are required. In other words, whenever an implicit conversion is allowed, it must follow that the absence of constraint error can be statically provable. This makes sense in high-integrity systems, because the places of potentially raised exceptions are distinguished in source code. The motivation is also to have a system that is consistent enough to cover both unconstrained types and class-wide types - these are similar enough to give them consistent treatment. I'm learning Ada for about one year. I'm still missing the proper terminology, so please bring me on track whenever I go astray - but remember that I'm writing here about some hypothetical Ada-like-but- better language, not about Ada2005. This allows me to get away with some terminology details. ;-) Some important foundations. A type can be unconstrained/constrained. It can be also a subtype of another type, adding some more constraints. An important observation is that the value-space and constraint-space might be or might not be equivalent within the given type. This distinction will be needed later to justify handling of various fundamental constructs. I will consider three different types that quite widely represent what we can have in Ada, and reconsider the examples from my previous posts (and those of Dmitry): - Integer (any number type, actually) as a type that has equivalent value-space and constraint-space. - String as a type that has different (although inclusive) value- and constraint-spaces. - Tagged types and class-wide types. 1. Integer and its subtypes (like Positive). An Integer object has some value - and only that. Any subtype of Integer is defined within the same space, which means that this only thing that Integer has is also subject for the constraint check. In other words, *every* modification of the Integer (or subtype of) object is potentially violating the constraint. Consider: procedure P1(X : in Integer); procedure P2(X : out Positive); procedure Swap(X, Y : in out Integer); declare X : Integer := -1; Y : Positive := 1; begin P1(X); -- (1) OK (matching) P1(Y); -- (1) OK (actual is stricter) P2(Y); -- (2) OK (matching) P2(X); -- (2) OK (actual is wider) Swap(X, Y); -- (3) compile-time error on second parameter end; The call at (3) should banned. For types that have equivalent value- and constraint- spaces, the rules for parameter passing are as follows: 1. "in" formal parameters can get actual parameters that have constraints matching or stricter 2. "out" formal parameters can get actual parameters that have constraints matching or wider 3. "in out" formal parameters can only get actual parameters that have matching constraints. Any violation of these rules leads to potential constraint error that cannot be prevented at compile-time. 2. String. Unconstrained array types have different value- and constraint-spaces (in the sense that they can have many different values within the same constraint). This means that there is a whole class of modifications that can be statically proven not to involve the constraint. Consider: procedure To_Upper(S : in out String); Such a procedure is perfectly reasonable. What is not reasonable is this: procedure Swap(X, Y : in out String); The problem comes from the fact that the two formal parameters could be initialized with actual parameters that have different constraints. This alone is not yet a problem (and we have to allow this signature!), but the body of Swap would have to contain some operations that potentially violate the constraint of each actual parameter. Potentially - meaning that run-time error could be raised. The solution is to introduce the concept of restricted parameters. Consider: procedure Safe_Swap(X, Y : in out String) with X'Length = Y'Length; The restriction says that this procedure can be called only with actual parameters that have the same length. There is still a lot we can do in the body (remember that value-space is different that constraint-space), but the safety can be protected at the call-site. Even statically: declare S1 : String := "abc"; S2 : String := "xyz"; S3 : String := "klmnop"; begin Safe_Swap(S1, S2); -- OK Safe_Swap(S1, S3); -- compile-time error Safe_Swap(S1, S3(1..3)); -- OK end; Now is the time to bring the assignment. The rule is simple: the language provides the assignment operation with restriction that its parameters must have equal constraint, if there are any. This allows to actually implement the Safe_Swap body! 3. Tagged types. I promised that this construct will consistently cover tagged types. Notice that for class-wide types that value-space and constraint-space are different, similarly to unconstrained arrays, and *unlike* number types. This means that there is a room for modifications that don't involve constraint, so we can and should allow in out T'Class. For example, the geometry package might have the Object type with some hypothetical "center" coordinates and the primitive operation Move that can move around the plane whatever specific figure we throw at it: procedure Move(A : in out Object'Class; DX, DY : in Real); This can work without modifying the constraint (tag), so should be allowed. But the naive Swap procedure should not be allowed - I mean, the signature is OK, but there is no way to implement it. We can have this instead: procedure Safe_Swap(X, Y : in out Object'Class) with X'Tag = Y'Tag; declare T1 : Triangle := ...; T2 : Triangle := ...; C : Circle := ...; begin Safe_Swap(T1, T2); -- OK Safe_Swap(T1, C); -- compile-time error end; Again, the language offers the assignment operation with similar signature (that is, restricted). This can be even inherited, dispatching, whatever. But has to be restricted, because T'Class is (sort of) unconstrained type: declare T1 : Triangle := ...; T2 : Triangle := ...; C : Circle := ...; begin T1 := T2; -- OK C := T1; -- compile-time error (violating the restriction) end; Of course, programmers can provide their own assignment overloads for whatever combination of types they care, but then it is *their* business. And their responsability. I believe that the above is consistent and meets the initial goals. The two things I was lacking before are: 1. Understanding the distinction between value-space and constraint- space (and the fact that their relation makes a difference between Integers and tagged types) 2. The hypothetical restricted functions; these are actually more general beasts, since the restriction can be about just anything. Whether it is enforced at compile- or run-time is another story, but for the sake of meeting my motivation of not having exceptions where implicit conversions are allowed I expect that restricted parameters follow the same logic and are themselves statically checked, with run- time checks injected in those places where the actual parameters are explicitly converted to match formals. Now - what am I missing? :-) -- Maciej Sobczak http://www.msobczak.com/