簡體   English   中英

如何避免在Clojure中嵌套

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM