[英]how to avoid nesting in clojure
當我編寫一個功能來檢查用戶可以通過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!
函數嵌套很丑,很難理解,如何編寫宏來避免大量嵌套。
這不需要宏。 我猜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")))))
這是很多人遇到的事情,所以不要感到難過。
請查看Christophe Grand的這個博客 ,我認為這是一個非常不錯的(簡潔的!)解決方案。
編輯:如果您需要像原始版本一樣縮短執行delay
,則只需要像這樣的花哨的內容(或者在其他文章中使用delay
的版本)-否則,noisesmith的答案就是解決之道。
這是您可以使用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
,因為這與在此monad中的工作方式更為相似。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.