This looks very untyped. Are apple and lemon seeds are of same type? Surely not, so they cannot be Unconstrained_Seeds. Same is with fruits, there cannot be Unconstrained_Array_Fruits.
If you use indefinite elements in arrays you need pointers. If you use pointers you need some pool. If you use a standard pool the representation is non-contiguous. So to ensure contiguity you need a special pool and a strategy to allocate elements. For example a stack pool on top of some array of storage elements would guarantee you contiguity but also you would lose some memory because the overall size is unknown in advance.
Initialization does not require body if standard array aggregate can be used.
Otherwise taking your picture literally:
type Seed is new Integer;
type Array_Of_Seeds is array (Positive range <>) of Seed;
subtype Apple_Seeds is Array_Of_Seeds (1..4);
subtype Lemon_Seeds is Array_Of_Seeds (1..3);
type Array_Of_Apples is array (Positive range <>) of Apple_Seeds;
type Array_Of_Lemons is array (Positive range <>) of Lemon_Seeds;
type Basket (Apples_Count, Lemons_Count : Natural) is record
Apples : Array_Of_Apples (1..Apples_Count);
Lemons : Array_Of_Lemons (1..Lemons_Count);
end record;
X : Basket :=
( Apples_Count => 3,
Lemons_Count => 2,
Apples => ((1,2,3,4), (4,5,6,7), (8,9,10,11)),
Lemons => ((12,13,14), (15,16,17))
);
You cannot have array of fruits because in Ada arrays cannot have discriminants. So you can pass a constraint down to array elements.
You can use 2D array to make it as untyped as possible:
type Seed_Count is new Integer;
type Fruit_Count is new Integer;
type Fruit_Seeds is
array (Fruit_Count range <>, Seed_Count range <>) of Seed;
type Basket1 (Apples_Count, Lemons_Count : Fruit_Count) is record
Apples : Fruit_Seeds (1..Apples_Count, 1..4);
Lemons : Fruit_Seeds (1..Lemons_Count, 1..3);
end record;
Y : Basket1 :=
( Apples_Count => 3,
Lemons_Count => 2,
Apples => ((1,2,3,4), (4,5,6,7), (8,9,10,11)),
Lemons => ((12,13,14), (15,16,17))
);