2

I have a namespace like this:

(ns foo.core)

(def ^:dynamic *debug-fn*
  "A function taking arguments [bar baz]"
  nil)

(defn bar-info
  [bar _]
  (println bar))

(defn baz-info
  [_ baz]
  (println baz))

(defn do-stuff
  [bar baz]
  (when *debug-fn* (*debug-fn* bar baz)))

(defn -main
  [& {:keys [debug-fn]}]
  (binding [*debug-fn* (symbol debug-fn)]  ;; THIS WON'T WORK!
    (do-stuff 27 42)))

What I would like to do is allow a debug function to be specified from the command line like this: lein run bar-info or lein run baz-info.

I'm not sure how to take the string specified as a command-line argument and turn it into the namespace-qualified function to bind. Do I need a macro to do this?

3 Answers 3

6

Use ns-resolve, you will need to specify namespace where your function is defined though.

user=> (defn f [n] (* n n n))
#'user/f
user=> ((ns-resolve *ns* (symbol "f")) 10)
1000
Sign up to request clarification or add additional context in comments.

Comments

2

Use alter-var-root:

user=> (doc alter-var-root)
-------------------------
clojure.core/alter-var-root
([v f & args])
  Atomically alters the root binding of var v by applying f to its
  current value plus any args
nil
user=> (alter-var-root #'*debug-fn* (fn [v] (fn [x] (println x) x)))
#<user$eval171$fn__172$fn__173 user$eval171$fn__172$fn__173@7c93d88e>
user=> (*debug-fn* 1)
1
1

3 Comments

I think problem is not with the binding but with the translation from string to callable function
O yes, I see it now. Sorry :)
Well, there was also a problem with the binding, due to my real do-stuff function creating a Swing panel with some callbacks, thus defeating my good intentions with binding. So thanks for this!
0

Though I've accepted Guillermo's answer above, I figured that it might also be useful to add the solution I ended up going with:

(def debug-fns
  {:bar-info (fn [bar _] (println bar))
   :baz-info (fn [_ baz] (println baz))

(def active-debug-fns (atom []))

(defn activate-debug-fn!
  [fn-key]
  (let [f (debug-fns fn-key)]
    (if f
      (swap! active-debug-fns conj f)
      (warn (str "Debug function " fn-key " not found! Available functions are: "
                 (join " " (map name (keys debug-fns))))))))

(defn debug-fn-keys
  [args]
  (if (args "--debug")
    (split (or (args "--debug") "") #",")
    []))

(defn do-stuff
  [bar baz]
  (doseq [f @active-debug-fns]
    (f bar baz)))

(defn -main
  [& args]
  (let [args (apply hash-map args)]
    (doseq [f (debug-fn-keys args)]
      (activate-debug-fn! (keyword k)))
    (do-stuff 27 42)))

So now you can say something like lein run --debug bar-info to get info on bars, or lein run --debug bar,baz to get info on both bars and bazes.

Any suggestions to make this more idiomatic will be happily accepted and edited in. :)

Comments

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.