Подтвердить что ты не робот

Почему, когда-let и if-allow поддерживают множественные привязки по умолчанию?

Почему when-let и if-let не поддерживают несколько привязок по умолчанию?

Итак:

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

... вместо:

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

Я знаю, что могу написать свой собственный макрос или использовать монаду (как описано здесь: http://inclojurewetrust.blogspot.com/2010/12/when-let-maybe.html).

4b9b3361

Ответ 1

Потому что (для if-let, по крайней мере) неясно, что делать с случаями "else".

По крайней мере, мотивированный Лучший способ вложить if-let in clojure Я начал писать макрос, который сделал это. Учитывая

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

он сгенерировал

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

и мне было непонятно, как продолжить (есть два места для "else" ).

Вы можете сказать, что для любого отказа должна быть единственная альтернатива, или нет для when-let, но если какой-либо из тестов мутирует состояние, тогда все еще будет беспорядочно.

Короче говоря, это немного сложнее, чем я ожидал, и поэтому я предполагаю, что текущий подход позволяет избежать необходимости делать то, что должно быть решением.

Другой способ сказать одно и то же: вы предполагаете, что if-let должно входить как let. Лучшей моделью может быть cond, которая не является "вложенной, если", но более "альтернативной", и поэтому не подходит для областей применения... или, еще один способ сказать это: if не справляется с этим случаем.

Ответ 2

Вот когда-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)) [email protected]))
    `(do [email protected])))

Применение:

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


Вот если-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

EDIT: оказалось, что привязки не должны просачиваться в другую форму.

Ответ 3

Если вы используете 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 при достижении значения nil.

(из раздела 6.5.1 ноль)