2
votes

I have this nagging problem:

Wrappers are functions that take a function, and returns another function that takes in a parameter, does something to the parameter and sticks it back into the function.

(defn wrapper [func]
  (fn [params]
    (func (do-something-to params))))

and what I'd like to do is to track parameter changes as it goes from wrapper to wrapper

for example, I can define two wrappers:

(defn wrap-inc [func]
  (comp func inc))

(defn wrap-double [func]
  (comp func #(* % 2)))

then,

(def h (-> #(* % 3)
       wrap-inc
       wrap-double))

is equivalent to:

(def h (fn [x] (* (inc (* 2 x)) 3)))
(h 1) => 9

Now, I would want to define dbg-> so that

(def h-dbg (dbg-> #(* % 3)
                  wrap-inc
                  wrap-double))

to still give me the same functional equivalent, but also keep track of old and new values:

(h-dbg 1) => 9

but will also display debug infomation in the console:

"wrap-double: (in: 1, out: 2)"
"wrap-inc: (in: 2, out 3)"

This sort of pattern will be extremely useful to debug ring wrappers like this to figure out what each one is doing, for example, this typical example:

(defn start []
  (jetty/run-jetty
   (-> #'routes-handler
       ;;(wrap-reload-modified ["clj-src"])
       (wrap-file "resources/public")
       wrap-file-info
       wrap-decamelcase-params
       wrap-keyword-params
       wrap-nested-params
       wrap-params
       wrap-ignore-trailing-slash) {:port 8890 :join? false}))
3

3 Answers

2
votes

I believe you're looking for something called the writer monad.

Here's a nice explanation of the Writer monad and some examples of it in action (sorry, it's in Haskell).

Basically, this monad helps you compose functions that also return, in addition to their "normal" output, a logging value. For example, if you had functions with these types:

f :: a -> (b, l)
g :: b -> (c, l)

you could use the writer monad to compose them to get a new function with type:

h :: a -> (c, l)

Warning: the Clojure macros -> and ->> can't be used in the exact same way when composing monadic functions, since you need to use bind for composition.

As an alternative solution, you could use a side-effecting wrapper:

(defn wrapper
  [f]
  (fn [& params]
      (print params) ;; or some other logging operation
      (apply f params)))

This works because the value of the last expression in the body of a function is its return value -- previous expressions are evaluated and their results thrown away.

0
votes

A quick and dirty way is to stuff it into a macro... this implementation only does functions with single valued arguments.

(defn wrap-print [f]
  (fn [& args]
    (print " ->" (second (re-find #"\$(.*)@" (str f))) args)
    (apply f args)))

(-> 3 ((wrap-print inc)) ((wrap-print dec)))
;; =>       "-> inc (3) -> dec (4)"

(defmacro dbg-> [n & funcs]
  (println "")
  (print n)
  (let [wfncs (map #(list (list wrap-print %)) funcs)]
     `(-> ~n ~@wfncs )))

(dbg-> 3 inc dec)
;; =>      "3 -> inc (3) -> dec (4)"

-1
votes

Maybe add-watch can help you doing this.

The limitation is you can only watch references(atom/ref/var/agent).