当我写一个检查用户的函数可以删除clojure的帖子时,我得到了这个
(defn delete!
{:arglists}
[^String id]
(if (valid-number? id)
(let [result {:code 200 :status "error" :messag "delete success"}]
(if-let [user (session/get :userid)]
(if-let [post (pdb/id id)]
(if (= user (post :user_id))
(do
(pdb/delete! (Long/valueOf id))
(assoc result :status "ok"))
(assoc result :message (emsg :not-own)))
(assoc result :message (emsg :post-id-error))))
(assoc result :message (emsg :not-login)))))
所以我想解决它,我得到了这个
https://github.com/4clojure/4clojure/blob/develop/src/foreclojure/register.clj#L27
https://github.com/4clojure/4clojure/blob/develop/src/foreclojure/utils.clj#L32 但它是线,但不是巢。
delete!
函数是嵌套丑陋的,很难理解它,如何编写一个宏来避免嵌套很多。或者其他方法来避免它。
答案 0 :(得分:5)
这不需要宏。我猜cond
是一个宏,但它是我们唯一需要使这个代码可读的。
(defn delete!
;; {:arglists} ; this line will not compile
[^String id]
(let [result {:code 200 :status "error" :message "delete success"}
user (session/get :userid)
post (and user (valid-number? id) (pbd/id id))]
(cond
(not user)
(assoc result :message (emsg :not-login))
(not post)
(assoc result :message (emsg :post-id-error))
(not= user (:user_id post))
(assoc result :message (emsg :not-own))
:else
(do
(pdb/delete! (Long/valueOf id))
(assoc result :status "ok")))))
答案 1 :(得分:1)
这是很多人遇到的事情,所以不要感觉不好。
查看this blog by Christophe Grand,我认为这是一个非常好的(简洁!)解决方案。
编辑:你只需要像这样的东西(或this other post中使用delay
的版本),如果你需要像原版一样短路执行 - 否则噪音的答案是要走的路
答案 2 :(得分:-1)
以下是你如何使用Either monad做这种事情 - 我确定已经有了它的库但我会在这里实现它的完整性。 (注意:此代码尚未经过验证。)
(defn success? [v]
(contains? v :success))
(defn inject [v]
{:success v})
(defn bind [v f]
(if (success? v)
(apply f (:success v))
v))
(defmacro >>= [v & body]
(let [binds (map #(list 'bind %) body)]
`(-> ~v ~@binds)))
(defn delete!
{:arglists}
[^String id]
(if (valid-number? id)
(let [result {:code 200 :status "error" :message "delete success"}
check
(>>= (inject {:id id})
#(if-let [user (session/get :userid)]
{:success (assoc % :user user)}
(:failure (assoc result :message (emsg :not-login))))
#(if-let [post (pdb/id (:id %))]
{:success (assoc % :post post)}
{:failure (assoc result :message (emsg :post-id-error))})
#(if (= (:user %) ((:post %) :user_id))
{:success %}
{:failure (assoc result :message (emsg :not-own))}))]
(if (success? check)
(do
(pdb/delete! (Long/valueOf id))
(assoc result :status "ok"))
(:failure check)))))
>>=
宏的作用类似于->
宏(显然,因为它使用它),但是如果任何函数返回{:failure ...}
那么链短路(感谢bind
)并且失败的函数的失败值将成为>>=
返回的值。
修改强>
我应该注意到,我命名为inject
的函数实际上被称为return
,但我决定将其命名为inject
,因为这更符合它在此处的作用单子。