5 ms·
That only works if the lifetime of a resource is a lexical region, right?
by ben0x539 12y ago
That only works if the lifetime of a resource is a lexical region, right?
- agumonkey 12y agoGood question. I wonder if there was some Lisp with linear logic that would allow lexical and ~borrowed (sorry if I misuse the term) resources management.
- malisper 12y agoIt shouldn't be very hard to create a basic ownership system. Here is a version I just wrote, which works by defining a new version of "defun" that uses dynamic variables to keep track of ownership: (defvar *transferring* '() "Objects which are going to be transferred from one owner to another.") (defvar *resources* '() "The resources which need to be freed when the current procedure is done.") (defmacro new-defun (name args &body body) "Define a procedure which will have ownership over the resources it creates using any of the 'new' procedures. `(defun ,name ,args (let ((*resources* *transferring*)) (setf *transferring* '()) (unwind-protect (progn ,@body) (mapcar #'free *resources*) (mapcar #'free *transferred*))))) ; These are resources which were supposed to be transferred by weren't due to an error. (defun transfer (resource) "Transfer ownership of a resource from the current one to the next procedure called which can accept ownership." (push resource *transferring*) (setf *resources* (remove resource *resources*)) resource) (defun new-open (&rest args) "Open a file whose owner is the first procedure up the stack which can accept ownership." (let ((file (apply #'open args))) (push file *resources*) file)) All that is needed now is a method "free" which will free any given resource. With this, any resource allocated with a "new" procedure (the only one I wrote was "new-open") will by put under ownership of the last procedure on the stack which was defined with "new-defun". Transfer between owners can take place by using "transfer". The best part is that more facilities are easy to build on top of this. The "drop" procedure would be trivial to add. The only problem I can see here is that there may be some issues if "free" throws an error, since "unwind-protect" doesn't protect the cleanup-forms. This should be fixable by just wrapping every call to "free" with "ignore-errors".
- mercurial 12y agoThe 'with' idioms I've seen in the past do, which, combined with yield operators, can make for some unexpected 'fun' at runtime, but it remains extremely convenient most of the time.
- lambda 12y agoRight. One case in which I've come across this problem in Python is writing unit tests for code using Twisted (and Trial, Twisted's unit test framework) with @inlineCallbacks, and trying to also use this style of resource management for mocks (or, essentially the same, the @patch decorator to set up the mocks). If you have something like: @patch('some_module.some_object') @inlineCallbacks def test_my_stuff(self): do_stuff() yield some_operation() do_more_stuff() self.assertFalse(True) Then you run into the problem that @patch has cleaned up all of your mocks by the time the yield returns. Now, I haven't tried to do anything equivalent in Rust; I'm not sure what the idiomatic equivalent would be. But having more explicit control of lifetime, rather than it always depending specifically on that stack frame, seems like it might help here.