Hacker News new | past | comments | ask | show | jobs | submit login

Off the top of my head there are two ways:

- Lexically scoped access types have storage pools that are destroyed when the type goes out of scope. This is similar to region-based memory management as in Cyclone[0].

- Limited controlled types are a lot like linear types and prevent unwanted aliasing and ensure deallocation happens correctly, as described in this paper[1].

[0]: https://en.m.wikipedia.org/wiki/Cyclone_(programming_languag...

[1]: https://dl.acm.org/doi/10.1145/165354.165362




I think I understand these. I think these solutions work where it is very clear what the scope of object should be.

But what about cases in large projects with many developers where a library/package allocates an object on the heap and passes it back to a caller? Sometimes it is not clear who is responsible for deallocating the object. This is the challenge we ran into building large projects in C, C++, and Ada. These days, I use Java, C#, Rust, or Go, because I know there will be no dangling pointers.


>a library/package allocates an object on the heap and passes it back to a caller

Limited controlled types handle this case.

Limited types prevent assignment (i.e. aliasing). Controlled types introduce destructors so everything is closed even when an exception is thrown.


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.


IIRC a circular graph would typically be done via pool-specific access types.

    -- 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 haven't written enough Ada to know the answer (I've only researched the memory management scheme as part of implementing a language with compile-time ownership tracking).

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.


Maybe the semantic problem you are trying to solve should be not solved but avoided.

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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: