|
From: (see below) on 7 Apr 2008 13:34 On 07/04/2008 17:34, in article Pine.LNX.4.64.0804071812200.15920(a)medsec1.medien.uni-weimar.de, "stefan-lucks(a)see-the.signature" <stefan-lucks(a)see-the.signature> wrote: >>> The only useful use for "+" in Ada is for conversions. >> >> OK, so that's not much of a use. On the other hand, the idea that "+" >> represents the identity function is ingrained enough in me that I've >> resisted using it for a conversion operator even though others around >> me have been doing that. Call it an obstinate adherence to >> meaningless purity or whatever. But like Randy, I would have >> preferred adding one (maybe even two) operator symbols that would have >> no meaning except that the user could define them, although I'm not >> sure about "#" since that already has a use (in based numeric >> literals). I'd prefer something not currently used at all, like "@" >> or "!" or tilde. Of course, it apparently isn't going to happen >> anyway so there's not much point arguing about which character would >> be best. > > Two natural candidates which come into my mind would be an unary '&' and > the '%'-sign: > > '%' (percent) indicates some kind of conversion anyway (though usually > in postfix notation, not as a prefix, "75 %" for 75/100). Potential problems there with string literals (see RM: J.2 Allowed Replacements of Characters)? -- Bill Findlay <surname><forename> chez blueyonder.co.uk
From: Georg Bauhaus on 12 Apr 2008 15:46 Eric Hughes wrote: > On Apr 5, 3:43 am, Pascal Obry <pas...(a)obry.net> wrote: >> What to do if we have Put_Line defined for String and >> Unbounded_String? Which version gets called? > > I know this answer is a bit heretical, but the answer I'd pick is > "either one". Non-deterministic execution, if you'd like the > technical term. Which in practice means that the compiler can pick a > deterministic path and just use that one. > > The reason for this is that String and Unbounded_String are not > arbitrarily related in conception. Ada, as presently constituted, is > unable of representing the details of this relationship. Users of > these types know what they're supposed to do, but the language does > not capture this. A correct implementation of Put_Line should have > the property that the result of Put_Line on a String is the same as > the result of Put_Line on an Unbounded_String converted from the same > String. This is the substance of algebraic specification. So, > assuming (contrary to fact) such a specification, it should not matter > for correctness which version executed. > > It would affect performance, true. But it's performance about > symbolic manipulations (in the representation of the type) with > symbols that do not appear in the language itself. Since these > symbols do not appear in the definition of String, it's the wrong path > to try to specify performance choices _at this level of abstraction_. > Some form of (expanded) representation clause is the right place to do > this. Representation clauses are the natural way in Ada to specify > implementation choices. > > Back the larger type issue, the ARM contains the types > universal_integer, universal_real, universal_fixed; these are listed > as predefined in ARM 3.2.1/10. (Erratum: not all the predefined types > are listed here.) I find it droll that the term "predefined" is not > defined. It doesn't need to be, really, though. "Everybody knows" > what numbers are supposed to do. The axioms of arithmetic are well- > known, even if all the ordinary numbers of mathematics are > unimplementable on computers (only bounded-length representations are, > even if the bound is indefinite). > > I believe many of the issues involving strings could be addressed by > introducing a predefined type universal_string. And then the hard > part: making everything work out right. _Inter alia_, all conversions > between string types would have to preserve the underlying > universal_string value. I would raise Length_Error if a silent > truncation were to occur, under the theory that truncation is an > operation on values (as distinguished from objects and variables) that > should *never* be made silently. And having universal_string should > eliminate most of those unary conversion operators to move between > different string types. The fixed String is an array, a basic and central piece of Ada. Unbounded_String is not an array. Both types use the word "string" in their name, which is unfortunate. They are two very different constructions. We can use Unbounded_String objects in ways that are not available with strings: they are lists rather than fixed arrays. Seen from this point of view, a more general question is, do we have standard ways of converting between arrays and lists? Should these conversions be captured by some "predefined" notion of universal_array? (Maybe you will find ideas by Dmitry Kazakov instructive.) Unfortunately, programming always deals with storage, memory, and times, in particular when a systems programming language like Ada is used, and when credit is given to the fact that programs mean operations of a computer with storage cells, instructions, and clock ticks. I can imagine values of some underlying universal_array as part of a nice clean ivory tower model of mathematical reductionism. Nasty real world computers ;) The APL world is different and offers another hint. Maybe Ada fixed Strings let us think they somehow must have one dimension, but not 2, 3, or N. Why? And should universal_array be made capable enough to convert arrays to lists and vice versa per dimension?
From: Dmitry A. Kazakov on 12 Apr 2008 17:09 On Sat, 12 Apr 2008 11:50:56 -0700 (PDT), Eric Hughes wrote: > I believe many of the issues involving strings could be addressed by > introducing a predefined type universal_string. I don't see it this way. You have mentioned universal_integer, which is already here. So a comparable case would be Integer vs. BigNum. The latter (arbitrary-precision arithmetic) plays same role for integers as Unbounded_String does for strings. Now, if ARM had BigNum defined, we would experience exactly same problems with both, as we have with Strings and Unbounded_String. > Now, my real hope is that universal_string wouldn't need to be > predefined at all, but specified algebraically or logically within a > future version of Ada. I can only agree with that. But note, that in order to do so, you will need slices, indices and sets of indices (ranges is an example of) defined algebraically as well. Presently Ada lacks them. The bottom line, instead of patching it here and there, the language type system should be redesigned. Hacks need to be replaced by sound concepts.[*] Unfortunately Ada community has little interest in that. ---------- * By this I don't mean backward incompatibility. Actually it is the hacks, which are responsible for most of incompatibilities, as they tend to introduce strange syntax, new reserved words and confusing semantics. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de
From: Eric Hughes on 13 Apr 2008 12:31 On Apr 12, 3:09 pm, "Dmitry A. Kazakov" <mail...(a)dmitry-kazakov.de> wrote: > So a comparable case would be Integer vs. BigNum. The latter > (arbitrary-precision arithmetic) plays same role for integers as > Unbounded_String does for strings. Now, if ARM had BigNum defined, we would > experience exactly same problems with both, as we have with Strings and > Unbounded_String. Well, yes; that's a good second example, although let's call it Unbounded_Integer. The mechanisms are identical for dealing with universal_string/String/Unbounded_String and univeral_integer/Integer/ Unbounded_Integer. In the first case we have (0=absent,1=present) 0/1/1 and in the second we have 1/1/0. In the first case we have endless type conversions to do ordinary work. In the second we have an absence in the standard. On Sat, 12 Apr 2008 11:50:56 -0700 (PDT), Eric Hughes wrote: > I believe many of the issues involving strings could be addressed by > introducing a predefined type universal_string. On Apr 12, 3:09 pm, "Dmitry A. Kazakov" <mail...(a)dmitry-kazakov.de> wrote: > I don't see it this way. I have a strong counterclaim--I don't think it can be solved without introducing such a type. Here's the real rub: universal_integer and universal_string are each _unimplementable_ as executable types. As abstract mathematical types, they're perfectly well-defined. The origin of the difference is that universal types have no possible bound in size. Since every computer type has such a bound, there's a fundamental disjunct between these two. An unbounded type actually does have a bound--it throws an exception when its internal bounds are exceeded. A hypothetical implementation of a universal type would be unable *in principle* to throw such an exception. This is clearly contrary to fact, where every program can run out of memory. If this sounds like my idea for partial type specification (one without code), it should. These ideas come from the same perspective. There's no concept (except _ad hoc_ in the ARM) of what a universal, unimplementable type is; there's certainly no way of declaring one. So here's the very beginning of a proposal. To declare and define such a type: universal_type_definition ::= *universal* *type* defining_identifier [known_discriminant_part] *;* Notice the absence of the keyword "is". Also notice that the declaration and definition are the same. The point is that a universal type _is not_ anything insofar as record declaration or anything else that might act as a representation. A universal type can not have a representation (not just "may not"). If it had a representation, it would not be universal. This is not merely a practicality; this is categorical. Now like other types, you could declare operations on universal types. But every such operation would also be unimplementable, so they're declaration-only. They cannot be defined, since there's no representation to work with. As with an interface, an ordinary type T could derive from a universal type U. It could override operations from the universal type. But since U has no implementation, such a derivation has no effect on the representation of T. The result of overriding would be to bind both the syntax and semantics of U to that of T. With only a single such T, an overriding declaration would be meaningless and of no effect. With two such declarations, however, the benefits begin. The meaning of such a pair of declarations says that the semantics of the two operations are identical when values of one type are substituted for that of the other _when those values are considered as universal values_ (and thus not as represented values within an implementation). With such a syntax, you obtain the ability to make a well-formed formula with clearly defined semantics. It says nothing about compilation. I consider this an advantage, as it is the essence of separate compilation. Getting to an unambiguous expression is the first step. Now, about compilation, a formula with well-defined semantics does not (in general) have well-defined pragmatics. The formula itself does not specify how long execution should take, what functions must be called, etc. All the formula as such says is what the results should be. A simple example: universal type U ; function op( U Object ) return U ; type T1 is private and U ; type T2 is private and U ; overriding function op( T1 Object ) return T1 ; overriding function op( T2 Object ) return T2 ; function U( T2 Object ) return T1 ; T2 x ; T1 y1 := T1.op( U( x ) ) ; T1 y2 := U( T2.op( x ) ) ; T1 y := x.op ; The expressions for y1 and y2 should be interchangeable as implementations for the expression for y. The point of the declarations is to give unambiguous semantics to the expression "x.op", so that it does not matter to the correctness of the program what implementation expression the compiler chooses. If Usenet had better formatting, I'd draw a commutative diagram as an illustration. There's special meaning behind the conversion function U. Its mandatory semantics is to convert an object of T2 to one of T1, such that their values are equal as universal values. Eric
From: Eric Hughes on 13 Apr 2008 12:53
On Apr 12, 1:46 pm, Georg Bauhaus <rm.tsoh.plus- bug.bauh...(a)maps.futureapps.de> wrote: > The fixed String is an array, a basic and central piece of Ada. > Unbounded_String is not an array. Sure, you can have your array; I have no issue at all with that. But Ada.Strings.String is just not a string, not in the way it does padding, not in its absence of concatenation. > Both types use the word "string" in their name, which is > unfortunate. I consider it more than unfortunate. I consider it just plain wrong. > Seen from this point of view, a more general question is, > do we have standard ways of converting between arrays and lists? > Should these conversions be captured by some "predefined" notion > of universal_array? No. But this answer is one that hinges on the specifics of the question, not on generalities. Arrays have a fixed number of elements; lists do not. This is more than a matter of implementation. It's a question of specification. Now these two types do share the property that they are indexable. A proper set of universal declarations should be able to capture this similarity. This requires a partial specification, in my view, and that subject is beyond the scope of this message, except to say that partial specifications and universal types are orthogonal concepts. Hidden in your question, though, is another issue. I don't believe that conversions should be automatic. I believe they should be explicitly declared and explicitly defined. > Unfortunately, programming always deals with storage, memory, > and times, in particular when a systems programming language > like Ada is used, and when credit is given to the fact that > programs mean operations of a computer with storage cells, > instructions, and clock ticks. I can imagine values of some > underlying universal_array as part of a nice clean > ivory tower model of mathematical reductionism. Nasty real > world computers ;) There's a pattern in dealing with this issue that deserves standardization with respect to universal types. -- When return is ordinary, then the result adheres to the universal definition. -- When return is exceptional, then there is no requirement that there's any adherence. Thus exceptions kick you out of the mathematical assumptions into the real world, and they do so in a reasonably controlled way. Eric |