1
votes

Trying to write a composed function in clojure that exits at the first nil value, (e.g something you'd do by chaining Maybes together in haskell) with the following:

(defn wrap [f] (fn [x] (if (nil? x) nil (f x))))

(defn maybe [arg & functs] ( (comp (reverse (map wrap functs))) arg))

So that I'd get, e.g.

(defn f1 [x] (+ x 1))

(maybe 1 f1 f1 ) => 3

(maybe nil f1 f1) => nil

Which is unfortunately giving me this: ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.IFn user/maybe (NO_SOURCE_FILE:1)

Can someone provide some help on what I'm doing wrong here? What's the idiomatic way to do this?

4
what does wrap do that (and x (f x )) not do?RedDeckWins

4 Answers

7
votes

The idiomatic way to do this is to use some->. See the documentation of this macro for more details.

Don't let that stop you from making your own, of course!

4
votes

comp expects each function as an individual argument, but you're passing it a list of functions as a single argument. To get around this, use apply.

(defn maybe [arg & functs] ( (apply comp (reverse (map wrap functs))) arg))
4
votes

There's always the clojure.algo.monads namespace with the maybe-m monad:

(with-monad maybe-m
  (defn adder [x]
    (let [f (fn [x] (+ x 1))]
      (domonad
        [a x
         b (f a)
         c (f b)]
       c))))

(adder 1)
=> 3 

(adder nil)
=> nil

Admittedly it might be a bit overkill for your requirements

0
votes

I know this has been answered, and I already put one up myself, but thought I'd add the following as I had a play with it using monads again, and this seemed a good question to post against.

Reading this article on threading monads, I was able to come up with the following by extending the m-> macro defined in the article to create a threaded maybe monad for simpler usage. TBH it doesn't come any simpler than just using some-> but this was for personal curiosity.

OK, to start there's some one off boiler plate code to define, here (in case the article ever vanishes) is Giles' threaded monad definition:

(defn bind-monadic-expr-into-form [insert form]
  (list 'm-bind insert
        (if (seq? form)
          `(fn [bound#] (~(first form) bound# ~@(rest form)))
          `(fn [bound#] (~form bound#)))))

(defmacro m->
  ([m x]
   `(with-monad ~m ~x))
  ([m x form]
   `(with-monad ~m
                ~(bind-monadic-expr-into-form x form)))
  ([m x form & more]
   `(m-> ~m (m-> ~m ~x ~form) ~@more)))

Now with this, you can define a threaded maybe macro as

(defmacro maybe->
  ([x] `(m-> ~maybe-m ~x))
  ([x form] `(m-> ~maybe-m ~x ~form))
  ([x form & more] `(maybe-> (maybe-> ~x ~form) ~@more)))

And use it like:

(maybe-> 1 inc)
=> 2

(maybe-> [1 2] (#(map inc %)))
=> (2 3)

(defn f1 [x] (+ 1 x))
(maybe-> 1 f1 f1)
=> 3

(maybe-> 1 f1 ((constantly nil)) f1)
=> nil

(maybe-> {:a 1 :b 2} :c inc)
=> nil

There's absolutely no advantage in using this over some-> in this context, but the m-> monad does add some interesting abilities in being able to create a fail-> macro as in the article I linked, which offers more than just "nil" as a return, giving you ability to differentiate the failure reason.