Skip to content

Monadic error utilities for general use in Clojure projects

License

Notifications You must be signed in to change notification settings

flocktory/failjure

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Failjure

Travis Clojars Project

Failjure is a utility library for working with failed computations in Clojure. It provides an alternative to exception-based error handling for applications where functional purity is more important.

It was inspired by Andrew Brehaut's error monad implementation.

Installation

Add the following to your build dependencies:

Clojars Project

Example

(require '[failjure.core :as f])

;; Write functions that return failures
(defn validate-email [email]
    (if (re-matches #".+@.+\..+" email)
      email
      (f/fail "Please enter a valid email address (got %s)" email)))

(defn validate-not-empty [s]
  (if (empty? s)
    (f/fail "Please enter a value")
    s))

;; Use attempt-all to handle failures
(defn validate-data [data]
  (f/attempt-all [email (validate-email (:email data))
                  username (validate-not-empty (:username data))
                  id (f/try* (Integer/parseInt (:id data)))]
    {:email email
     :username username}
    (f/when-failed [e]
      (log-error (f/message e))
      (handle-error e))))

Quick Reference

fail

fail is the basis of this library. It accepts an error message with optional formatting arguments (formatted with Clojure's format function) and creates a Failure object.

(f/fail "Message here") ; => #Failure{:message "Message here"}
(f/fail "Hello, %s" "Failjure") ; => #Failure{:message "Hello, Failjure"}

failed? and message

These two functions are part of the HasFailed protocol underpinning failjure. failed? will tell you if a value is a failure (that is, a Failure or a java Exception.

attempt-all

attempt-all wraps an error monad for easy use with failure-returning functions. You can add any number of bindings and it will short-circuit on the first error, returning the failure.

(f/attempt-all [x "Ok"] x)  ; => "Ok"
(f/attempt-all [x "Ok"
              y (fail "Fail")] x) ; => #Failure{:message "Fail"}

You can use when-failed to provide a function that will handle an error

(f/attempt-all [x "Ok"
                y (fail "Fail")]
  x
  (f/when-failed [e]
    (f/message e))) ; => "Fail"

attempt-> and attempt->>

If you're on-the-ball enough that you can represent your problem as a series of compositions, you can use these threading macros instead. Each form is applied to the output of the previous as in -> and ->>, except that a failure value is short-circuited and returned immediately.

(defn validate-non-blank [data field]
  (if (empty? (get data field))
    (f/fail "Value required for %s" field)
    data))

(let [result (f/attempt->
              data
              (validate-non-blank :username)
              (validate-non-blank :password)
              (save-data))]
  (when (f/failed? attempt)
    (log (f/message result))
    (handle-failure result)))

try*

This library does not handle exceptions by default. However, you can wrap any form or forms in the try* macro, which is shorthand for

(try
  (do whatever)
  (catch Exception e e))

Since failjure treats returned exceptions as failures, this can be used to adapt exception-throwing functions to failjure-style workflows.

HasFailed

HasFailed is the protocol that describes a failed result. This library implements HasFailed for Object (the catch-all not-failed implementation), Exception, and the built-in Failure record type, but you can add your own very easily:

(defrecord AnnotatedFailure [message data]
  f/HasFailed
  (failed? [self] true)
  (message [self] (:message self)))

License

Copyright 2016 Adam Bard and Andrew Brehaut

Distributed under the Eclipse Public License v1.0 (same as Clojure).

About

Monadic error utilities for general use in Clojure projects

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Clojure 100.0%