Skip to content

rlbdv/murphy-clj

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

murphy (What could go wrong?)

NOTE: While we’d love for people to try this out, as long as the version is less than 1.0, we reserve the right (which we may well exercise) to change the API.

Consider:

(try
  (print-masterpiece)  ; Throws lp0-on-fire
  (finally
    (turn-off-light)))  ; Throws switch-broken

At this point, you’ll know that you need to fix your light switch, but have no idea that your printer is on fire. That’s because a throw from a finally clause simply discards any pending exception.

To preserve all of the failure information, we can use exception suppression, which is provided by newer versions of the JDK and write this instead:

(try!
  (print-masterpiece)  ; Throws lp0-on-fire
  (finally
    (turn-off-light)))  ; Throws lp0-on-fire, with switch-broken
                        ; available via (.getSuppressed lp0-on-fire).

As mentioned in the exception suppression documentation linked above, whether or not a suppressed exception is recorded or discarded depends on the constructor arguments for the original exception.

In any case, if an exception does contain suppressed exceptions, they should be displayed by the normal top-level JVM exception handler, assuming they make it that far.

Facilities

(try! …)

Behaves like the normal try, except that it supports multiple finally clauses which are executed in order, and if any given finally clause throws while an exception is already pending, the new exception will be suppressed via the Throwable addSuppressed method.”

(with-open! …)

Behaves like the normal with-open except that exceptions thrown by any of the close methods will be suppressed. And of course any exception pending at the end of the cleanup will be thrown.

(with-final …)

Configurable cleanup, suppressing exceptions, either on :error (any throw) or :always:

(with-final [foo (.acquire lock) :always .release
             bar (open something) :always .close
             baz {:foo foo :bar bar}]
  ...)

:error can be particularly useful in cases where normal cleanup must happen in a completely different scope.

(defn start-server [...]
  (with-final [foo (open-foo ...) :error .close
               bar (connect-bar ...) :error .disconnect
               ...]
    ...do many things...
    {:foo foo :bar bar ...}))

(defn stop-server [info]
  (with-final [_ (:foo info) :always .close
               _ (:bar info) :always .disconnect
               ...]
    true)

Contributing

You can run all of the existing tests (including lein test) with lein check-all.

All patches must be “signed off” by the author before official inclusion (see ./SIGNED-OFF-BY). If you like, git can can add the appropriate pseudo-header for you via the –signoff argument to commit, amend, etc.

License

This project is free software; you can redistribute it and/or modify it under the terms of (at your option) either of the following two licenses:

  1. The GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1, or (at your option) any later version: https://www.gnu.org/licenses/lgpl-2.1.html
  2. The Eclipse Public License; either version 1.0 or (at your option) any later version: http://www.eclipse.org/legal/epl-v10.html

Copyright © 2017-2018, 2020-2021 Rob Browning <rlb@defaultvalue.org>

About

Clojure library for better handling of bad situations

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published