Zaz
Zaz

Reputation: 48769

Passing variables and metadata

I wrote a short function for debugging:

(defn printvar
    "Print information about given variables in `name : value` pairs" 
    [& vars]
    (dorun (map #(println (name %) ":" (eval %)) vars)))

Then I tried to test it:

(defn -main [arg1 arg2]
    (def moustache true) (def answer 42) (def ocelots-are "awesome!")
    (printvar 'moustache 'answer 'ocelots-are)
    (printvar 'arg1 'arg2))

But ran into some really confusing behaviour:

$ lein repl
> (-main "one" "two")
# moustache : true
# answer : 42
# ocelots-are : awesome!
# CompilerException java.lang.RuntimeException: Unable to resolve symbol: arg1 in this context, compiling:(/tmp/form-init4449285856851838419.clj:1:1)

$ lein run "one" "two"
# Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: moustache in this context, compiling:(/tmp/form-init4557344131005109247.clj:1:113)

Experimenting a bit more, I discovered this:

(defn -main [arg1 arg2]
    (meta #'arg1))
# Exception in thread "main" java.lang.RuntimeException: Unable to resolve var: arg1 in this context, compiling:(dict_compress/core.clj:9:11)

(defn -main [arg1 arg2]
    (def arg1 arg1)
    (meta #'arg1))
# {:ns #<Namespace dict-compress.core>, :name arg1, :file dict_compress/core.clj, :column 2, :line 10}

Now I'm totally confused.

What exactly are you passing when you do (f 'var) and (f var)?

Why are there different behaviours when run from the REPL versus directly?

What's the difference between a received argument versus a defined variable?

How can I fix my code?

Am I going about debugging the wrong way?

Upvotes: 3

Views: 321

Answers (2)

T.Gounelle
T.Gounelle

Reputation: 6033

Just to elaborate on @Guillermo's comment, here is a macro that does the printing of any variable, locally or globally bound.

(defmacro printvar
  ([])
  ([v & more]
   `(let [v# ~v]
      (println '~v "=" v#)
      (when (seq '~more)
        (printvar ~@more)))))

With this you can try the sequence :

user> (def glob-var "foo")
#'user/glob-var
user> (defn -main [loc1 loc2]
        (printvar glob-var loc1 loc2))
#'user/-main
user> (-main "bar" 42)
glob-var = foo
loc1 = bar
loc2 = 42
nil
user>

Upvotes: 0

guilespi
guilespi

Reputation: 4702

Inside printvar the def'ed vars moustache answer and ocelots-are are correctly printed because def defines them as "globals".

Meaning there is a moustache var that the printvar function can "see".

Think about it this way, this works:

(def moustache 43)
(defn printvar [] 
   (println moustache)
(defn main [arg1]
    (printvar))

This doesn't work:

(defn printvar []
    (println arg1))
(defn main [arg1]
    (printvar))

Which is exactly what you're doing, passing the parameter name to eval does nothing for the parameter scope (printvar won't be able to see it).

A couple of issues with your code:

  • You shouldn't be defing inside a function, local bindings are defined with let
  • If you want to eval you need to consider scope of what you're evaling.

Upvotes: 2

Related Questions