Error handling in clojure

Exception handling errors is terrible in any language is there in the roadmap a way to improve this idiom I know there are libraries like cats or failjure but this should be on the core of the language to make everyone use this kind of idioms or just to define one like doing pattern match in tuples like in elixir you have the ok and error tuple.

1 Like

What’s so bad about exceptions?

(throw
  (ex-info "This is broken" {:type :broken-pipe}))

Just throw something like that wherever you can fail, and you’re done.

Now if you use something that can throw and to which you know how to recover from programatically, try/catch it and handle it.

(try
  (something-that-can-throw)
  (catch Exception e
    (case (:type (ex-data e))
      :broken-pipe (handle-broken-pipe)
      :leaking-pipe (handle-leaking-pipe)
      (throw e))))

It’s not the beauty that CommonLisp conditions and restarts system is, but its rarely something I hear complain about.

5 Likes

I used this in a couple of projects: https://github.com/MichaelDrogalis/dire

I like the idea of decoupling error handling from the rest of the logic.

I am not sure it is still actively maintained, though.

I like a lot how elixir uses the with macro for error handling

The with macro isn’t required in Clojure.

Where you would do:

with {:ok, result1} <- can_fail(),
     {:ok, result2} <- can_also_fail(result1)
do
  handle_success(result2)
else
  {:error, error} -> handle_error(error)
end

You just do what I did above again:

(try
  (-> (can-fail)
      (can-also-fail)
      (handle-success))
  (catch Exception error
    (handle-error error)))

You can think of it like everything in Clojure returns result or Exception. When things succeed, you don’t need to destructure the return value, since it returns the result as is, you can just use it, so no need for pattern-matching on success. Then on error, you also need not do anything, unless you want to, in which case you try/catch it. Otherwise the error is raised and will crash the process.

3 Likes

Are we talking here about somehow avoiding runtime exceptions from leaking the gore of Java objects used by Clojure and/or null pointer exceptions, or something else? I think it would be auspicious if the language would tell you you’re passing a fn instead of a coll and such, rather than let it crash on Java class names used by clojure ― in order that the language can have growth from newcomers by making them less lost on error messages.

1 Like

I would suggest failjure - a monadic error handling. This article describes the philosophy behind and answers the question “why not just use exceptions”.

1 Like

I’ve been using it, but it will be great if the community move towards to a an unique way to solve this

I’m still not able to follow what problem you feel exceptions have that you would want solved?

I’m not really seeing any particular advantage to failjure, even after reading the article. I think its also really important to consider the full context of the programming language to understand why some use exceptions more so then returning error codes.

In Haskell for example, the first class support for Monad and all its supporting syntactic sugar makes it that returning an error code or the successful result does not result in unnecessarily verbose result handling code. You don’t need to have if a == Nothing then Nothing else ... everywhere every time you call any function that can fail. That’s because you’ll most likely compose the failing functions and their results using the monadic bind, which acts similarly to the Elixir with macro. But also, all execution is lazy, and so you can propagate the result back and pass it to other functions until one function actually needs to use it, only at that point must it handle the error case. Another benefit in Haskell is the type system will guarantee that you do not forget to check for error when it can have one, so by making the return type a sum type, you force error handling by the callers in a similar way that Java checked exceptions do (though I’d say a much nicer way, without the downfalls of checked exceptions.).

Yet, even with this, Haskell implemented a Exception mimicking monad, which behaves much like Java’s Exceptions apart for being implemented as a Monad, and thus benefiting from more syntax sugar then just the simple try/catch Java gives you. They did it so that you can attach much more context to errors, like a full stack trace, line numbers, and can support multiple exception out of a function and do conditional handling based on their type.

Another example is Elixir, as we saw earlier on. In Elixir, its the first class pattern matching and all its syntactic sugar which helps make the use of returning a tuple not overly verbose in a similar vain. Yet it can still result in quite a bit of boilerplate. Now things become complicated in Elixir, sometimes you pattern match on failure, sometimes you rescue exceptions, sometimes you try/catch values to fork control flow in a goto style, sometimes you let the supervisor restart the process. And thus, many questions and blog posts were written about how the hell do you deal with exceptions and errors in Elixir? In practice, its not that hard, but it does get confusing.

Now lets look at Clojure. Clojure extends Java, and must therefore deal with it (a curse and a blessing). So obviously, if the function chain includes some Java, you’ll need to deal with Java throwing exceptions, and your only option is to catch them to handle them. Clojure discards checked exceptions though, in that you do not need to declare your thrown exceptions (an improvement in my opinion).

When it comes to Clojure’s Clojure code it actually uses multiple strategies. Often times, it returns garbage instead when taking garbage. That is, it tries to just work no matter the input and return some form of output. In other cases, it returns nil or the value, in a pseudo Maybe style. Sometimes, Clojure does return pseudo Either, like with Vars, they can be unbound or bound. The rest of the time it throws exceptions.

Since Clojure does not have first class pattern matching, or monad, I think it would be too cumbersome to adopt a pattern of returning error codes from functions, and so Exceptions seem like a much better candidate.

So what I’m getting at is that Haskell had first class monads, and thus built its exception mechanism on it. Elixir had first class pattern matching, and thus built its exception mechanism on it. Clojure had first class exceptions, and thus built its exception handling mechanism on it.

So which style is best?

I don’t think any style wins out personally. I think people have the impression “Exceptions” are bad, but they are simply recounting what they’ve heard without understanding what, why and when. Most criticism of Java exceptions come from people disliking the CheckedExceptions. This is not an issue in Clojure. That’s why I’m trying to push towards discussing practical issues, such as, I want to do this, and I can’t, or I can but it seems overly verbose and makes the code confusing. Only then can you really be justified to say, I need a library to handle this error case in a simpler way, keeping the code short and readable.

9 Likes

Agreed, in practice just using try/catch along with throw/ex-info hasn’t been a real problem for me, and fits in well with Java/JavaScript.

When I started with Clojure development I also researched libraries like failjure, because they felt more functional…but I’ve never really felt a need for them.

The only thing regarding to exceptions that I miss a lot is Common Lisp’s conditional restarts. But again, I don’t know how viable that is to get because of the interop story.

5 Likes