问题
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?
回答1:
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!
回答2:
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))
回答3:
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
回答4:
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.
来源:https://stackoverflow.com/questions/30242136/maybe-in-clojure