!standard 4.5 10-02-09 AC95-00184/01 !class Amendment 10-02-09 !status received no action 10-02-09 !status received 09-10-27 !subject System of Units !summary !appendix From: Martin Dowie Date: Tuesday, October 27, 2009 11:58 AM FWIW... [Editor's note: This conversation forked from one on AI05-0175-1.] Although the pilot (or any user of an avionic system) is presented with degrees, it's very likely that value is converted before being 'used' by the underlying software system. I've seen them converted into a fixed-point range -1.0 .. 1.0-small; float range -180 .. 180; float range -pi .. pi; float range -1.0 .. 1.0 and probably others. I think getting a "System of Units" addition to the language would help here more than trying to create a super-set solution to the curiosities of deg/rad, az/el, u/v/w or lat/long. If there were a SoU feature to the language I'm pretty sure that building lat/long et al packages on top would be much simpler. **************************************************************** From: Franco Gasperoni Date: Tuesday, October 27, 2009 12:52 PM Having a System of Units in Ada and having the type system catch unit inconsistencies would be very helpful indeed **************************************************************** From: Dmitry A. Kazakov Date: Tuesday, October 27, 2009 1:28 PM Yes, but these are different issues. A system of units as used in physics ignores representation issues of model numbers. When you deal with floating-point dimensioned numbers you can ignore these issues because floating-point is "closer" to reals than the fixed-point model, it is already inaccurate and this inaccuracy "covers" the issues which become pressing for fixed-point. There you have to control rounding, and that will be in terms of units. The same value in feet and in meters would be numerically different, so the outcome of the operations will depend on the unit. Which is equivalent to the obvious fact that the small is dimensioned. It is also so with floating-point, but we ignore that because floating-point is already inaccurate. (However, for example Constraint_Error in feet and Constraint_Error in meters are semantically different, which is just ignored) Now let you divide fixed-point feet by fixed-point hours what is the small of the result fixed-point type? **************************************************************** ult fixed-point type? From: Randy Brukardt Date: Tuesday, October 27, 2009 1:23 PM We spent a lot of time on various proposals for this the last time, and we were not able to come up with anything that was both useful and worked semantically. All we could do was allow the "deletion" of operators with abstract, so that tools that create the appropriate set of types and operators could define only the operators that they need. Go read AI95-00324 (especially the !appendix) to see why this problem is so hard. There also were several comp.lang.ada threads on this topic. This "units checking" has been a sort of holy grail for some Ada users since the beginning of the Ada language. But everyone seems to have a different idea of what they mean by this - it's almost always some silver bullet that would magically eliminate half of the work and errors in programs. Moreover, everyone who has studied the problem seriously has come to the conclusion that a useable solution is extremely hard -- essentially impossible within the Ada framework. As such, it is a terrible waste of time to spend any energy on this issue. I realize that any time some expert says something is "impossible" they are usually wrong. I would be very happy to be proved wrong on this point. All you have to do is propose some *new* scheme that doesn't run into the usability, performance, implementability, compatibility, and consistency with the rest of the language problems that every other proposal has run into. In the absence of someone doing that, there is nothing to do here. The best way to implement systems of units in Ada is to use a tool to define the dozens of types and hundreds of operators needed (or do those declarations by hand: it only needs to be done once per project). But there is so little commonality between the needs of different projects that there doesn't seem to be anything more that can be done. Go ahead. Prove me wrong. Or stop whining. P.S. Next time someone says that they want a units checking solution without any indication of what they mean by that, I am going to scream!! You'll hear me in Europe!! **************************************************************** From: Edmond Schonberg Date: Tuesday, October 27, 2009 2:06 PM We've been discussing internally a simple scheme involving subtypes and pragmas, that should do most of the job and be extremely lightweight. I notice that the beginning of AI95-0324 mentions that such was considered but discarded because it represented too much of an implementation burden. It's true that it's all compiler work, but it appears to us quite doable. Nothing will be ready to discuss next week, but hopefully we'll have a prototype implementation in a couple of months, that the community can experiment with. (Is this good enough to stifle your scream, at least before Valencia :-)? **************************************************************** From: Martin Dowie Date: Tuesday, October 27, 2009 2:46 PM That sounds really encouraging. I appreciate Randy's frustration with this always coming up but to us mere users, it's a little galling to see C++ guys getting this (via Boost) and even Java had a language addition proposal (http://jcp.org/en/jsr/detail?id=275) and there not be anything really on the table for Ada! **************************************************************** From: Randy Brukardt Date: Tuesday, October 27, 2009 3:45 PM There are very good solutions to this problem that do not involve the language (similar to the C++ "solution" you mention above). I personally feel that the package generator tool solution provides the best possible result, as the resulting package adds no runtime overhead, is well-integrated with the language (because it *is* the language), doesn't have any compatibility problems, and so on. One could imagine a language-defined package to provide a solution to the problem, but it would only be useful for a small subset of units problems (thus the tool approach is more promising IMHO). Ada 2005 addressed the major shortcoming of the tool approach in Ada 95 by providing a way to "delete" predefined operations. The only problem with that as *the* solution is that it isn't something that we can put into the Ada standard (it has nothing to do with tools), and thus it doesn't have the visibility needed. (There also is a minor issue with user-defined I/O attributes, but that hasn't g ten much traction in general.) From another message: > We've been discussing internally a simple scheme involving subtypes > and pragmas, > that should do most of the job and be extremely lightweight. I'm dubious that doing "most of the job" has much value. Tucker's proposal tried to do "most of the job" but turned out to do rather little of it. I'm also against "solutions" that look bolted on -- that are not well integrated into the language. The tool solution is much better than that; the resulting package does not have integration problems. I also strongly dislike pragmas controlling legality rules. I will strongly oppose any attempt to add such pragmas for any reason whatsoever (it's not specific to this idea). So you've got three strikes already in my book. I realize that your description of the proposal is so sketchy that I might be reading too much into it, and a fuller proposal might be less objectionable. > I notice that the beginning of AI95-0324 mentions that such was > considered but discarded because it represented too much of an implementation burden. > It's true that it's all compiler work, but it appears to us quite doable. > Nothing will be ready to discuss next week, but hopefully we'll have a > prototype implementation in a couple of months, that the community can > experiment with. (Is this good enough to stifle your scream, at least before > Valencia :-)? No, for the reasons given above. But even if those can be mitigated, such a proposal would be too late for this round of Ada improvements; there is only a week left before the deadline for proposals. So I don't think it will help the users much (solving this problem in 2020 is not of much interest to the current Ada users!). Personally, I would like to see some attempt to standardize an Ada units tool set. I'm not sure what form such standardization should take, but I think the tool ought to be widely available as a part of all Ada toolkits. **************************************************************** From: Edmond Schonberg Date: Tuesday, October 27, 2009 4:20 PM here is a sketch of what we have in mind, as an internal GNAT development. It is clearly too late to include this in the forthcoming revision of the language and should be considered an invitation for comments for the development of a compiler-specific tool, nothing more. If it seems to meet the needs of the community we can consider it for inclusion in Ada2020 or thereabouts! --- A simple model for physical dimension checking in GNAT. Periodically someone in the Ada community asks for some tool to do dimensional checking, thinking that the strong typing of the language and the ability to define arithmetic operators should make this easy. It turns out that a naive approach that defines ,for example: function "*" (X, Y : Length) return Area; runs rapidly into a combinatorial nightmare: there are always additional operators to declare, expressions rapidly become ambiguous, the burden on the programmer is high, and the attempt is eventually dropped. AI95-0324 proposed a generic model to systematize this approach, and added a very elegant I/O facility for dimensioned values, but was criticized (most energetically by Christof Grein) because it did not solve the combinatorial explosion. We want to propose a simpler scheme, based on pragmas and subtypes, and fully static. The following fragment gives an intuitive picture of the approach: package MKS_Units is type MKS_Value is new Long_Float; subtype Meter is MKS_Value; pragma Dimension (Meter, 1, 0, 0); m : constant Meter := 1.0; mm : constant Meter := 0.001; subtype Square_Meter is MKS_Value; pragma Dimension (Square_Meter, 2, 0, 0); subtype Time is MKS_Value; pragma Dimension (Time, 0, 0, 1); sec : constant Time := 1.0; subtype Acceleration is MKS_Value; pragma Dimension (Acceleration, 1, 0, -2); g : constant acceleration := 9.81 * m / (sec ** 2); ... A similar package would be available for CGS (and Britsh?) units. The pragma Dimension specifies the dimensional structure of a given subtype. The three (or four, if we use a gaussian system) exponents are rational numbers whose numerator and denominator are literals. The elegant Rational package developed by Christof Grein is part of the intended implementation, but of course internally every Ada compiler also has some rational numbers implementation, for arbitrary precision arithmetic. The compiler knows how to handle integer and fractional powers, predefined square root, and elementary functions. every Units package will contain an instantiation of Ada.Generics.Elementary_Functions for the common floating- point type. If dimensional checking is active, the compiler does the usual arithmetic on exponents for every arithmetic operation and assignment (including parameter passing) and warns of a mismatch. We do not want the implementation-defined pragma to affect the legality of the program. **************************************************************** From: Bob Duff Date: Tuesday, October 27, 2009 6:17 PM > I also strongly dislike pragmas controlling legality rules. I will > strongly oppose any attempt to add such pragmas for any reason > whatsoever (it's not specific to this idea). Randy, I think maybe you've forgotten the real rule about this -- the above is half right. ;-) If a pragma makes an otherwise-illegal program legal, it's normally considered to be in poor taste. The other way around is OK -- nothing wrong with a pragma that one uses to place some restriction on oneself, thus making a legal program illegal. The most obvious example is pragma Restrictions (one of my favorite pragmas, and certainly not in poor taste). It's purpose is of course to make some legal programs illegal. There are many other examples. I just did a quick read of Annex L, and it looks like 19 out of 50 language-defined pragmas have this property. For implementation-defined pragmas, there is Impl Advice to this affect (since Ada 95). This makes sense -- it means if you have such pragmas in your program, and want to port to another compiler that doesn't understand them, it is likely to work. Note that I insisted on the weasel-word "Normally" in that Advice. ;-) For the "System of units" discussion, a pragma (whether language defined or implementation defined) that makes it illegal to multiply meters*meters-->meters is in perfectly good taste. **************************************************************** From: Randy Brukardt Date: Tuesday, October 27, 2009 6:42 PM > Randy, I think maybe you've forgotten the real rule about this -- the > above is half right. ;-) No, after rereading Robert Dewar's rant on good taste in pragmas (it's filed in AI05-0163-1 if anyone wants to see it), I came to realize that I just plain hate pragmas. They're almost always a sub-par solution to a problem. It is OK, of course, for an implementation to define pragmas (it's pretty much all implementers have), but I dislike the language doing so. All that said, the proposal that was actually posted seems about as good as a pragma solution could be. But I'd actually prefer a solution with real teeth. (BTW, I wonder what the rules in generics for these (sub)types are. Whatever they are, they'll cause trouble, I think. If you enforce the units rules, you'll have problems with simple things like Text_IO. If you don't enforce the rules, you'll have an easy way to circumvent the rules - intentionally or unintentionally. I know, I'm starting to think like Steve. ;-) But I haven't asked yet what happens if you apply this pragma to a task with a progenitor -- I'm still learning. ;-) **************************************************************** From: Dmitry A. Kazakov Date: Wednesday, October 28, 2009 3:34 AM > We want to propose a simpler scheme, based on pragmas and subtypes, > and fully static. In cases where units are massively deployed, like automation, they are not always static. E.g. you should be able to write a code working with dimensioned values, which unit constraint is unknown until run-time. Simple example is a non-generic dimensioned I/O package. BTW, how are you going to pass the pragma Dimension as a formal generic parameter? **************************************************************** From: Jean-Pierre Rosen Date: Wednesday, October 28, 2009 5:43 AM > here is a sketch of what we have in mind, as an internal GNAT > development. It is clearly too late to include this in the forthcoming > revision of the language and should be considered an invitation for > comments for the development of a compiler-specific tool, nothing more. > If it seems to meet the needs of the community we can consider it for > inclusion in Ada2020 or thereabouts! IIRC, there was long ago a paper (by N Cohen, or was it P. Hilfinger?) about checking dimensions *dynamically* with a discriminated type and proper redefinition of operators (many other papers, notably by Grein/Kazakof since). He claimed that although it was dynamic, a compiler could be smart enough to detect violations at compile time. It seems to me that what you are proposing with pragmas is nothing more than what could be accomplished with the discriminated approach. So why not make a package with the discriminated type, and let compiler magic recognize this type and do what you propose. Compilers without special magic would still provide the dynamic check. A pragma Special_Scrutiny (or whatever, maybe Restriction (no_inconsistency, on => T) might be required to allow inhomogeneous operations to be rejected, but it should go through Randy's taste filter. **************************************************************** From: Franco Gasperoni Date: Wednesday, October 28, 2009 6:30 AM JP, do you have a pointer to that paper? **************************************************************** From: Jean-Pierre Rosen Date: Wednesday, October 28, 2009 7:11 AM There is a famous paper (Grein, Kazakov, Wilson) about all issues with dimension computing in the proceedings of AE 2003 (Toulouse), LNCS 2655. It references a paper by Hilfinger, "An Ada package for Dimensional Analysis" in ACM Transactions on Programming Languages and Systems, vol 10(2), April 1988. It might be the one, although I had memories of seeing it in Ada Letters, but a quick scan of my old collection did not find it (but I have holes in it). Too bad Google does not (yet) index paper... **************************************************************** From: Edmond Schonberg Date: Wednesday, October 28, 2009 10:37 AM > In cases where units are massively deployed, like automation, they are > not always static. E.g. you should be able to write a code working > with dimensioned values, which unit constraint is unknown until > run-time. > Simple example is a non-generic dimensioned I/O package. The scheme I have in mind is strictly compile-time. It will not cover all 6 points of your great survey article, but should do the important part of the job: compile-time checks, no run-time overhead, support for derived types and composite types of dimensioned components, and some generic programming. An important desideratum is simplicity : given that this project currently has an internal budget in the femto- euros, there is no point is planning for peta-lines of code. I think that the important need is to validate scientific computations, the proper I/O issue seems less important to me (and obviously requires much more elaborate mechanisms, as described in A95-0324). > BTW, how are you going to pass the pragma Dimension as a formal > generic parameter? My inclination is to have no dimensionality checking in generic code, but rather perform the proper checks on instances, when the actual (sub)types are known. So there is no need to extend dimension checking to the contract model. In any case, if I have a gaussian solver or a sorting routine, I don't see where dimensions would come into play. **************************************************************** From: Edmond Schonberg Date: Wednesday, October 28, 2009 10:42 AM >It seems to me that what you are proposing with pragmas is nothing more >than what could be accomplished with the discriminated approach. So why >not make a package with the discriminated type, and let compiler magic >recognize this type and do what you propose. Compilers without special >magic would still provide the dynamic check. Mostly because the magic of a pragma is much cheaper than the magic of hidden discriminants, that will represent a huge development effort, and still requires the redefinition of operators. There are reasons why this pioneering paper has not seen a single solid implementation in 21 years :-)! >A pragma Special_Scrutiny (or whatever, maybe Restriction >(no_inconsistency, on => T) might be required to allow inhomogeneous >operations to be rejected, but it should go through Randy's taste filter. We already have a pragma Suppress for this very purpose, and Suppress (Dimensionality_Checking) will probably be handy. I think this is usable only if it is lightweight and give few false positives, so it requires an equally lightweight mechanism to turn it off. **************************************************************** From: Jeffrey R. Carter Date: Wednesday, October 28, 2009 10:44 AM > IIRC, there was long ago a paper (by N Cohen, or was it P. Hilfinger?) > about checking dimensions *dynamically* with a discriminated type and > proper redefinition of operators (many other papers, notably by > Grein/Kazakof since). He claimed that although it was dynamic, a > compiler could be smart enough to detect violations at compile time. I was on a project where we used such a package for development. Once we had the units straight, for production we replaced the discriminated record type with a straight floating-point type. This worked very well. Something that wouldn't have required us to replace the type would have worked even better. **************************************************************** From: Jean-Pierre Rosen Date: Wednesday, October 28, 2009 11:26 AM > Mostly because the magic of a pragma is much cheaper than the magic of > hidden discriminants, that will represent a huge development effort, > and still requires the redefinition of operators. There are reasons > why this pioneering paper has not seen a single solid implementation > in 21 years :-)! I never mentionned /hidden/ discriminants. I was really thinking of something like: type Unit (M,K,S,A : Integer) is record Value : Float; end record; The basic information is exactly what you provide to the pragma... **************************************************************** From: Randy Brukardt Date: Wednesday, February 10, 2010 12:01 AM > It references a paper by Hilfinger, "An Ada package for Dimensional > Analysis" in ACM Transactions on Programming Languages and Systems, > vol 10(2), April 1988. Amazingly (or maybe not, as I don't throw stuff out easily), I have this particular ACM TOPLAS issue on my bookshelf. The article in question is the first one in the issue, ahead of one by Nicholas Wirth (where he defines type extensions - cool). He proposes a relatively simple units package. Here is a sample (transcribed by hand, so any errors are probably mine): package Units is type Quant (D0, D1, D2, D3 : Integer := 0) is record V : Float; end record; -- Convinient abbreviations: subtype Scalar is Quant(0,0,0,0); Unit0 : constant Quant := (1,0,0,0,1.0); Unit1 : constant Quant := (0,1,0,0,1.0); Unit2 : constant Quant := (0,0,1,0,1.0); Unit3 : constant Quant := (0,0,0,1,1.0); -- Standard arithmetic: function "+" (L,R : Quant) return Quant; function "-" (L,R : Quant) return Quant; ... -- Coercions: function "/" (L,R : Quant) return String; function "+" (L : Scalar) return Float; function "-" (L : Scalar) return Float; -- Relationals: function "<" (L, R : Quant) return Boolean; ... -- Exceptions: -- Relationals, binary "+" and "-", and "/" yielding String -- raise Constraint_Error if L and R have different subtypes. pragma Inline (); end Units; He goes on to show a sample implementation, and to discuss the fact that the inlining involved will generally eliminate the unit checks by simple constant propagation. He eventually makes the extrodinary statement: "Dimensions as types have no place in Ada because because they are *definable* in Ada with no extra language features." (Emphasis his.) He does back off of that statement a bit saying that compilers need to recognize and optimize this construct well, and that will require some user "agitation". That he certainly was right about (given that we are still discussing this 22 years later)! Anyway, I agree with Jean-Pierre that this general approach is definitely one to consider. Compilers *do* optimize discriminants already (and they have to be able to handle discriminants not stored with the type in order to implement Unchecked_Unions). So compilers *could* be able to construct these types without any space overhead. Prreconditions and postconditions would allow expressing the checks and resulting discriminants in the specifications of the operators. Compilers would have to be smart enough to remove those checks (especially if the discriminants are omitted from the in-memory representation), but I would hope that they do that in any case. So it seems that only a bit of "help" is needed to make a viable solution out of this. The only thing that I see as clunky is the ugly aggregates to create values of the various types; perhaps a little bit of syntactic sugar to make the aggregates read better would do the trick? ****************************************************************