About
unwind-protect, I believe the problem is that some functions won't be able to know that they are unwind-protected if you stop the computation and restart it later. For instance, consider the following code:
Code: Select all
(defun a (cc2)
(with-call/cc
(print 1)
(setf cc2
(let/cc k
(funcall cc2 k)))
(print 3)
(format t "~%STOP NOW!~%")
(let/cc k
(return-from a
(lambda ()
(format t "~&Continuing...~%")
(funcall cc2 k))))
(print 5)))
(defun b (cc1)
(with-call/cc
(print 2)
(setf cc1
(let/cc k
(funcall cc1 k)))
(print 4)
(let/cc k
(funcall cc1 k))))
Then
Code: Select all
cl-user> (a #'b)
1
2
3
STOP NOW!
#<CLOSURE (lambda ()) {1003BD0919}>
cl-user> (funcall *)
Continuing...
4
5 5
Neat
Now, suppose you modify it a little bit:
Code: Select all
(defun a (cc2)
(with-call/cc
(unwind-protect
(progn
(print 1)
(setf cc2
(let/cc k
(Funcall cc2 k)))
(print 3)
(format t "~%STOP NOW!~%")
(let/cc k
(return-from a
(lambda ()
(format t "~&Continuing...~%")
(funcall cc2 k))))
(print 5))
(format t "~&Function A has been successfully executed!~&"))))
(defun b (cc1)
(with-call/cc
(print 2)
(setf cc1
(let/cc k
(funcall cc1 k)))
(oops-undefined-function)
(print 4)
(let/cc k
(funcall cc1 k))))
Now, there will be an error once you try to continue the computation, because the function
oops-undefined-function is not defined.
Well, how would you like the code to behave? Function A returns right after STOP NOW is printed, so, if the unwind-protect is left as-is, then the cleanup code will execute after STOP NOW, which happens if you put the unwind-protect around with-call/cc:
Code: Select all
(defun a (cc2)
(unwind-protect
(with-call/cc
(print 1)
(setf cc2
(let/cc k
(Funcall cc2 k)))
(print 3)
(format t "~%STOP NOW!~%")
(let/cc k
(return-from a
(lambda ()
(format t "~&Continuing...~%")
(funcall cc2 k))))
(print 5))
(format t "~&Function A has been successfully executed!~&")))
If you want the cleanup after you try to continue and the undefined function error is thrown, how is B supposed to know that it is running under the scope of a unwind-protect? For this to work that way, you would need to do this:
Code: Select all
(defun a (cc2)
(with-call/cc
(print 1)
(setf cc2
(let/cc k
(Funcall cc2 k)))
(print 3)
(format t "~%STOP NOW!~%")
(let/cc k
(return-from a
(lambda ()
(unwind-protect
(progn
(format t "~&Continuing...~%")
(funcall cc2 k))
(format t "~&Function A has been (un)successfully executed!~&")))))
(print 5)))
With the exception that, if A or B fails before that, the cleanup code will not be executed. I don't see how this can be possibly implemented. You are welcome to try doing this and, if you manage to do it, you will deserve a very warm congratulations
It should be possible to modify the previous example to show why
catch and
throw are bad to use in this case - you might not be able to know that you are under the scope of a catch after continuation.
Finally, about
progv, the problem is it binds special variables during runtime. You can't know during macroexpansion time which variables are to be rebound. That is not the case when you use let, since the names are explicit in the body of let. Well, I don't actually know if cl-cont supports special variables or not, but, even if it does, I would not trust that progv could be easily implemented at all.