santiagocabrera96

santiagocabrera96

Programming Clojure, Fourth Edition: ex-info should include the cause. (p. 224)

In page 224, the following code is suggested.

(defn load-resource [path]
  (try
    (if (forbidden? path)
      (throw (ex-info "Forbidden resource"
                      {:status 403 :resource path}))
      (slurp path))
    (catch java.io.FileNotFoundException e
      (throw (ex-info "Missing resource"
                      {:status 404 :resource path})))
    (catch java.io.IOException e
      (throw (ex-info "Server error"
                      {:status 500 :resource path})))))

I’ve seen this in practice a lot and not including the cause when catching an exception generates headaches to debug later.

I’d suggest to do a bit of explanation of adding the cause exception to the ex info in the catch clauses like this:

(defn load-resource [path]
  (try
    (if (forbidden? path)
      (throw (ex-info "Forbidden resource"
                      {:status 403 :resource path}))
      (slurp path))
    (catch java.io.FileNotFoundException e
      (throw (ex-info "Missing resource"
                      {:status 404 :resource path}
                      e)))
    (catch java.io.IOException e
      (throw (ex-info "Server error"
                      {:status 500 :resource path}
                      e)))))

This is really useful for me to add context to exceptions that might happen in calling functions on sequences to understand what element failed.

Here’s a dummy example where I can call a function that might throw, and I would want more context on where it failed, and I can add context to the existing ex-info.

(defn randomly-fails []
  (when (> (rand-int 10) 8)
    (throw (ex-info "Randomly failed!" {}))))
(run! #(try (randomly-fails)
            (catch clojure.lang.ExceptionInfo e
              (throw (ex-info (ex-message e)
                              (assoc (ex-data e) :n %)
                              e))))
      (range 100))
=> #'examples.interop/randomly-fails
Execution error (ExceptionInfo) at examples.interop/randomly-fails (form-init16509100671821839256.clj:3).
Randomly failed!
*e
=>
#error
{:cause "Randomly failed!",
 :data {},
 :via [{:type clojure.lang.ExceptionInfo,
        :message "Randomly failed!",
        :data {:n 7},
        :at [examples.interop$eval2303$fn__2304 invoke "form-init16509100671821839256.clj" 6]}
       {:type clojure.lang.ExceptionInfo,
        :message "Randomly failed!",
        :data {},
        :at [examples.interop$randomly_fails invokeStatic "form-init16509100671821839256.clj" 3]}],

Where Next?

Popular Pragmatic Bookshelf topics Top

jimschubert
In Chapter 3, the source for index introduces Config on page 31, followed by more code including tests; Config isn’t introduced until pag...
New
mikecargal
Title: Hands-on Rust: question about get_component (page 295) (feel free to respond. “You dug you’re own hole… good luck”) I have somet...
New
Mmm
Hi, build fails on: bracket-lib = “~0.8.1” when running on Mac Mini M1 Rust version 1.5.0: Compiling winit v0.22.2 error[E0308]: mi...
New
AndyDavis3416
@noelrappin Running the webpack dev server, I receive the following warning: ERROR in tsconfig.json TS18003: No inputs were found in c...
New
nicoatridge
Hi, I have just acquired Michael Fazio’s “Kotlin and Android Development” to learn about game programming for Android. I have a game in p...
New
brunogirin
When installing Cards as an editable package, I get the following error: ERROR: File “setup.py” not found. Directory cannot be installe...
New
dsmith42
Hey there, I’m enjoying this book and have learned a few things alredayd. However, in Chapter 4 I believe we are meant to see the “>...
New
creminology
Skimming ahead, much of the following is explained in Chapter 3, but new readers (like me!) will hit a roadblock in Chapter 2 with their ...
New
tkhobbes
After some hassle, I was able to finally run bin/setup, now I have started the rails server but I get this error message right when I vis...
New
Keton
When running the program in chapter 8, “Implementing Combat”, the printout Health before attack was never printed so I assumed something ...
New

Other popular topics Top

PragmaticBookshelf
Stop developing web apps with yesterday’s tools. Today, developers are increasingly adopting Clojure as a web-development platform. See f...
New
AstonJ
Or looking forward to? :nerd_face:
503 15149 280
New
brentjanderson
Bought the Moonlander mechanical keyboard. Cherry Brown MX switches. Arms and wrists have been hurting enough that it’s time I did someth...
New
Rainer
My first contact with Erlang was about 2 years ago when I used RabbitMQ, which is written in Erlang, for my job. This made me curious and...
New
PragmaticBookshelf
Create efficient, elegant software tests in pytest, Python's most powerful testing framework. Brian Okken @brianokken Edited by Kat...
New
PragmaticBookshelf
Develop, deploy, and debug BEAM applications using BEAMOps: a new paradigm that focuses on scalability, fault tolerance, and owning each ...
New
AstonJ
This is a very quick guide, you just need to: Download LM Studio: https://lmstudio.ai/ Click on search Type DeepSeek, then select the o...
New
AstonJ
Curious what kind of results others are getting, I think actually prefer the 7B model to the 32B model, not only is it faster but the qua...
New
pragdave
Build robust LLM-powered apps, chatbots, and agents while mastering AI engineering principles that will help you outlast the tools and th...
New
xiji2646-netizen
Woke up to this today: Claude Code’s complete source code exposed via npm source map. Not a snippet. All 512,000 lines. 1,900 TypeScript ...
New

Sub Categories: