I see how limited controlled types allow for reference counting. I don't see how they can be used to implement circular graphs and situations where it is not clear who needs an object to continue to exist.
-- Forward declaration.
Type Element(<>);
-- Assuming there's a Graph.Pool implementation of the base Storage_Pool object.
Type Pointer is access Element
with Storage_Pool => Graph.Pool;
Subtype Handle is not null Pointer;
Type Children is array(Positive range <>) of Handle;
Type Element(Parent : Handle; Child_Count: Natural) is record
Data : Integer; -- Or whatever your actual data would be.
Link : Children( 1..Child_Count );
end record;I think the Ada answer would be to keep everything that has a lifetime, anything that's a resource, in its own module behind a strict limited controlled interface. But that's a very vague answer.
Much like "spaghetti code" is not only considered poor style but isn't even supported by modern languages, "spaghetti data" should also be considered a bad pattern, which more advanced languages force you to avoid.