Herein appears a new favorite pattern for defining macros that define functions that define "out params." Clever? Or madness? Time will tell...

In this code:


(defmacro require-member-id [member-id & body]
  `(if ~member-id
     (do ~@body)
     [false "member-id missing"]))

(def ^:dynamic handle-prevayler!
  "Can be replaced in unit tests using `binding` to inject exceptions, etc."
  (fn [p cmd args]
    (prevayler/handle! p [cmd args])))

(defn- pfn [p]
  (fn [cmd args on-success]
    (try
      (handle-prevayler! p cmd args)
      on-success
      (catch Throwable t [false (ex-message t)]))))

(defmacro defopn
  "This macro defines a prevayler operation function. The first 2 args
   NOT tagged with `^:out` must be the prevayler instance and the member-id,
   respectively. The first parameter tagged with `^:out` is not passed into
   the generated function, but is instead bound to the result of invoking `pfn`
   on the given prevayler instance."
  [n args & body]
  (let [v (map (juxt meta identity) args)

        [p member-id :as in]
        (into [] (->> v (filter (comp not :out first)) (map second)))

        [p!] (into [] (->> v (filter (comp :out first)) (map second)))]
    `(defn ~n [~@in]
       (require-member-id ~member-id
         (let [~p! (pfn ~p)]
           ~@body)))))

(defopn perform-save! [_p member-id checklist checklist-vars, ^:out p!]
  (let [checklist-id (*new-checklist-id*)]
    (p!
      "save"
      {:id checklist-id
       :member-id member-id
       :checklist checklist
       :checklist-vars checklist-vars}
      [true checklist-id])))

(defopn perform-delete! [_p member-id checklist-id, ^:out p!]
  (p! "del" {:id checklist-id, :member-id member-id} [true]))

This essentially introduces an "around aspect" around each of the functions defined using defopn. The namespace I pulled this from uses this function 18 times, which saves quite a lot of code I think?

tags: coding