Custom Exceptions in Clojure?

11,618

Solution 1

Make a file src/user/MyException.clj (where src is on CLASSPATH) containing:

(ns user.MyException
  (:gen-class :extends java.lang.Exception))

Check the value of *compile-path* at the REPL. Make sure this directory exists and is on CLASSPATH. Create the directory if it doesn't exist; Clojure won't do so for you.

user> *compile-path*
"/home/user/foo/target/classes/"
user> (System/getProperty "java.class.path")
".......:/home/user/foo/target/classes/:......."

Compile your class:

user> (compile 'user.MyException)
user.MyException

If it worked, in *compile-path* you should now have files something like this:

/home/user/foo/target/
/home/user/foo/target/classes
/home/user/foo/target/classes/user
/home/user/foo/target/classes/user/MyException.class
/home/user/foo/target/classes/user/MyException__init.class
/home/user/foo/target/classes/user/MyException$loading__4410__auto__.class

Restart your Clojure REPL / JVM to load these classes. Again, make sure these new class files are on CLASSPATH. Now you should be able to use your class:

user> (user.MyException.)
#<MyException user.MyException>

Solution 2

Rather than generating custom classes, there are two much simpler ways to use custom exceptions:

  1. Use slingshot - this provides custom throw+ and catch+ macros that let you throw and catch any object, as well as exceptions.

  2. In clojure 1.4 and above, you can use clojure.core/ex-info and clojure.core/ex-data to generate and catch a clojure.lang.ExceptionInfo class, which wraps a message and a map of data.

Using this is straightforward:

(throw (ex-info "My hovercraft is full of eels"
                {:type :python-exception, :cause :eels}))

(try (...)
  (catch clojure.lang.ExceptionInfo e
    (if (= :eels (-> e ex-data :cause))
      (println "beware the shrieking eels!")
      (println "???"))))

Or in a midje test:

(fact "should throw some eels"
    (...) 
    => (throws clojure.lang.ExceptionInfo
          #(= :eels (-> % ex-data :cause))))

Solution 3

FWIW, unless you are creating a custom exception for interop reasons you may want to consider using clojure.contrib.condition instead. It comes with a precompiled custom exception that you piggy-back custom data onto using it's API. I've been able to avoid creating many custom exceptions by using it instead. The docs are here: http://clojure.github.com/clojure-contrib/condition-api.html

Share:
11,618
erikcw
Author by

erikcw

Updated on June 06, 2022

Comments

  • erikcw
    erikcw almost 2 years

    I've been trying to create a user-defined exception in Clojure, and have been having all sorts of problems. I tried the method outlined here:

    http://en.wikibooks.org/wiki/Clojure_Programming/Concepts#User-Defined_Exceptions

    (gen-and-load-class 'user.MyException :extends Exception)
    

    But that doesn't seem to work in Clojure 1.2 (or I'm doing something wrong...). My environment is Clojure 1.2, Emacs, and lein swank.

    Thanks for your help!

  • Vincenzo Maggio
    Vincenzo Maggio almost 11 years
    Many thanks, I've looked for a long time for a simple solution to trasparent exception management in Clojure!
  • m0skit0
    m0skit0 almost 10 years
    clojure-contrib is now deprecated.
  • danfromisrael
    danfromisrael over 6 years
    for custom exceptions in ring-service, i'd recommend using the approach suggested here: 8thlight.com/blog/mike-knepper/2015/05/19/…