Defer in Lisp

(defun main ()
  ;; Suppose we wanted to create a file, write to it,
  ;; and then close when we're done. Here's how we could
  ;; do that with unwind-protect.

  ;; Immediately after getting a file object with
  ;; create-file, we ensure the closing of that file
  ;; with close-file. This will be executed when the
  ;; control leaves the unwind-protect form, after
  ;; write-file has finished.
  (let ((f (create-file "/tmp/defer.txt")))
    (unwind-protect
         (write-file f)
      (close-file f))))

(defun create-file (p)
  (format t "creating~%")
  (let ((f (open p :direction :output :if-exists :supersede)))
    (if (null f)
        (error "Failed to create file")
        f)))

(defun write-file (f)
  (format t "writing~%")
  (format f "data~%"))

;; It's important to check for errors when closing a
;; file, even in a cleanup form.
(defun close-file (f)
  (format t "closing~%")
  (handler-case
      (close f)
    (error (c)
      (format *error-output* "error: ~A~%" c)
      (uiop:quit 1))))

;; Running the program confirms that the file is closed
;; after being written.

;; $ sbcl --script defer.lisp
;; creating
;; writing
;; closing

In this Lisp translation, we use unwind-protect to mimic the behavior of Go’s defer. The unwind-protect macro ensures that its cleanup forms (in this case, closing the file) are executed when control leaves the protected form, whether normally or due to a non-local exit (like an error).

The create-file function opens a file for writing, write-file writes to it, and close-file closes it, handling any potential errors.

Note that Lisp doesn’t have a direct equivalent to Go’s defer, but unwind-protect provides similar functionality for resource management and cleanup. The handler-case macro is used for error handling in the close-file function, similar to how the Go version checks for errors when closing the file.

To run this Lisp program, you would typically save it to a file (e.g., defer.lisp) and then run it using a Lisp implementation like SBCL (Steel Bank Common Lisp):

$ sbcl --script defer.lisp
creating
writing
closing

This demonstrates that the file operations occur in the expected order, with the file being closed after it’s written to, just like in the original Go example.