Is there anyway to implement Resource Acquisation is Initialization in Scheme?
I know that RAII does not work well in GC-ed languages (since we have no idea whe the object is destroyed). However, Scheme has nice things like continuations, dynamic-wind, and closures — is there a way to use some combination of this to implement RAII?
If not, how do schemers design their code to not use RAII?
[A common example I run into is the following:
I have a 3D mesh, I have a Vertex Buffer Object atached to it,
when the Mesh is no longer used, I want the VBO freed up.]
Thanks!
If this is just a one-off, you could always just write a macro that wraps around
dynamic-wind, doing the setup and teardown in the before and after thunks (I’m assuming thatallocate-vertex-buffer-objectandfree-vertex-buffer-objectare your constructor and destructors here):If this is a pattern that you use a lot, for different types of objects, you might write a macro to generate this sort of macro; and likely you are going to want to allocate a series of these at a time, so you may want to have a list of bindings at the beginning, instead of just a single one.
Here’s an off-the cuff, more general version; I’m not really sure about the name, but it demonstrates the basic idea (edited to fix infinite loop in original version):
And you would use this as follows:
Here’s an example that demonstrates it working, including exiting and re-entering the scope using continuations (this is a rather contrived example, apologies if the control flow is a bit hard to follow):
This should print:
call/ccis simply an abbreviation ofcall-with-current-continuation; use the longer form if your Scheme does not have the shorter one.Update: As you clarified in your comments, you are looking for a way to manage resources that can be returned out of a particular dynamic context. In this case, you’re going to have to use a finalizer; a finalizer is a function that will be called with you object once the GC has proven that it cannot be reached from anywhere else. Finalizers are not standard, but most mature Scheme systems have them, sometimes under different names. For instance, in PLT Scheme, see Wills and Executors.
You should keep in mind that in Scheme, a dynamic context can be re-entered; this differs from most other languages, in which you may be able to exit a dynamic context at any arbitrary point using exceptions, but you cannot re-enter. In my example above, I demonstrated a naïve approach of using
dynamic-windto deallocate resources when you leave the dynamic context, and re-allocate them if you enter again. This may be appropriate for some resources, but for many resources it would not be appropriate (for instance, re-opening a file, you will now be at the beginning of the file when you re-enter the dynamic context), and may have significant overhead.Taylor Campbell (yes, there is a relation) has an article in his blag (the 2009-03-28 entry) addressing this issue, and presenting a few alternatives based on the exact semantics that you want. For instance, he provides an
unwind-protextform that will not call the cleanup procedure until it is no longer possible to re-enter the dynamic context in which the resource is accessible.So, that covers a lot of different options that are available. There is no exact match to RAII, as Scheme is a very different language and has very different constraints. If you have a more specific use case, or more details on the use case that you mentioned briefly, I may be able to provide you with some more specific advice.