4 ms·
There's an interesting example of resource management in Haskell with monads [1], but it's probably not easy to follow if you aren't already steeped in Haskell
by rbehrends 10y ago
There's an interesting example of resource management in Haskell with monads [1], but it's probably not easy to follow if you aren't already steeped in Haskell lore, so let me be a bit more basic.
First, note also that it would not be particularly hard to add RAII on top of a garbage-collected language to coexist with GC for resource management; it's just not done in practice. And it's not because language designers are ignorant of it (Bjarne Stroustrop's Design and Evolution of C++ is part of the standard recommended reading list in the field).
Generally, you want resource usage to be a provable property of a program. Not that you'd actually write a formal proof, but you generally want to be able to explain at least informally why resource usage follows certain constraints (e.g. having certain upper bounds).
The basic insight that you need is that resource lifetime is just another semantic property that you can handle with basically the same techniques as other properties of programs; you do not need special language support for it (though, obviously, it helps if your language is a bit more expressive than a Turing machine :) ).
This means that you'll generally tie resource usage to program state and program behavior that you can reason about. The incidental semantics of object lifetime can be dangerous, especially in a functional language, as object lifetime can sometimes be unpredictable.
One of the major hiccups are closures. Closures capture their environment (including local variables) and if they survive the stack frame that created them (because they are returned or stored on the heap), then the lifetime of any captured object can be extended in a fairly unpredictable fashion. Obviously, that is not a good thing, as you have a hard time proving lifetime properties, but few functional programmers would limit themselves to a more trivialized use of closures just for the sake of RAII.
Instead, as I said, you tie resource management to program behavior or state. In the most simple case, that can be scoped resource management. But it can also be an LRU cache, a system based on transactions, or something else entirely. Here's a simple example of a library I sometimes use in OCaml:
class example = object
inherit Tx.resource
initializer print_endline "create"
method release = print_endline "close"
end
let _ = Tx.scoped (fun () -> new example)
This is a simple lexically scoped transaction, but the library also allows for chained, nested, etc. transactions that don't begin and end based on lexical scope, but (say) program events (e.g. terminate a transaction when a socket is closed from the outside and release resources that are associated with that connection). It can also distinguish between commit and abort behavior (similar to the Haskell example above), will properly error if resource creation is not done within the context of a transaction, plus a few other bells and whistles.
[1] https://aherrmann.github.io/programming/2016/01/04/resource-management-in-haskell/ https://aherrmann.github.io/programming/2016/01/04/resource-...