Errors in Clojure

In Clojure, error handling is typically done using the try/catch mechanism, which is more similar to Java’s approach than Go’s explicit error returns. However, we can still demonstrate similar concepts using Clojure’s functions and data structures.

(ns errors-example
  (:require [clojure.string :as str]))

;; We'll use a function that may return an error
(defn f [arg]
  (if (= arg 42)
    ;; In Clojure, we can use a map to represent an error
    {:error "can't work with 42"}
    ;; Otherwise, return the successful result
    (+ arg 3)))

;; Define some "sentinel" errors
(def err-out-of-tea "no more tea available")
(def err-power "can't boil water")

;; A function that may return different types of errors
(defn make-tea [arg]
  (cond
    (= arg 2) err-out-of-tea
    (= arg 4) {:error err-power :context "making tea"}
    :else nil))

(defn main []
  ;; Demonstrate error handling with f
  (doseq [i [7 42]]
    (let [result (f i)]
      (if (:error result)
        (println "f failed:" (:error result))
        (println "f worked:" result))))

  ;; Demonstrate error handling with make-tea
  (doseq [i (range 5)]
    (let [result (make-tea i)]
      (cond
        (= result err-out-of-tea)
        (println "We should buy new tea!")

        (and (map? result) (= (:error result) err-power))
        (println "Now it is dark.")

        (nil? result)
        (println "Tea is ready!")

        :else
        (println "Unknown error:" result)))))

(main)

In this Clojure version:

  1. We define a function f that returns either a result or an error map.

  2. Instead of using explicit error types, we use strings or maps to represent errors.

  3. The make-tea function demonstrates different error scenarios, including a simple string error and a more complex error map.

  4. In the main function, we use conditional logic to check for different error conditions.

  5. We use doseq (Clojure’s equivalent of a for loop) to iterate over sequences.

  6. Error checking is done using Clojure’s conditional forms (if and cond) and by examining the structure of the returned values.

When you run this program, you should see output similar to:

f worked: 10
f failed: can't work with 42
Tea is ready!
Tea is ready!
We should buy new tea!
Tea is ready!
Now it is dark.

This example demonstrates how to handle errors in Clojure, using a combination of return values and conditional logic. While it doesn’t use exceptions, which are more common in Clojure for error handling, it shows how you can implement a similar pattern to Go’s error handling when needed.