Exit in Clojure

Here’s an idiomatic Clojure example demonstrating the concept of exiting a program:

(ns exit-example
  (:require [clojure.java.shell :as shell]))

;; This function will not be called due to early exit
(defn cleanup []
  (println "Cleanup function called!"))

(defn exit-program [status]
  (println "Exiting with status:" status)
  ;; The following line will prevent the cleanup function from being called
  (System/exit status))

(defn -main []
  ;; Register cleanup function to be called on normal exit
  (.addShutdownHook (Runtime/getRuntime) (Thread. cleanup))
  
  (println "Starting the program")
  
  ;; Exit with status 3
  (exit-program 3))

;; Run the program
(-main)

This Clojure program demonstrates how to exit a program with a specific status code. Let’s break down the key components:

  1. We define a cleanup function that simulates a cleanup operation. In a real-world scenario, this might involve closing file handles or database connections.

  2. The exit-program function is our custom exit function. It prints the exit status and then calls System/exit with the given status code. This is equivalent to os.Exit in Go.

  3. In the -main function, we use addShutdownHook to register our cleanup function. This is similar to using defer in Go, but it’s important to note that shutdown hooks are not guaranteed to run when using System/exit.

  4. We then call exit-program with a status of 3, which will immediately terminate the program.

To run this program:

  1. Save the code in a file named exit_example.clj.
  2. Run it using the Clojure command-line tool:
$ clj exit_example.clj
Starting the program
Exiting with status: 3

To check the exit status in a shell:

$ clj exit_example.clj
Starting the program
Exiting with status: 3
$ echo $?
3

Note that the cleanup function is not called due to the use of System/exit. This behavior is similar to Go’s os.Exit, which also doesn’t run deferred functions.

In Clojure, it’s generally preferred to let the program exit normally rather than using System/exit. This allows for proper resource cleanup and is considered more idiomatic. However, System/exit is available when you need to forcibly terminate the program with a specific exit code.

Remember that in Clojure, like in Go, the return value of the -main function is not used to determine the exit status. To set a non-zero exit status, you must use System/exit explicitly.