1
(defmacro block [ctx & expr]
    `(let [~@(mapcat (fn [[k v]] [k `~v]) ctx)]
         ~@expr
     ))

(defn action1 [] (print "action1") (rand-nth [true false]))
(defn action2 [] (print "action2") (rand-nth [true false]))

( block  { __blockaddrabsolute "1_1" __blockaddr "1_1"}
      ( block  {typeofparent "ummutate"  __nodeid "c21f80" __blockaddr "1_1_1"} ( action1 ))
      ( block  {__blockaddrabsolute "1_1_2" __nodeid "c60590" __blockaddr "1_1_2"} ( action2 ))
      ( block  {__blockaddrabsolute "1_1_3" __nodeid "c60595" __blockaddr "1_1_3"} ( action1 )) 
      ( block  {__blockaddrabsolute "1_1_4" __nodeid "c60596" __blockaddr "1_1_4"} ( action2 ))
 "end" )

I want to break the execution from macro evaluation if any of the action returns false.

Expected output :

action1 true
action2 true
action1 false

1 Answer 1

2

The short-circuiting behavior you want is available through if/when forms, so we can use macros to transform a series of forms in the body into nested when forms:

(defmacro block [bindings & body]
  (let [whens (reduce (fn [acc elem]
                        `(when ~elem ~acc))
                      (last body)
                      (reverse (butlast body)))]
    `(let [~@(mapcat (fn [[k v]] [k `~v]) bindings)]
       ~whens)))

Then if we macroexpand your sample block form we get this (reformatted for readability):

(let* [__blockaddrabsolute "1_1" __blockaddr "1_1"]
  (when (block {typeofparent "ummutate", __nodeid "c21f80", __blockaddr "1_1_1"} (action1))
    (when (block {__blockaddrabsolute "1_1_2", __nodeid "c60590", __blockaddr "1_1_2"} (action2))
      (when (block {__blockaddrabsolute "1_1_3", __nodeid "c60595", __blockaddr "1_1_3"} (action1))
        (when (block {__blockaddrabsolute "1_1_4", __nodeid "c60596", __blockaddr "1_1_4"} (action2))
          "end")))))

Because your action1/action2 functions return random booleans you'll get varying results, but you do get the desired short-circuiting behavior. If any of the nested forms fail the when test, the ultimate result will be nil.

I'd consider refactoring this by introducing a more focused, generally useful do-like macro that short-circuits when any of its inner forms aren't truthy, and isn't concerned with bindings at all. Then use let for your inner bindings:

(defmacro do-when [x & xs]
  (if xs
    `(when ~x (do-when ~@xs))
    `~x))

(do-when
  (let [x 1 y 2] (println "step 1") (= x (dec y)))
  (rand-nth [true false])
  "randomly successful result")
Sign up to request clarification or add additional context in comments.

2 Comments

what if action1 and action2 are macros instead of function (defmacro action1 [] `(let [~'flag (rand-nth [true false])] (do (println (str "action1" ~'flag )) ~'flag) ))
@Jai that should be fine, assuming your inner macros expand to valid code. Something else I wanted to suggest is trying to think about this problem in terms of data transformations instead of imperative-style statements. This seems like it could be represented as a tree and evaluated with simple functions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.