为什么默认情况下when-let和if-let不支持多个绑定?

时间:2012-07-26 18:52:54

标签: clojure

为什么when-letif-let默认不支持多个绑定?

所以:

(when-let [a ...
           b ...]
  (+ a b))

...而不是:

(when-let [a ...
  (when-let [b ...
    (+ a b)))

我知道我可以编写自己的宏或使用monad(如下所述:http://inclojurewetrust.blogspot.com/2010/12/when-let-maybe.html)。

3 个答案:

答案 0 :(得分:13)

因为(对于if-let,至少)对“其他”案件的处理并不明显。

至少,在Better way to nest if-let in clojure的推动下,我开始编写一个执行此操作的宏。鉴于

(if-let* [a ...
          b ...]
  action
  other)

它会生成

(if-let [a ...]
  (if-let [b ...]
    action
    ?))

我不清楚如何继续(“其他”有两个地方)。

你可以说任何失败都应该有一个替代方案,或者when-let都没有,但如果任何一个测试变异状态,那么事情仍然会变得混乱。

简而言之,它比我预期的要复杂得多,所以我猜当前的方法避免了对解决方案应该做什么的调用。

另一种说法相同的方式:你假设if-let应该像let那样嵌套。一个更好的模型可能是cond,它不是“嵌套if”,而是更多“替代if”,因此不适合范围...或者,另一种说法:{ {1}}不能更好地处理这种情况。

答案 1 :(得分:6)

这是when-let *:

(defmacro when-let*
  "Multiple binding version of when-let"
  [bindings & body]
  (if (seq bindings)
    `(when-let [~(first bindings) ~(second bindings)]
       (when-let* ~(vec (drop 2 bindings)) ~@body))
    `(do ~@body)))

<强>用法:

user=> (when-let* [a 1 b 2 c 3]
                (println "yeah!")
                a)
;;=>yeah!
;;=>1


user=> (when-let* [a 1 b nil c 3]
                (println "damn! b is nil")
                a)
;;=>nil


以下是if-let *:

(defmacro if-let*
  "Multiple binding version of if-let"
  ([bindings then]
   `(if-let* ~bindings ~then nil))
  ([bindings then else]
   (if (seq bindings)
     `(if-let [~(first bindings) ~(second bindings)]
        (if-let* ~(vec (drop 2 bindings)) ~then ~else)
        ~else)
     then)))

用法:

user=> (if-let* [a 1 
                 b 2 
                 c (+ a b)]
              c
              :some-val)
;;=> 3

user=> (if-let* [a 1 b "Damn!" c nil]
              a
              :some-val)
;;=> :some-val

编辑:事实证明,绑定不应该以其他形式泄露。

答案 2 :(得分:5)

如果您使用cats,那么您可能会发现mlet函数有用:

(use 'cats.builtin)
(require '[cats.core :as m])
(require '[cats.monad.maybe :as maybe])

(m/mlet [x (maybe/just 42)
         y nil]
  (m/return (+ x y)))
;; => nil
     

如您所见,当遇到零值时,mlet会短路。

(来自第6.5.1节)