Hi all, while I understand what these operations are doing and how they’re different, I’m having a hard time thinking about when exactly to use each. Clearly, explicit type conversions are needed when going between say floating point types and integer types, but is there a set of best practices /rules on when each should be used? Claude told me it’s a good practice to qualify literals, for example. TIA for help with the newbie question. Just trying to learn best practices.
In most cases, the only reason to use a qualified expression is when the compiler complains that a construct is ambiguous due to overloading, and needs to be “told” what particular type is expected at a particular point in the program. In more obscure cases, a qualified expression can be used to limit the possible values to a given subtype, which can, for example, eliminate the need for an “others” alternative in a “case” statement.
Conversion is needed when you have a value of one type, but you need a value of a different type, and the types support explicit conversion between each other.
Note that the Ada RM usually has an example or two in the section where a particular kind of construct is defined. E.g. for qualified_expression, see Qualified Expressions
I do not agree with Claude’s advice about literals. You should generally use literals (other than perhaps 0 or 1) only when defining named constants, and in those cases you can give a type explicitly in the constant’s declaration, so no further qualification is needed.
“Qualified” is used for disambiguating.
Consider the following:
Function X return Integer;
Function X return Float;
Procedure Z( Object : Integer );
Procedure Z( Item : Float );
-- …what happens with this?
Z( X );
Now, there are actually two ways to disambiguate which X
you’re using:
- Tell the compiler what the type of
X
is:Z( Float'(X) );
— this is qualification; or - Use named association:
Z( Item => X )
— this obviously will not work if the parameters are the same name.
Type conversion is different in that you’re telling the compiler “use this thing as that thing”, though not “at the bitpattern level” thatUnchecked_Conversion
does; consider:
Type Fahrenheit is new Integer;
--- …
Procedure Set_Thermostat( Temp : Fahrenheit ) is
IO_Port : Integer with Import, Address => CONSTANT_HARDWARE_ADDRESS;
Begin
IO_Port:= Temp; -- Error: type-mismatch.
End Set_Thermostat;
In this case we need to take Temp
and use it as the parent-type Integer
, so we would say Integer(Temp);
—this is conversion— though this is a contrived example, we could simply make IO_Port
of type Fahrenheit.