如何避免在clojure中嵌套

时间:2014-09-17 12:07:45

标签: 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!函数是嵌套丑陋的,很难理解它,如何编写一个宏来避免嵌套很多。或者其他方法来避免它。

3 个答案:

答案 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,因为这更符合它在此处的作用单子。