, хотя это не рекомендуется и противоречит закону даже думать о коротком замыкании исполнения таким образом, это все еще вполне возможно с макросами (исключительно для образования и развлечения)
(defmacro return [& x]
`(list '~'return (do ~@x)))
(defmacro dotimes+ [[i n] & body]
`(loop [~i 0 res# nil]
(cond (and (list? res#) (= '~'return (first res#))) (second res#)
(< ~i ~n) (recur (inc ~i) (do ~@body))
:else res#)))
можно использовать так:
user> (dotimes+ [i 10]
(println i)
(if (== i 5) (return :short-circuited)))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
:short-circuited
user> (dotimes+ [i 10]
(println i)
(if (== i 5) (return)))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
nil
user> (dotimes+ [i 10]
(println i))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
;; 6
;; 7
;; 8
;; 9
nil
обратите внимание, что он по-прежнему ожидает, что макрос return
будет вызываться в хвостовой позиции (аналогично recur
в loop
макросе)
(dotimes+ [x 4]
(println "attempt" (inc x))
(let [answer (read-line)]
(println "answer is:" answer)
(if (= answer "yes")
(return "YEAH!!!")
(println "WRONG!"))))