Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Exceptions in Clojure?

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!

like image 548
erikcw Avatar asked Sep 30 '10 22:09

erikcw


3 Answers

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))))
like image 112
Korny Avatar answered Sep 28 '22 07:09

Korny


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>
like image 24
Brian Carper Avatar answered Sep 28 '22 07:09

Brian Carper


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

like image 43
Ben Mabey Avatar answered Sep 28 '22 07:09

Ben Mabey