         (if (next r#) r#
           (let [r# (nth r# 0)]
             (if (instance? Exception r#) (throw r#) r#))))]))
+
+(defmacro atomic
+  "Alpha - subject to change!
+  Tool to ease Redis transactions for fun & profit. Wraps body in a `wcar`
+  call that terminates with `exec`, cleans up reply, and supports automatic
+  retry for failed optimistic locking.
+
+  Body must contain a `multi` call and may contain calls to: `watch`, `unwatch`,
+  `discard`, etc. Ref. http://redis.io/topics/transactions for more info.
+
+  `return` and `parse` NOT supported after `multi` has been called.
+
+  Like `swap!` fn, body may be called multiple times so should avoid impure or
+  expensive ops.
+
+  ;;; Atomically increment integer key without using INCR
+  (atomic {} 100 ; Retry <= 100 times on failed optimistic lock, or throw ex
+
+    (watch  :my-int-key) ; Watch key for changes
+    (let [curr-val (-> (wcar {} ; Note additional connection!
+                         (get :my-int-key)) ; Use val of watched key
+                       (as-long)
+                       (or 0))]
+      (return curr-val)
+
+      (multi) ; Start the transaction
+        (set :my-int-key (inc curr-val))
+        (get :my-int-key)
+      ))
+  => [[\"OK\" nil \"OK\" \"QUEUED\" \"QUEUED\"] ; Prelude replies
+      [\"OK\" \"1\"] ; Transaction replies (`exec` reply)
+      ]
+
+  See also `lua` as alternative way to get transactional behaviour."
+  [conn-opts max-cas-attempts & body]
+  `(atomic* ~conn-opts ~max-cas-attempts (do ~@body)
+     (throw (ex-info (format "`atomic` failed after %s attempt(s)"
+                       ~max-cas-attempts)
+              {:nattempts ~max-cas-attempts}))))
 
 (comment
   ;; Error before exec (=> syntax, etc.)

  :alpha, :asc, :desc."
  [key & sort-args] (apply sort key (parse-sort-args sort-args)))

(defmacro atomic
  "Alpha - subject to change!!
  Tool to ease Redis transactions for fun & profit. Wraps body in a `wcar`
  call that terminates with `exec`, cleans up reply, and supports automatic
  retry for failed optimistic locking.

  Body must contain a `multi` call and may contain calls to: `watch`, `unwatch`,
  `discard`, etc. Ref. http://redis.io/topics/transactions for more info.

  `return` and `parse` NOT supported after `multi` has been called.

  Like `swap!` fn, body may be called multiple times so should avoid impure or
  expensive ops.

  ;;; Atomically increment integer key without using INCR
  (atomic {} 100 ; Retry <= 100 times on failed optimistic lock, or throw ex

    (watch  :my-int-key) ; Watch key for changes
    (let [curr-val (-> (wcar {} ; Note additional connection!
                         (get :my-int-key)) ; Use val of watched key
                       (as-long)
                       (or 0))]
      (return curr-val)

      (multi) ; Start the transaction
        (set :my-int-key (inc curr-val))
        (get :my-int-key)
      ))
  => [[\"OK\" nil \"OK\" \"QUEUED\" \"QUEUED\"] ; Prelude replies
      [\"OK\" \"1\"] ; Transaction replies (`exec` reply)
      ]

  See also `lua` as alternative way to get transactional behaviour."
  [conn-opts max-cas-attempts & body]
  (assert (>= max-cas-attempts 1))
  `(let [conn-opts#  ~conn-opts
         max-idx#    ~max-cas-attempts
         prelude-result# (atom nil)

                 ;; Was [] with < Carmine v3
                 (return r#)
                 (if (= idx# max-idx#)
                   (do ~on-failure)
                   (recur (inc idx#)))))))]

     [@prelude-result#
