Ideal Language

The Ideal Language cannot leak resources

The ideal programming language should not leak resources. Resources include not just memory, but also files, network connections, device locks, sound buffers, and anything needing to be acquired and released. Even an attentive programmer can forget to cleanup or be confused by a complicated API. For the vast majority of use cases resources should not be allowed to be lost.

Implications

The clearest implication is mandating an RAII (resource acquisition is initialization) pattern. Resources are to be allocated via a constructor to an object and released when the object is destroyed. It is very important that the resources are released as soon as  possible, and not sometime later. Delaying resource release can introduce artificial scarcity in the system. This has strong implications for object management and garbage collection.

To be robust this mechanism must include memory. As the most frequently used resource it makes sense that memory is automatically managed. Thus you can’t really have any malloc and free functions. You can’t even really have new and delete functions. Well, you can have the allocation functions, you just need to ensure that the memory is automatically released like any other resource.

Naturally this all implies a good exception system. We already assume that the language must have exceptions. With respect to resources it implies that the cleanup mechanism during stack unwinding is very clear. Essentially, regardless of how a scope is left, the resources allocated in the scope must be released.

Exclusions

A determined programmer will always have a way to violate this rule in a language. Nothing can really prevent a cache of objects that always grows and never shrinks. Since people will always use caches and collections it is very important to provide a good assortment of them. This provides standard code instead of ad-hoc code. Weak pointers will also be needed, and perhaps other exotic pointer types to give flexibility to API authors.

Questions

How far this goes is a big quesiton. Scanning garbage collection is insufficient to guarantee that resources are released quickly. At the same time without such garbage collection a reference loop would go unnoticed.  Resource loops are a big problem as they prevent simple reference counting. Reference counting can of course be used, and should be, but something more is needed. Optimally all resources should be freed immediately, including those inside reference loops. How this is to be done is an open question.

Categories: Ideal Language

Tagged as: , , ,

9 replies »

    • I find it hard to believe that RAII objects would ever be used in a recursion situation. Just because a class has a destructor it doesn’t mean it is working with a system resource.

      That aside, the article mentions the F# compiler in a particular circumstance. In the general case I see no reason why the optimizer couldn’t manage the calls to the destructors and constructors as to remove the recursion.

    • Never mind RAII and resources, the problem with tail-call optimization lies in destructors. Because they must be executed last, it means you can’t have your call in tail position.

      You are right about the optimization. The compiler could in theory automatically modify the recursive function to use continuation-passing style, passing calls to the destructors as a continuation. I had forgotten about that.

    • That’s true, but I think I was considering something even simpler than that. If dealing with simple data, which is often the case in recursion, the destructors may be trivial, or very simple. Post-inlining the optimizer could make the determination that the destruction can actually happen at any time (since it involves either no code, or has no further dependencies in the function).

  1. The D language version 2 has the “scope” operator, which is a language feature that makes it easier to dispose of resources.
    x = // some resource
    // when leaving this scope, close X, no matter what
    scope(EXIT, x.close());
    // when leaving this scope without an error, close X. Otherwise leave it open
    scope(SUCCESS, x.close());
    // when leaving this scope due to an exception, close X. Otherwise leave it open
    scope(ERROR, x.close());

    You can forget to use the scope operator, of course, so it’s not perfect. But it lets you put your resource cleanup code sequentially immediately after your resource acquisition, instead of at the end of your exception handling block.

    • I like deferred code, I think Go also has defer. I’ve added “defer” to my own Leaf language as well, though not as flexible as in D. I debated with some other people and they don’t believe it needs to be so flexible, as it would get confusing. So I have only “defer”, which executes code anytime the scope leaves, and “defer_error” which executes code only when an error occurs.

      I was tempted to add “defer_noterror” (scope(SUCCESS)), but this is simply normal program flow. Those statements just go at the end of the block. I couldn’t actually think of a good use-case where it’d be better expressed in a defer statement.

    • I think having all three, in Leaf’s case defer, defer_error, and defer_noterror, lets you make things more consistent. It seems odd to me to have one or two portions of your resource cleanup code right below the resource acquisition in most of the software, but in the odd case when you have special handling on success you have the cleanup at the bottom of the block. I think defer, defer_error, and defer_noterror would be more consistent.

      But it wouldn’t be a deal-breaker either way. In general, good luck on creating your language. That’s a hell of an undertaking.

    • Well, that’s just it. If the use-case is primarily resource cleanup, I’m unable to think of a situation where you’d cleanup on normal flow, but not error flow. Do you have one in mind?

      Adding support for defer_noterror is quite simple now (small variant on other two). So it’s truly only a “choice” that it isn’t in the language (to avoid unneeded complexity).

      Thank you for the good luck, I’ll definitely need it.

    • I can’t think of a common use case for cleaning up except on error, but as a rare but feasible case I can imagine that you have some kind of communication relay. Information comes from source A to you and you process it and forward to destination B. So you have two handles, one to A and one to B. If your connection to A is disrupted, maybe you keep the handle to B open in order to transmit an error message.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s