I am not an Ada expert, but I came up with this. Please notice that the idea here is not to have the compiler do proper dimensional analysis but simply to avoid the default operator overloading of "*" for the Distance type that also returns a Distance value.
Nothing very high-level, as we need to explicitly manipulate the single-component records, but it does the job.
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
type Distance is record
Value : Float;
end record;
type Area is record
Value : Float;
end record;
function "*" (Left, Right : Distance) return Area is
begin
return (Value => Left.Value * Right.Value);
end "*";
D1 : constant Distance := (Value => 10.0);
D2 : constant Distance := (Value => 20.0);
-- D3 : constant Distance := D1 * D2;
-- Does not compile. GNAT returns the following error:
-- main.adb:21:33: expected type "Distance" defined at line 4
-- main.adb:21:33: found type "Area" defined at line 8
A : constant Area := D1 * D2;
begin
Put_Line ("Area A is " & Float'Image(A.Value));
end Main;*
Re:Ada for the C++ and Java Developer [pdf]
by yannickmoy on Monday March 29, 2021 @03:38AM
(#26618624)
As someone said, the best solution here is to use the dimensionality analysis in GNAT: https://docs.adacore.com/gnat_ugn-docs/html/gnat_ugn/gnat_ug...
as in:
with Ada.Text_IO; use Ada.Text_IO;
with System.Dim.Float_Mks; use System.Dim.Float_Mks;
with System.Dim.Float_Mks_IO; use System.Dim.Float_Mks_IO;
procedure Main is
subtype Distance is System.Dim.Float_Mks.Length;
subtype Area is System.Dim.Float_Mks.Area;
D1 : constant Distance := 10.0*m;
D2 : constant Distance := 20.0*m;
-- D3 : constant Distance := D1 * D2;
-- Does not compile. GNAT returns the following error:
-- main.adb:13:08: dimensions mismatch in object declaration
-- main.adb:13:37: expected dimension [L], found [L**2]
A : constant Area := D1 * D2;
begin
-- print: Area A is 2.00000E+02 m**2
Put ("Area A is ");
Put (Item => A);
Put_Line ("");
end Main;Oh, thanks for pointing it out. Honestly, I was more interested in leveraging the type system than on proper dimensional analysis. I could find no mechanism by which Ada allows the programmer to disable the default operation overloads, so I decided to try that out.
Re:Ada for the C++ and Java Developer [pdf]
by OneWingedShark on Tuesday March 30, 2021 @09:28AM
(#26634356)
IIRC you can disable/forbid the default like so:
Function "*"(Left, Right: Distance) return Distance is abstract;
then use: Function "*"(Left, Right : Distance) return Area;That seems to do the trick for an individual predefined operator specification. Thanks!
This is the right way to approach it on my opinion.
Re:Ada for the C++ and Java Developer [pdf]
by foerbert on Sunday March 28, 2021 @08:14AM
(#26609666)
There is now, at least with Ada 2012 and GNAT[0][1]. While I'm pretty sure only GNAT has implemented it, I'm not sure if it's actually in the 2012 standard or not.
I haven't played with it yet, but it does looks pretty interesting.
[0]https://gcc.gnu.org/onlinedocs/gcc-4.9.4/gnat_ugn_unw/Perfor... [1]https://blog.adacore.com/uploads/dc.pdf
PTC also supports Ada 2012.
Re:Ada for the C++ and Java Developer [pdf]
by bob-ROS on Sunday March 28, 2021 @07:39AM
(#26609493)
I got it working with:
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
type Distance is new Float;
type Area is new Float;
function "*" (Left, Right : Distance) return Area is
temp : Distance;
begin
temp := Left * Right;
return Area (temp);
end "*";
D1 : Distance := 2.0;
D2 : Distance := 3.0;
A : Area;
begin
A := D1 * D2; -- OK
Put_Line(A'Image);
end Main;
Interestingly, it took me several attempts to create this overload, make it a single line return would make it recursive: function "*" (Left, Right : Distance) return Area is
begin
return Left * Right;
end "*"; -- ^ Does not work
-- raised STORAGE_ERROR : stack overflow or erroneous memory access
Also: function "*" (Left, Right : Distance) return Area is
begin
return Area (Left * Right);
end "*"; -- ^ Does not work either, compiler gives me "ambiguous operand in conversion"Could you do
return Area(Float (Left) * Float (Right));
Re:Ada for the C++ and Java Developer [pdf]
by bob-ROS on Sunday March 28, 2021 @08:23AM
(#26609738)
Yes, that works.
Re:Ada for the C++ and Java Developer [pdf]
by webreac on Sunday March 28, 2021 @04:29PM
(#26614179)
Could you do
return Area (Distance (Left * Right));
?
Re:Ada for the C++ and Java Developer [pdf]
by OneWingedShark on Tuesday March 30, 2021 @09:33AM
(#26634413)
IIRC, the one-liner here [assuming you disabled the default "" operator] would be:
Return Area( Distance'Base'(Left * Right) );
The apostrophe at the inner portion is 'qualification', a method for directing the compiler that the enclosed value is supposed to be a particular subtype. (Typically used to resolve ambiguities.)There's a library for that: http://www.dmitry-kazakov.de/ada/units.htm
Re:Ada for the C++ and Java Developer [pdf]
by Avshalom on Sunday March 28, 2021 @07:14AM
(#26609378)
It does have operator overloading so you could certainly define some
function "*"(Left, Right : Distance) return Area
and if dedicated enough build up a whole set of unit-types with corresponding conversion rules.Not an Ada guy at all though so no clue if there's a less by-hand way of doing it.
The Units library does exactly what you describe.
http://www.dmitry-kazakov.de/ada/units.htm
I can get why you have to be explicit in type casts if you're trying to be safe, but is there any way to say that
Or So that of I say a: Area and b : CubeVolume and a := d1 * d2 it just works, and then b := a * d3 also works.IOTW, explicit casts not needed.
Or is it the case that Ada really took the concept of "explicit is better than implicit" and went to town?
I just wonder because I love F#'s unit of measure types and how they can prevent logical type errors (say, multiplying 10 m/s by 3kg when assigning to a variable of type m/s^2).
Or for this case, if I wrote
It'd fail hard, because a Distance * a Distance is an Area.I sorta figured Ada would have invented this sorta stuff and then it was cribbed by other languages.
So yeah, wondering what the other mechanisms the author mentions are. Because I've always heard Ada is great at type safety, so I'd be keen to see how safe it makes your code when combining values with units that aren't logical to combine.