Clojure and ClojureScript allow the user to handle platform exceptions with the 'try special form. This page aims to provide a specification for a cross-platform error handling strategy.
UPDATE 2013/11/05
ClojureScript regained its capability to catch all with ticket http://dev.clojure.org/jira/browse/CLJS-661
The implemented syntax was
There is a pending ticket implementing this syntax for Clojure: http://dev.clojure.org/jira/browse/CLJ-1293
UPDATE 2016/12/08
Added proposals for clojure
Problems
1) Error handling is highly platform dependent, '(try (catch)) directly dispatches on platform type names. There is no platform-independent catch-all clause.
'try special form has a cascaded 'instanceof dispatch built in, which is reasonable for java because it maps directly to the native try{}catch(E e){}, but adds complexity in ClojureScript, where the native try{}catch(e){} is untyped.
2) instanceof is not capable of catching all types because of JS semantics
This won't work! "a string" does not satisfy isinstance of js/String. Instead, you need to check typeof == "string". Similarly, js/Object isn't sufficient either, since strings, numbers, etc don't satisfy isinstance js/Object.
Currently there is no catch-all in clojurescript at all. [CLJS-661]
Scope
This proposal considers a generic design for '(try (catch)) expressions, that can be augmented with platform-dependent error handling.
The design should be backwards-compatible with Clojure and ClojureScript and it should be implementable in every Clojure dialect for platforms that have platform-provided escape continuations (i.e. exceptions).
Rejected alternatives
Former ClojureScript's (try* .. (catch e# ..))
Platform-Error handling should have a basic form that's the same on all platforms. Even if Clojure gained try*, one still couln't mix catching specific exceptions with catch-all.
Proposed solutions
(catch :default _) means (catch Throwable_)
In addition to the regular type-dispatched catch clauses, try forms will accept a '(catch :default e# ..) clause, that catches all throwables in a platform-independent manner.
The platform-independent subset of 'try forms would essentially amount to '(try .. (catch :default e# ..)).
Delivering a more sophisticated mechanism than catch-all, even though included in the scope, is not proposed, because it can be easily implemented as library code.
(catch :default _) means (catch Exception _) and (catch :all _) means (catch Throwable _)
try forms will accept :default and :all clauses, to mean Exception and Throwable, respectively. This is in line with java best practices, where Throwable signifies a usually-not-recoverable condition like OOM.
Other than in clojurescript, there exists a common ancestor class for all throwable things, which can be caught with a regular catch.
Retaining a platform-independent notion "catch everything catchable" is worth preserving, in case other platforms face similar problems
(catch :default _) means (catch Exception _)
try forms will accept :default clauses, to mean Exception. This is in line with java best practices, where Throwable signifies a usually-not-recoverable condition like OOM, which should almost never be caught.
Since there exists a common throwable ancestor, handling them will be possble, but doing so will usually be a pretty platform-sensitive thing to do, so #?(:clj (catch Throwable _)) doesn't seem much to ask from users.
Retaining a platform independent notion of "catch everything catchable" isn't worth bothering:
- our try-catch class dispatch in clojurescript used to shadow the actual platform default, which is the untyped catch
js' untyped catch matches snugly with :default, since there are no official "do not catch" types, or unofficial ones, for that matter. - it's unlikely for target platforms to simultaneously have a distinction similar to java's Throwable/Exception while not deriving them from a common base, as in javascript.
only that combination would necessitate both :default and :all
2 Comments
Hide/Show CommentsNov 04, 2013
Brandon Bloom
CLJS ticket and patch here: http://dev.clojure.org/jira/browse/CLJS-661
I went with the (catch :default e ...) syntax, since that matches multimethods, core.async, and a few other places, plus both I and David Nolen liked it best
Would be great to spur this to happen for JVM/CLJ as catching Throwable too!
Nov 05, 2013
Brandon Bloom
CLJ ticket and patch here: http://dev.clojure.org/jira/browse/CLJ-1293