loading...
Cover image for How to handle exceptions in Clojure

How to handle exceptions in Clojure

andreevich profile image Yuri Mednikov Originally published at mednikov.tech ・4 min read

In some form or another, but exceptions exist in almost any languages. Due to the fact, that Clojure actually runs on JVM, it inherits its exception system from Java. From a technical point of view, the exception concept stands for unwanted or unexpected event, which occurs during the execution of a program i.e at run time, that disrupts the normal flow of the program’s instructions.

The hierarchy of exceptions in Clojure is similar to the one in Java and includes: checked exceptions, runtime exceptions and errors. Their relationship is presented on the diagram below:

All exceptions subclass Throwable class. Runtime exceptions correspond to a separate group, because technically, a recovery from these exceptions are may still be possible. This group is not checked in a compilation time. Contrary to this, checked exceptions should be explicitly handled.

Errors are placed separately. The concept of error is quite tricky. Often, this due to the fact that there is nothing that a program can do to recover from the error, because it indicates serious problems that a reasonable application should not even try to catch. Let think about the memory error - java.lang.VirtualMachineError. Such error indicates that the Java Virtual Machine is broken or has run out of resources necessary for it to continue operating. Or in other words, an app goes out of memory, it simply can't allocate additional memory resources. Surely, if a failure is a result of holding a lot of memory that should be made free, an exception handler could attempt to free it (not directly itself, but call JVM to do it). And even such handler may be useful in this context, such attempt might not be successful.

There are different points of view on exception handling in functional languages. Nevertheless, exception do exist in Clojure and can not be avoided, so they should be handled.

Standard approach

This section presents a standard (or general) error handling approach in Clojure. If you have an experience programming in Java, you will find it familiar, because in a similar way with Java, Clojure utilizes try-catch blocks.

Try-catch block

As it was mentioned, Clojure provides a same way to handle exceptions as Java. The code, that causes an exception is placed inside try block and you also can provide a functionality, that will be triggered, when exception would be caught. It is placed inside catch block. Take a look on the code snippet below:

(defn DangerFunction [number]
    (+ number 10)
)

(defn ExceptionHandler []
    (DangerFunction "1")
)

Here we have two functions. The first one - DangerFunction accepts a number and returns its sum with 10. Inside the second one - ExceptionHandler - we call it, but instead of a number we brings a string value as an argument. In a run time it will cause ClassCastException. Technically, it is a runtime exception, so it is not checked in a compilation time. But, when the ExceptionHandler will be called, it will result in the following result:

To avoid this, we implement an exception handling mechanism:

(defn ExceptionHandler []
    (try 
        (DangerFunction "1")
        (catch ClassCastException ex
            (println "I catched the excepiton!")
        )
    )
)

Now, the call of DangerFunction with an invalid argument will be caught. This allows to recover from the exception situation:

Finally

The catch block represents a sutuation, which will exist only when an exception will occur. In that way, the normal program flow will be interrupted. The finally block allows to provide a functionality, that will be triggered anyway. It is used in a similar way as in Java:

(defn ExceptionHandler []
    (try 
        (DangerFunction "1")

        (catch ClassCastException ex
            (println "I catched the excepiton!")
        )

        (finally 
            (println "I am executed anyway!")
        )
    )
)

As it was mentioned, the logic placed inside finally will be executed in any case, even the exception will not be thrown. We can prove it by passing a valid number to the function:

Multiple catch

Potentially, a code block may throw more than one exception type. Certainly, you can just catch a generic Exception. But usually you need to determine what it was exactly, because it will execute a different logic afterwards. So, Clojure, like Java, provides an opportunity to have multiple catch blocks.

For example let take the following program. It handles two operations: sums the argument with 10 and the divides it by zero. Of course, the last action will cause an ArithmeticException, but it could be not triggered, if we will provide a bad argument (not a number). So, consider this code snippet:

(defn MultipleExceptionHanlder [x]
    (try
        (def result (+ x 10))
        (/ result 0)

        (catch ClassCastException c
            (println "This is ClassCastException")
        )
        (catch ArithmeticException a
            (println "This is ArithmeticException ")
        )
    )
)

(println "Multiple exception: bad input")
(MultipleExceptionHanlder "100")

(println "MultipleException: divide by 0")
(MultipleExceptionHanlder 100)

Let call this function with two scenarios: a string argument and a number argument, like it is demonstrated below:

What do you need to remember here? Generally, we assume in this code that exceptions are at same level. But you have to order catch blocks from most specific to most general. In other words, catch for ArithmeticException must come before catch for Exception.

That is all for this post. If you have questions regarding an exception handling in Clojure, you can write them in comments below or contact me directly.

Discussion

markdown guide