Some Thoughts About Resources

An Introduction

Let's assume for now that a resource is (by definition) something that gets allocated, used, and then deallocated. Examples include a block of memory that should be released, a file that should be closed, and so on. Some type systems (affine type systems, linear type systems, maybe others) seem to explicitly account for the existence of resources. Setting that aside, let's focus on the concept of resources from an object-oriented perspective.

An Argument

In C#, the idiomatic representation of a resource is an object that implements the IDisposable interface. This interface exposes a single method called Dispose that accepts no arguments and returns no values. Implicitly, the method has an impure postcondition; it changes the state of some external system such that the resource is no longer valid. This is very strange.

One of the basic rules of object-oriented programming is that you should only change the state of an object by invoking one of the object's methods. Another basic rule is that objects should never exist in an invalid state. Why do resources seem to violate these rules?

Perhaps you could argue that you aren't changing the state of an external system by deallocating a resource? But I'm not sympathetic to that argument. If you want to open a file, then you need to change the state of the filesystem. If you want to close a file, then you need to change the state of the filesystem again. The filesystem is external to a file in the same way that a library is external to a book. If this isn't already obvious, then trying to implement a mock filesystem should make it more clear.

Perhaps you could argue that it's okay to invoke a filesystem method indirectly, when you invoke Dispose? But I'm not sympathetic to that argument, either. Yes, you can do that; it's basically what happens when you close a file stream in C# using the standard library. But it makes about as much sense as telling a book to take itself back to the library.

Let's assume for now that you want to ignore the IDisposable pattern in favor of e.g. a filesystem service with one method to open files and another to close them. If you imagine a sequence diagram for this approach, then you should see a pair of service objects represented by vertical lines. Early in the diagram, you should see an exchange which opens a file: A request that contains a path, and a response that contains a file handle. Later in the diagram, you should see an exchange which closes the file: A request that contains a file handle, and a response that contains an empty tuple. There aren't really any resource objects in this diagram; even the file handle is better understood as a resource identifier. So what exactly is a resource object?

The premise of the question is flawed, because a resource isn't an object. We say that a resource exists when an identifier has a particular relationship with the current state of some external system. That relationship is the relationship that exists between allocation and deallocation. But a resource doesn't have its own internal state, and it can't send or receive messages, so it isn't an object.

Comments

That's an argument for using the word "resource" in a particular way, but it's not necessarily a good argument. I'm still trying to understand these concepts, so I should probably start with some observations.

First, a called method does not generally take ownership of a resource from a calling method that passes the resource identifier as an argument. Instead, the resource is "borrowed" and then (implicitly or explicitly) returned to the caller when the called method returns.

Second, a called method does not generally yield ownership of a resource to the calling method as a return value. Instead, the resource is typically deallocated in the same method that allocated it.

Third, a calling method can "borrow" a resource from a called method if the called method has a delegate parameter that accepts a resource identifier as an argument. Basically, the calling method defines a delegate, and the called method invokes it between allocation and deallocation of the resource.

Fourth, a garbage collector seems to be a mechanism for subverting the rule about deallocating a resource in the same method that allocated it. This allows a calling method to "borrow" a resource from a called method without using delegate parameters, but I'm not sure when (if ever) that's desirable.

Finally, an object seems to be a type of resource. We say that an object exists when a memory address has a particular relationship with the current state of process memory. That relationship is the relationship that exists between (successful) invocation of the constructor and (successful) invocation of the destructor. And it's exactly that relationship which makes it possible for the object to support internal state and message passing.

If you try to visualize this with a sequence diagram, you see something kind of interesting. The relationship between the object's memory address and process memory visibly exists for a specific period of time, and it's bounded by the exchange of specific messages. Those messages represent invocation of a constructor and a destructor, and they're being sent (directly or indirectly) to an "object" that represents process memory. So long as the relationship exists, you have a new "object" that can send or receive messages, but this object seems to exist on a different layer of abstraction.

Appendix

// This pattern can be used to enforce deallocation of resources.                             
public class FileSystem : IFileSystem {                                                       
    public TResult ReadFromFile<TResult>(string path, Func<Stream, TResult> fn) { 
        using (var stream = File.Open(path, FileMode.Open, FileAccess.Read)) {                
            return fn(stream);                                                                
        }                                                                                     
    }                                                                                         
}