comp.lang.ada
 help / color / mirror / Atom feed
* Dimensional Data Types
@ 1994-12-03  0:06 Do-While Jones
  1994-12-04  5:09 ` Carlos Perez
  0 siblings, 1 reply; 4+ messages in thread
From: Do-While Jones @ 1994-12-03  0:06 UTC (permalink / raw)


The dimensional data type issue keeps coming up.  The latest exchange was
in CLA article 2709.

> ron house <house@HELIOS.USQ.EDU.AU> wrote:

> How about "Ada - use it when you want to multiply a length by a length
> and get a length, or when you want to be prevented from dividing an area
> by a length because they have different types."

> (PS: yes, you can fix that by defining zillions of additional *,/,- and +
> functions, but good grief...)

He got a response
FROM: "Bennett, Chip (KTR) ~U" <BennettC@J64.STRATCOM.AF.MIL>

> It seems to me that how many functions you have to define depends on how
> you approach the problem.  Let's start with the premise that there are
> many triples of numbers that exhibit similar relationships.  For example:
>
     > 1) length, 2 (squared), area
     > 2) length, 3 (cubed), volume
     > 3) length, N, hyperspace  :-)
>
> Or perhaps a different relationship:
>
     > 1) speed, time, distance
     > 2) principle, rate, interest
     > 3) mass, acceleration, force
     > 4) current, resistance, voltage
     > 5) mass, (light speed) squared, energy
     > 6) large unit of measure, constant, small unit of measure
>
> Now, we ought to be able to create a generic package that declares a set
> of allowable functions for a particular relationship
> (A := B * C, C := A / B, etc.).
> Then we could create a package that declares a set of private types
> (i.e. speed, time, and distance) and instantiates the generic package of
> math functions using the private types.  Since the types are private they
> are limited to the functions in the instantiated generic.
>  Area := Length ** 2 is allowed, but Length := Length ** 2 is not
> allowed.
>
> In Ada 83 there is a limitation in that we can't extend the set of
> allowable functions for some triple that has some _extra_ relationship
> that the others don't have, but child packages in Ada 9X would allow for
> adding new functions to the private type.
>
> Given that there is a fairly limited set of relationship groups for
> triples that make sense in the real world, I doubt you would have to
> write very many additional math functions.
>
> All that said, I've never actually tried this, but it seems plausible.
> Any thoughts?

I have tried it, and it works very well, but I'm getting ahead of myself.

Dimensional data types were proposed for Ada 9X, but they were rejected.
They weren't rejected because they aren't useful.  They were rejected
because it was possible to build them perfectly well in Ada 83, so there
was no need to add special language features to support them.  (That's the
beauty of Ada.  You don't have to make special features part of the
language.  The language is powerful enough that you can build special
features exactly the way you like them.)  Ada 9X has even more power, so it
might be possible to come up with even better dimensional data types in Ada
9X.

Some Ada programmers apparently do something like this:

AREA := Area_type(LENGTH) * Area_type(LENGTH);

This appears to be a knee-jerk reaction to a compiler type-mismatch error
message.  I think that is a really bad response because one could just as
easily write

AREA := Area_type(SPEED) * Area_type(LENGTH);

which is certainly wrong.  That is no more safe than declaring all the
variables to be type FLOAT (which is another common "solution" to the
"problem" of Ada's strong typing.)

Most of the work I do involves scientific calculations.  I use dimensional
data types ALL the time.  They have saved my bacon more times than I care
to remember.  Not only that, they make it much easier for me to reuse my
software components because all my components are based on a common set of
basic data types.

I spent a little bit of effort years ago creating the abstraction, and now
I can use the abstraction as easily as I can write, "with TIME_UNITS;" or
"with ELECTRICAL_UNITS;".

First, I created two generic packages, INTEGER_UNITS and FLOAT_UNITS, which
define private data types with the operators that make sense for
dimensioned data types.  The only difference in these packages is that one
can be instantiated for integer data types, and the other can be
instantiated for real data types.

Second, I instantiated them for INTEGER, INTEGER_32, FLOAT, LONG_FLOAT,
etc.  Usually I instantiate them without range constraints, but in the
case of temperature data types I use absolute zero as the lower limit.

Third, I created several non-generic packages that DERIVE particular kinds
of dimensional units from the Units type in the instantiation of the
appropriate generic package.  For example, DIM_FLOAT is the instantiation
of FLOAT_UNITS for type FLOAT.  So, I simply write, "type Feet is new
DIM_FLOAT.Units;".  This creates a type Feet which has all the desired
properties.

I divided the dimensional units into consistent sets.  This makes it
possible for me to add packages with new types for a new problem domain
without making all my previous work obsolete.  (I don't do any nuclear
physics work, so I don't have any data types that supports that problem
domain.  But I could add a package with those data types without affecting
anything I have already done.)

The packages I use all the time are:

ANGULAR_UNITS:  This packages defines the types Degrees and Radians,
conversions between them, and the constant PI.

TIME_UNITS:  This package defines the type Milliseconds, Seconds, and
Hertz.  The Seconds data type is a floating point type that I use for time
calculations.  Hertz is a floating-point type that can be multiplied by
Seconds with a dimensionless result.  Milliseconds is an integer type used
to represent the time of day to 1 millisecond accuracy.  (Most of the data
I work with is tagged with IRIG-B time tags, to 1 millisecond resolution.)
I overloaded the Milliseconds relational operators (">", ">=", etc.) to
take into account midnight, so 1 AM is later than 11 PM.  This was easily
done in Ada 83, even without tagged types or modular types.  I also gave
the Milliseconds type functions called Image and Value that convert to and
from 12 character strings of the form "HH:MM:SS.XXX".

ELECTRICAL_UNITS:  This package defines types needed for electrical
engineering problems.  It's specification is:

with DIM_FLOAT;

package ELECTRICAL_UNITS is

  type Volts is new DIM_FLOAT.Units;
  type Amps  is new DIM_FLOAT.Units;
  type Ohms  is new DIM_FLOAT.Units;
  type Watts is new DIM_FLOAT.Units;
  type dBs   is new DIM_FLOAT.Units;

  -- Ohm's Law (E = IR)
  function "*"(I : Amps; R : Ohms) return Volts;
  function "*"(R : Ohms; I : Amps) return Volts;
  function "/"(E : Volts; R : Ohms) return Amps;
  function "/"(E : Volts; I : Amps) return Ohms;

  -- Watt's Law (P = IE)
  function "*"(I : Amps; E : Volts) return Watts;
  function "*"(E : Volts; I : Amps) return Watts;
  function "/"(P : Watts; E : Volts) return Amps;
  function "/"(P : Watts; I : Amps) return Volts;

  -- Decibels are defined to be:
  --      10 Log10(Measured Power / Reference Power)
  -- Assuming a constant resistance, the definition is
  -- equivalent to:
  --      20 Log10(Measured Voltage / Reference Voltage)

  -- The following operators include the Log (or antilog) operation
  -- as part of the division (or multiplication).

  function "/"(MEASURED, REFERENCE : Watts) return dBs;
  function "*"(DB : dBs; REFERENCE : Watts) return Watts;

  function "/"(MEASURED, REFERENCE : Volts) return dBs;
  function "*"(DB : dBs; REFERENCE : Volts) return Volts;

end ELECTRICAL_UNITS;

BRITISH_UNITS:  This package defines types needed for mechanical
engineering (or classical physics) problems.  It starts out like this:

with DIM_FLOAT,
     TIME_UNITS;

package BRITISH_UNITS is

  type Feet                 is new DIM_FLOAT.Units;
  type Sq_feet              is new DIM_FLOAT.Units;
  type Cu_feet              is new DIM_FLOAT.Units;

  type Feet_per_sec         is new DIM_FLOAT.Units;
  type Feet_per_sec_sq      is new DIM_FLOAT.Units;

  type Pounds               is new DIM_FLOAT.Units;
  type Slugs                is new DIM_FLOAT.Units;

  type Pounds_per_sq_inch   is new DIM_FLOAT.Units;
  type Slugs_per_cu_foot    is new DIM_FLOAT.Units;

  type Foot_pounds          is new DIM_FLOAT.Units;
  type Horsepower           is new DIM_FLOAT.Units;

  type Slug_foot_per_sec    is new DIM_FLOAT.Units;

  G : constant Feet_per_sec_sq := +32.1740;

  -- and "*" and "/" operators for mixed types, including Seconds
  -- from the TIME_UNITS package.

ENVIRONMENTAL_UNITS:  This package defines pressure, relative humidity and
temperature data types.  Since I use it for problems that involve
atmospheric pressure, it defines a pressure type "mm_Hg" and functions that
convert millimeters of mercury to PSI, and vice versa.

I find these packages to be very useful.  They make my code easy to read
(because declarations like "DISTANCE : Feet;" are less ambiguous than
"DISTANCE : Length_type;").

As a general rule, I don't use USE clauses, but I do USE the dimensional
packages so the dimensional types, constants, and operators are visible.
(Ada 9X has a limited USE clause for people who are not permitted to use
USE clauses which could be used instead.) [If you have written a natural
language parser, test it on the previous sentence.  If it can parse that
sentence, it can parse anything! :-) ]

Since the USE clause makes the operators visible, I just write things like
"VELOCITY := DISTANCE / TIME;" and Ada does all the dimensional checking
for me.  If I write "VELOCITY := DISTANCE / VOLTS;", I naturally get an
error message.  Of course I can respond to the error message by doing
something really stupid, like "VELOCITY := DISTANCE / Seconds(VOLTS);", but
I am not that dumb yet.

So, Ada 83 has a perfectly good way to represent dimensional data types.
You have to define the operators, but there aren't "zillions" that need to
be defined.  Ada 9X gives you even more ways to create dimensional data
types, so there is possibly an even better solution in Ada 9X.

If you don't want the safety and clarity that dimensional data types give
you, you can just declare everything to be FLOAT, like you would in C.  You
don't have to use dimensional types if you don't want to.

Do-While Jones


-- 
            +--------------------------------+
            |    Know                 Ada    |
            |        [Ada's Portrait]        |
            |    Will              Travel    |
            | wire do_while@ridgecrest.ca.us |
            +--------------------------------+



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Dimensional Data Types
  1994-12-03  0:06 Dimensional Data Types Do-While Jones
@ 1994-12-04  5:09 ` Carlos Perez
  1994-12-05  1:39   ` Carlos Perez
  1994-12-05 15:00   ` Do-While Jones
  0 siblings, 2 replies; 4+ messages in thread
From: Carlos Perez @ 1994-12-04  5:09 UTC (permalink / raw)


Do-While Jones (do_while@owens.ridgecrest.ca.us) wrote:

: Since the USE clause makes the operators visible, I just write things like
: "VELOCITY := DISTANCE / TIME;" and Ada does all the dimensional checking
: for me.  If I write "VELOCITY := DISTANCE / VOLTS;", I naturally get an
: error message.  Of course I can respond to the error message by doing
: something really stupid, like "VELOCITY := DISTANCE / Seconds(VOLTS);", but
: I am not that dumb yet.

It appears to me that you have designed an idiot-resistant system
(there is no such thing as idiot-proof ;-) 

I thought that your VOLTS was a private type and so you can't type
cast VOLTS into SECONDS.  You may need to have some functions whose
sole purpose is to convert dimensional types into scalar (non-dim) types
like Volts_To_Float and Float_To_Volts so you can define new and improved
cross-dimensional operators such as "*". 

My point is that Ada won't permit a simple cast as you described, unless
I missed something? 

: Do-While Jones

When-Others Perez 



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Dimensional Data Types
  1994-12-04  5:09 ` Carlos Perez
@ 1994-12-05  1:39   ` Carlos Perez
  1994-12-05 15:00   ` Do-While Jones
  1 sibling, 0 replies; 4+ messages in thread
From: Carlos Perez @ 1994-12-05  1:39 UTC (permalink / raw)


Carlos Perez (perez@oldcolo.com) wrote:
: My point is that Ada won't permit a simple cast as you described, unless
: I missed something? 

Hm, I think I did miss something...  SECONDS and VOLTS are private types
derived from the same parent?  GNAT permits me to cast one private type
into another as long as they derive from the same parent.  Somehow, I thought
SECONDS was a new numeric rather than a new private.  

Anyways, I've never tried to cast private types... been out of the
Ada coding business for too long  :-(  Sorry for the wasted bandwidth.
I'll check my LRM tomorrow for the rules on type conversion and privates.

: : Do-While Jones

: When-Others Perez 

"brain raised Storage_Error" Perez



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Dimensional Data Types
  1994-12-04  5:09 ` Carlos Perez
  1994-12-05  1:39   ` Carlos Perez
@ 1994-12-05 15:00   ` Do-While Jones
  1 sibling, 0 replies; 4+ messages in thread
From: Do-While Jones @ 1994-12-05 15:00 UTC (permalink / raw)


In article <3briug$9ab@news-2.csn.net> perez@oldcolo.com (Carlos Perez) writes:
>Do-While Jones (do_while@owens.ridgecrest.ca.us) wrote:
>
>: Since the USE clause makes the operators visible, I just write things like
>: "VELOCITY := DISTANCE / TIME;" and Ada does all the dimensional checking
>: for me.  If I write "VELOCITY := DISTANCE / VOLTS;", I naturally get an
>: error message.  Of course I can respond to the error message by doing
>: something really stupid, like "VELOCITY := DISTANCE / Seconds(VOLTS);", but
>: I am not that dumb yet.
>
>It appears to me that you have designed an idiot-resistant system
>(there is no such thing as idiot-proof ;-) 
>

I regret including that paragraph.  It caused more confusion than 
enlightenment.  I was trying to say that it is idiot-resistant but not 
idiot-proof.  I just didn't say it very well.

>I thought that your VOLTS was a private type and so you can't 
type >cast VOLTS into SECONDS.  You may need to have some functions whose
>sole purpose is to convert dimensional types into scalar (non-dim) types
>like Volts_To_Float and Float_To_Volts so you can define new and improved
>cross-dimensional operators such as "*". 
>
>My point is that Ada won't permit a simple cast as you described, unless
>I missed something? 
>

Yes, you did miss something.  Volts and Seconds are private types.  They 
are DERIVED types that happened to be derived from a private type.  So, 
you can do a simple type cast.  For example, this program compiles 
without error:

with TIME_UNITS;
with ELECTRICAL_UNITS;
procedure Really_Stupid is
  S : TIME_UNITS.Seconds;
  V : ELECTRICAL_UNITS.Volts;
  use ELECTRICAL_UNITS; -- for explicit type conversion
begin
  V := Volts(S);
end Really_Stupid;

Which just goes to show that if you try really hard, Ada will let you 
shoot yourself in the foot.  But you have to try hard to do it, and it is 
pretty obvious when you do.

>: Do-While Jones
>
>When-Others Perez 


-- 
            +--------------------------------+
            |    Know                 Ada    |
            |        [Ada's Portrait]        |
            |    Will              Travel    |
            | wire do_while@ridgecrest.ca.us |
            +--------------------------------+



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~1994-12-05 15:00 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1994-12-03  0:06 Dimensional Data Types Do-While Jones
1994-12-04  5:09 ` Carlos Perez
1994-12-05  1:39   ` Carlos Perez
1994-12-05 15:00   ` Do-While Jones

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox