Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Clojure wrap checked exceptions with unchecked exceptions?

The following code demonstrates a case where a checked exception, ExecutionException, is thrown by an operation and Clojure is wrapping it with RuntimeException.

Why is Clojure doing this? Is this normal? Clojure appears to be doing something different than Java would in this case. What is the idiomatic way to handle the actual exception, in this case Exception, that caused the failure?

user=> (def f (future (Thread/sleep 10000) (throw (Exception. "hello world"))))
#'user/f

user=> (.get f)
Exception hello world  user/fn--318 (NO_SOURCE_FILE:81)

user=> (.printStackTrace *e)
java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.Exception: hello world
    at clojure.lang.Util.runtimeException(Util.java:165)
    at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:97)
    at clojure.lang.Reflector.invokeNoArgInstanceMember(Reflector.java:316)
    at user$eval320.invoke(NO_SOURCE_FILE:82)
    at clojure.lang.Compiler.eval(Compiler.java:6465)
    at clojure.lang.Compiler.eval(Compiler.java:6431)
    at clojure.core$eval.invoke(core.clj:2795)
    at clojure.main$repl$read_eval_print__5967.invoke(main.clj:244)
    at clojure.main$repl$fn__5972.invoke(main.clj:265)
    at clojure.main$repl.doInvoke(main.clj:265)
    at clojure.lang.RestFn.invoke(RestFn.java:421)
    at clojure.main$repl_opt.invoke(main.clj:331)
    at clojure.main$main.doInvoke(main.clj:427)
    at clojure.lang.RestFn.invoke(RestFn.java:397)
    at clojure.lang.Var.invoke(Var.java:397)
    at clojure.lang.AFn.applyToHelper(AFn.java:159)
    at clojure.lang.Var.applyTo(Var.java:518)
    at clojure.main.main(main.java:37)
Caused by: java.util.concurrent.ExecutionException: java.lang.Exception: hello world
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
    at java.util.concurrent.FutureTask.get(FutureTask.java:83)
    at clojure.core$future_call$reify__5684.get(core.clj:6064)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:92)
    ... 16 more
Caused by: java.lang.Exception: hello world
    at user$fn__318.invoke(NO_SOURCE_FILE:81)
nil
    at clojure.core$binding_conveyor_fn$fn__3713.invoke(core.clj:1817)
    at clojure.lang.AFn.call(AFn.java:18)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:680)
like image 681
Eric Schoonover Avatar asked Jan 18 '23 11:01

Eric Schoonover


1 Answers

Note that an ExcecutionException is indeed being thrown - it's just that the exception is getting caught and handled by Clojure.

Clojure will wrap certain exceptions in a RuntimeException when it handles them - I believe this is done to avoid the need to handle checked exceptions in the Clojure source code.

If you want to access the underlying exception, then (.getCause e) should work.

like image 192
mikera Avatar answered Feb 11 '23 18:02

mikera