Error handling helpers in Clojure(Script)
Main motivations for writing this library are:
- Managing errors as values insted of try/catch syntax
- Available on both Clojure and ClojureScript
- No function creation boundaries (Can be used in core.async go block)
[jp.nijohando/failable "0.4.1"]
jp.nijohando/failable {:mvn/version "0.4.1"}
Clojure
(require '[jp.nijohando.failable :as f])
CloureScript
(require '[jp.nijohando.failable :as f :include-macros true])
function fail
creates a failure object representing an error.
(f/fail)
;=> #jp.nijohando.failable.Failure{}
Failure object also can be created by specifying the reason for falure.
(f/fail :user-not-found)
;=> #jp.nijohando.failable.Failure{
; :jp.nijohando.failable/reason :user-not-found}
The reason can be retrieved by function reason
(def x (f/fail :user-not-found))
(f/reason x)
;=> :user-not-found
@
( deref
) is more simple way to get the reason.
(def x (f/fail :user-not-found))
@x
;=> :user-not-found
Since failure object is expressed as a record of type jp.nijohando.failable.Failure
, Any additional infomation can be associated to the object.
(-> (f/fail :user-not-found)
(assoc :user-id 1001))
;=> #jp.nijohando.failable.Failure{
; :jp.nijohando.failable/reason :user-not-found,
; :user-id 10001}
Failure object can be created by wrapping the another one or an exception that caused it.
(def origin (f/fail :origin))
(def wrapped (f/wrap origin :wrapped))
wrapped
;=> #jp.nijohando.failable.Failure{
; :jp.nijohando.failable/reason :wrapped,
; :jp.nijohando.failable/cause #jp.nijohando.failable.Failure{
; :jp.nijohando.failable/reason :origin}}
(f/cause wrapped)
;=> #jp.nijohando.failable.Failure{
; :jp.nijohando.failable/reason :origin}
function do*
is similar to do, but it captures any exception thrown from the body forms and converts it into a failure object , and returns it.
(f/do* (throw (ex-info "test" {})))
;=> #jp.nijohando.failable.Failure{
; :jp.nijohando.failable/reason :jp.nijohando.failable/exception,
; :cause #error { ... }}
function succ?
/ fail?
checks whether the value is successful or failed.
Only values that are the instance of Failure will be failed.
(def x (f/fail))
(def y 1)
(f/fail? x)
;=> true
(f/succ? x)
;=> false
(f/fail? y)
;=> false
(f/succ? y)
;=> true
function ensure
throws an exception if the value is failed, otherwise the value is just returned.
(let [x (f/fail)]
(f/ensure x))
;=> ExceptionInfo Failed to ensure value clojure.core/ex-info (core.clj:4739)
(let [x 1]
(f/ensure x))
;=> 1
There are 6 conditional threading macros that are similar to some->, some->>, and as-> but the conditions are focused on whether the result is succeeded or failed.
Macro | Continuation condition | exceptionproof |
---|---|---|
succ-> |
Successful | - |
succ->* |
Successful | ✓ |
fail-> |
Failed | - |
fail->* |
Failed | ✓ |
as-succ-> |
Successful | - |
as-succ->* |
Successful | ✓ |
as-fail-> |
Failed | - |
as-fial->* |
Failed | ✓ |
The threading is continued while the result is not failed.
(f/succ-> 1
(inc)
(inc)
(dec))
;=> 2
When expr is failed, the threading is stopped and failure object is returned.
(f/succ-> 1
(inc)
((fn [x] (f/fail)))
(dec))
;=> #jp.nijohando.failable.Failure{}
These are the opposite of succ->
succ->*
that continue processing while the result is failed.
Like a as->
, but the thrading is continued while the result is not failed.
(f/as-succ-> 1 n
(inc n)
(inc n)
(dec n))
;=> 2
When expr is failed, the threading is stopped and failure object is returned.
These are the opposite of as-succ->
as-succ->*
that continue processing while the result is failed.
There are 4 variants of let macros that are similar to let, but the binding evaluation is stopped if the bound value does not satisfy the condition.
Macro | Condition | exceptionproof |
---|---|---|
slet |
Successful | - |
slet* |
Successful | ✓ |
flet |
Failed | - |
flet* |
Failed | ✓ |
(f/slet [_ (prn 1)
_ (prn 2)
x (f/fail)
_ (prn 3)]
(prn "Not reach here"))
;=> 1
;=> 2
;=> #jp.nijohando.failable.Failure{}
There are 4 let-style-if macros that are similar to if-some, but the conditions are focused on whether the result is succeeded or failed.
Macro | Condition | exceptionproof |
---|---|---|
if-succ |
Successful | - |
if-succ* |
Successful | ✓ |
if-fail |
Failed | - |
if-fail* |
Failed | ✓ |
(f/if-succ [x 1]
:ok
:ng)
;=> :ok
(f/if-succ [x (f/fail)]
:ok
:ng)
;=> :ng
(f/if-fail [x 1]
:ng
:ok)
;=> :ok
(f/if-fail [x (f/fail)]
:ng
:ok)
;=> :ng
There are 4 let-style-when macros that are similar to when-some, but the conditions are focused on whether the result is succeeded or failed.
Macro | Condition | exceptionproof |
---|---|---|
when-succ |
Successful | - |
when-succ* |
Successful | ✓ |
when-fail |
Failed | - |
when-fail* |
Failed | ✓ |
(f/when-succ [x 1]
:ok)
;=> :ok
(f/when-succ [x (f/fail)]
:ok)
;=> nil
(f/when-fail [x 1]
:ng)
;=> nil
(f/when-fail [x (f/fail)]
:ng)
;=> :ng
A Function with an asterisk at end of the name is exceptionproof.
(e.g do*
,succ->*
, fail->*
and so on)
Exceptionproof function captures any exception thrown internally, converts it to failure object, and handles it.
© 2017-2018 nijohando
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.