Почему, когда-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).
Ответы
Ответ 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 ноль)