I have a bunch of coordinate systems that I have to use in my personal project. It would benefit me if I could somehow store a meters type within a kilometer type.
A summary of what I think that I am aiming for here is that if something is 8km,500m,070cm,003mm on the x axis, then I wouldn’t have to store that as 8.500_703 inside type Kilometer is delta 10.0**(-6) range -10.0 .. 10.0; but could instead have it stored as a cascade of all relevant types, with everything being put into the largest type that it can go in. I think perhaps this is achievable with additional subprograms, but I really feel as if I could (mis)use the type system in this way.
I was trying weird things that weren’t legal such as:
type Custom_Kilometers is range -10 .. 10;
subtype Custom_Meters is Custom_Kilometers range 1 .. 1 delta 10.0**(3);
I think the second line in the example shows what I am trying to do. To have a parent type, and then add more detail. I understand using the delta like this is wrong, but since it adds decimals I am trying to show my intent in illegal code. -10 .. 10 km -> 1 .. 1 km -> 1000m .. 1000m
If I pass 1000m into a meters type I would like it to convert to 1km. Wishful thinking I guess.
-- I know this doesn't work, but I am trying to show my intent,
-- as I cannot seem to describe my problem well otherwise.
Some_Weird_Example_Name : Collapsing_Distance_Type := ( km => 2,
m => 4500,
cm => 3,
mm => 5 );
-- Example internal memory representation would be 6km500m3cm5mm etc...
-- the meters for example would left as 500, and the 4k meters into 4km.
I’m not asking anyone to do the work for me. I just need to know if something like I am asking is even possible. It feels like such a weird thing that I am trying to do here, but it’s what I’m after. Is this a strategy for distance storage, is there an alternative, or am I chasing my tail again?
EDIT: I accidentally deleted and redeleted the post trying to edit, but I am going to try an idea I just had after putting this post up. I’m not completely dead in the water just yet! Somehow I always manage to have an idea immediately after posting something.
After looking over my initial refusal to use additional subprograms I understand that the way I could achieve the collapsing behavior by just having a function that takes in all the different distance units, and collapses them smallest to largest, which would be trivial to implement.
But the first part that I am after to use the type system still stands. I’ll try more in the morning, can’t think straight right now.
What exactly is wrong with using a fixed-point decimal type for this? No one can tell you how to have a type that’s different from a fixed-point decimal without knowing what property you’re trying to avoid or achieve.
The morning is here, and I am now facepalming really really hard. I was trying to be more clever than I needed. Fixed point will probably work for what I need. Thanks.
John Barnes, in his book, has a similar thing. The method he more or less uses is you declare a type of metre (or however you prefer to spell it, depending on whether you write it in English, French or American). Then you declare a constant that is kilometre : constant metre := 1000.0; (and you do that for mm, etc). Then, when you want to write 10km, you write it as 10 * kilometre. It is a simple method that I like
You need all cross-type operations, which you simply cannot declare statically. The compiler (or a preprocessor) can. In fact AdaCore has such a thing built-in as an aspect. See Aspect Dimension (GNAT Reference Manual)
I rather use a subtype solution with the unit being is a discriminant of the corresponding type, because in practice you frequently do not know the unit statically. As an example consider a scientific plotting program.
Note also that it is not about real types only. You might wish to have dimensioned complex numbers, vectors, matrices, fuzzy numbers etc.
Of course, for the more complex situation of combining distance with time (that is, dealing with velocity) and, potentially, with the other types such as complex numbers, vectors, phasors and the like, the world becomes a little more complex as Dmitry rightly points out (in addition to Dmitri’s link, also check out 3.11 Aspect Dimension_System in the GNAT RM - it gives an example of what Dmitri is talking about). Use of the Aspect Dimension, of course, would be a reasonable approach, but if you have libraries around (as I do) that date way back (some of my libraries were built under Ada ‘83 and I still use them even though I might have modernised their internals where it made sense and was straight forward to do so), then your approach can be to simply use those libraries. The nice thing about Ada is it allows you to do that.
Note that, when it comes to Aspects, you should heed the (GNU GCC) warning that maybe not all compilers may implement them (all) since the Dimension aspect is an implementation defined aspect that is specific to GNAT (but GNAT does implement it and does so across all the platforms that it works on, so if you are going to use GNAT on all targets for your code, then you are good to go).