在clojure中调试包装函数

时间:2012-09-10 23:05:38

标签: debugging clojure

我有这个唠叨的问题:

Wrappers是接受函数的函数,返回另一个接受参数的函数,对参数执行某些操作并将其重新添加到函数中。

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

我想要做的是跟踪从包装器到包装器的参数更改

例如,我可以定义两个包装器:

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

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

然后,

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

相当于:

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

现在,我想要定义dbg->以便

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

仍然给我相同的功能等价物,但也跟踪旧的和新的值:

(h-dbg 1) => 9

但也会在控制台中显示调试信息:

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

这种模式对调试这样的环包装器非常有用,可以弄清楚每个包装器的作用,例如,这个典型的例子:

(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 个答案:

答案 0 :(得分:2)

我相信你正在寻找一种叫做writer monad的东西。

Here's对作家monad的一个很好的解释以及它在行动中的一些例子(对不起,它在Haskell中)。

基本上,这个monad可以帮助你编写除了“正常”输出之外还返回记录值的函数。例如,如果您有这些类型的函数:

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

你可以使用编写器monad来组合它们以获得一个类型为的新函数:

h :: a -> (c, l)

警告:在编写monadic函数时,Clojure宏->->>不能以完全相同的方式使用,因为您需要使用{{1}对于构图。

作为替代解决方案,您可以使用副作用包装器:

bind

这是有效的,因为函数体中最后一个表达式的值是它的返回值 - 先前的表达式被计算并且它们的结果被抛弃了。

答案 1 :(得分:0)

快速而肮脏的方法是将其填充到宏中......此实现仅使用单值参数执行函数。

(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)"

答案 2 :(得分:-1)

也许add-watch可以帮助您做到这一点。

限制是您只能观看参考(atom / ref / var / agent)。