Defer in Racket

#lang racket

(require racket/file)

; 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 dynamic-wind.

(define (main)
  ; dynamic-wind is used to ensure that the cleanup function
  ; (close-file) is called after the body (write-file) is executed,
  ; even if an exception occurs.
  (let ([f (create-file "/tmp/defer.txt")])
    (dynamic-wind
      (lambda () (void)) ; setup (empty in this case)
      (lambda () (write-file f))
      (lambda () (close-file f)))))

(define (create-file p)
  (displayln "creating")
  (let ([out (open-output-file p #:exists 'replace)])
    (if (output-port? out)
        out
        (error "Failed to create file"))))

(define (write-file f)
  (displayln "writing")
  (displayln "data" f))

; It's important to check for errors when closing a
; file, even in a cleanup function.
(define (close-file f)
  (displayln "closing")
  (with-handlers ([exn:fail? (lambda (e)
                               (fprintf (current-error-port) 
                                        "error: ~a~n" 
                                        (exn-message e))
                               (exit 1))])
    (close-output-port f)))

(main)

In Racket, we don’t have a direct equivalent of Go’s defer keyword. However, we can achieve similar functionality using the dynamic-wind function. This function allows us to specify setup, body, and cleanup actions, ensuring that the cleanup is performed even if an exception occurs during the body execution.

Here’s a breakdown of the translation:

  1. We define a main function that creates a file, writes to it, and ensures it’s closed afterwards.

  2. Instead of defer, we use dynamic-wind. The first function (setup) is empty in this case. The second function (body) writes to the file. The third function (cleanup) closes the file.

  3. The create-file function opens a file for writing and returns the output port.

  4. The write-file function writes data to the file.

  5. The close-file function closes the file and handles any errors that might occur during closing.

To run the program:

$ racket defer.rkt
creating
writing
closing

This program demonstrates how to use dynamic-wind in Racket to ensure that resources are properly cleaned up, similar to how defer is used in other languages.