Maybe in clojure

混江龙づ霸主 提交于 2020-01-06 02:45:07

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!