Hi, am I missing something ? Why is the following illegal ?
type Variant_Record (Kind: TKind) is record
case Kind is
when Circle => Radius: Float;
when Rectangle =>
Length: Float;
Width: Float;
when Square =>
Width: Float;
end case;
end record;
I have to go through unreadable hoops to get the same result:
type Variant_Record (Kind: TKind) is record
case Kind is
when Circle => Radius: Float;
when others =>
Width: Float;
case Kind is
when Rectangle => Length: Float;
when others => null;
end case;
end case;
end record;
It doesn’t seem logical, I would need to test for Kind before accessing any of those field, so why can’t they bear the same name, even though the exemple above does exactly that anyway ?
FWIW that has annoyed me in the past, as well. But I think the reason is that you may not know at compile-time precisely which TKind you have. That is, you can have something liek this:
procedure Do_Something_Stupid_With (Figure: in out Variant_Record) is
begin
Figure.Width := @ * 2;
-- use of @ from Ada 2022, not sure if you're familiar w/it yet
end Do_Something_Stupid_With;
Now, maybe you want to double the width regardless of whether it’s a Rectangle or a Square. Maybe you want to double the width only if it’s a Square. Maybe someone is working with variants in a different situation and has a lot of them and while two of them could have a Width field, they mean completely different things. Given all the possibilities for ambiguity, it’s not unreasonable to force the second approach.
I don’t know that that’s why Ada doesn’t allow the first approach, but I can definitely see these reasons carrying the day in a discussion.
FWIW, I don’t think it occurred to me to try the second approach, so kudos.
The test for Kind might be separated a long way from the reference to the field, so it is not generally possible for the compiler to know what variant you are talking about when it sees a reference to the “Width” field. If all fields named “Width” in fact have identical properties, then you are correct that the compiler could make it all work, but typically that implies that you actually have a subrange of TKind that represents the kinds of objects that have a Width field, and it might makes sense to give a name to such a subrange, and then you can use that name rather than “others” to label the variant part devoted to objects that have a Width.
If not all fields named “Width” have identical properties, then the problem I mentioned in the beginning applies, and the compiler would need to guess which one you were talking about, and the compiler generally doesn’t like to “guess”.
Huh, I complained but had an inkling it would be that way. Makes sense.
So in actuality using an “others” case value is kinda giving an anonymous subtype to the two underlying cases, and duplicating the field’s name doesn’t ensure the fields really are identical because there is no subtype conformance test or any test at all. Understood.
Namely within aggregates and within field selection. Consider:
Type Example( Discriminant : Boolean:= False ) is record
case Discriminant is
when True => Value : Some_Float;
when False => Value : Some_Fixedpoint;
end case;
end record;
Now, consider ( Value => 1.0, others => <> ).
While technically you could loop back to the definition and disambiguate, saying “ohh, since it’s false Value is the fixed-point”… and that’s a fair point, but enter component selection, given that this construct has a default-discriminant Ada allows for whole-assignment replacement, so, what does Some_Object.Value mean: is it the fixed-point, or the floating-point?
I think it’s the right call not to allow variant-parts to have the same-names: it allows the compiler to correctly confirm the type that you actually want there.
There is a trick that you can use to do what you want, though: overloading.
Function X( Object : Example ) return Some_Fixedpoint;
Function X( Object : Example ) return Some_Float;
Procedure X( Object : in out Example; Value : Some_Fixedpoint );
Procedure X( Object : in out Example; Value : Some_Float );
Handling all the internal state-changes as necessary within the implementation.