Нам нужно вставить первый аргумент во все последующие предложения перед их оценкой. Таким образом, решение должно быть макросом. Если мы готовы обойтись без проверки синтаксиса, мы можем определить ее довольно кратко:
(defmacro or-> [arg & pred-exprs]
(let [insert-expr (fn [[x & xs]] (list* x arg xs))
inserted-exprs (map insert-expr pred-exprs)]
(cons 'or inserted-exprs)))
Работает:
problem=> (or-> "foobar"
(clojure.string/starts-with? "foo")
(clojure.string/starts-with? "bar"))
true
problem=> (or-> "foobar"
(clojure.string/starts-with? "for")
(clojure.string/starts-with? "bar"))
false
Но он оценивает форму arg
для каждого предпринятого предложения. Чтобы оценить форму arg
только один раз, мы вставляем форму let
для gensym
:
(defmacro or-> [arg & pred-exprs]
(let [arg-sym (gensym)
insert-arg (fn [[x & xs]] (list* x arg-sym xs))
inserted-exprs (map insert-arg pred-exprs)]
`(let [~arg-sym ~arg] (or ~@inserted-exprs))))
Я бы хотел сделать это с помощью автогензима, но я могу понять, как это сделать. Однако это сделано, это не многопоточность в обычном смысле Clojure, который всегда выполняет все предложения с резьбой.