Custom Exceptions in Clojure?

Exception HandlingClojure

Exception Handling Problem Overview


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!

Exception Handling Solutions


Solution 1 - Exception Handling

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 2 - Exception Handling

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 3 - Exception Handling

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionerikcwView Question on Stackoverflow
Solution 1 - Exception HandlingKornyView Answer on Stackoverflow
Solution 2 - Exception HandlingBrian CarperView Answer on Stackoverflow
Solution 3 - Exception HandlingBen MabeyView Answer on Stackoverflow