djhworld
djhworld

Reputation: 6776

Program flow - bail out of operation if one computation fails

Sorry if this question doesn't make any sense.

I'm currently in the midst of writing a small application and I want to 'chain' some functions together that form a wider computation.

So for example, I have a function that calls a webservice and returns a hash-map as a result, or nil if the was an error or something went wrong. The next function in the chain takes the map and does some further processing and then passes it on to the next function...and so on.

The thing is if the first function fails, it's going to pass nil to the next function and that will throw a nullpointer (or whatever) and I don't want it to even reach this stage. So in other words I want the whole computation to fail if one item in the chain fails.

e.g.

;successful computation = function1 returns W -> function2 returns X -> function3 returns Y -> function4 returns Z -> Z
;failed computation = function1 returns W -> function2 returns X -> function3 returns nil -> nil
(-> (function1 "http://webservice.com") (function2) (function3) (function4))

The thing is I don't want to be littering each of my functions with if(nil? arg) at the start, ideally I'd like an abstraction that could take care of that for me but I'm not really clued up on what's available in Clojure

I was thinking of adopting the Scala style Option type that can either be Some(value) or None but I'd still need to litter my code with these checks at the start

Any ideas or replies would be really appreciated

Upvotes: 5

Views: 147

Answers (3)

Christian Berg
Christian Berg

Reputation: 14476

You could define a helper function for chaining the function calls and checking for nil values in between:

(defn chain [v & functions]
  (cond (nil? v) nil
        (empty? functions) v
        :else (recur ((first functions) v) (rest functions))))

Then call it like this:

(chain "http://webservice.com" function1 function2 function3 function4)

Upvotes: 1

Joost Diepenmaat
Joost Diepenmaat

Reputation: 17773

clojure.contrib.core has the -?> macro that does exactly what you want.

See http://clojuredocs.org/clojure_contrib/clojure.contrib.core/-_q%3E

Upvotes: 4

Matthias Benkard
Matthias Benkard

Reputation: 15759

Clojure Contrib's algo.monad includes an implementation of the monad that Scala calls Option. It is called maybe-m and works by treating nil as None. You can use it like this:

(ns mulk.monads-test
  (:refer-clojure)
  (:use clojure.repl
        clojure.algo.monads))

(defn function1 [x]
  x)

(defn function2 [x]
  (+ x 10))

(defn function3 [x]
  (* x 2))

(defn calc-stuff [thing]
  (domonad maybe-m
    [x1 (function1 thing)
     x2 (function2 x1)
     x3 (function3 (+ x2 10))]
    x3))

 (calc-stuff 10)   ;=> 60
 (calc-stuff nil)  ;=> nil

Upvotes: 4

Related Questions