Version 1.1 of acs/ac-00184.txt
!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 t
hat 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 th
is 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 too
l, 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 t
hat 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 (<all of the above>);
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?
****************************************************************
Questions? Ask the ACAA Technical Agent