fun with vars
DESCRIPTION
A basic intro and exploration into Clojure's Vars... with some fun had with macros. This presentation was given during the Aug. 2009 meeting of the Bay Area Clojure Meetup (http://www.meetup.com/The-Bay-Area-Clojure-User-Group/). Thanks to Amit Rathore for help putting this together. Also, thanks to Runa.com for hosting the Meetup.TRANSCRIPT
Kyle ObaRuna Employee #8@mudphonekyleoba@gmail
FUN with Vars
Bay Area Clojure MeetupAug 6, 2009
Tuesday, August 11, 2009
agenda
いこうクロージュル # => ikouCLOJURE
• demonstration of the early stages of an online api lookup tool
how routing works in WEBBING
• why not start with a macro?
• how it turned out to be a Var problem
READER => EVALUATION => VARS
• in which you should remember THREE things
1. var is a special form
2. this is possible:
3. if a symbol is NOT RESOLVED to a value ON EVALUATION => BOOM!
RECUR
symbol Varmap valuebinding
Tuesday, August 11, 2009
agenda
いこうクロージュル # => ikouCLOJURE
• demonstration of the early stages of an online api lookup tool
how routing works in WEBBING
• why not start with a macro?
• how it turned out to be a Var problem
READER => EVALUATION => VARS
• in which you should remember THREE things
1. var is a special form
2. this is possible:
3. if a symbol is NOT RESOLVED to a value ON EVALUATION => BOOM!
RECUR
unresolv’d sym eval boom
var = special
sym - Var - value
symbol Varmap valuebinding
Tuesday, August 11, 2009
ikouCLOJURE : routing
(def routes-map { ...
"/doc/get" ikou-doc/get-doc "/docs/find" ikou-doc/get-find-doc
"/docs/ns" ikou-doc/docs-for-ns "/names/ns" ikou-doc/names-for-ns
...})
Tuesday, August 11, 2009
ikouCLOJURE
sort of demonstration
Tuesday, August 11, 2009
ikouCLOJURE : routing
(def routes-map { ...
"/doc/get" ikou-doc/get-doc "/docs/find" ikou-doc/get-find-doc
"/docs/ns" ikou-doc/docs-for-ns "/names/ns" ikou-doc/names-for-ns
...})
Tuesday, August 11, 2009
ikouCLOJURE : routing
(def routes-map { "/doc/get" ikou-doc/get-doc})
FIRST TRY: a macro (why not?)
(defmacro get-doc [thing] `(with-out-str (doc ~thing)))
get-doc WORKS at the REPL
but, it doesn’t EVALUATE as part of the routes-map=> java.lang.Exception: Can't take value of a macro
at clojure.lang.Compiler.analyzeSymbol(Compiler.java:4684)
Tuesday, August 11, 2009
ikouCLOJURE : to be absolutely clear about that macro thing
(defmacro m-get-doc [thing] `(with-out-str (doc ~thing)))
(defn get-doc-w [x] (m-get-doc x))
this WON’T EVALUATE
it throws a java.lang.Exception: Unable to resolve var: x in this context
[Thrown class clojure.lang.Compiler$CompilerException]
Tuesday, August 11, 2009
ikouCLOJURE : to be absolutely clear that error
even this WON’T EVALUATE
(defn bogus-doc [x] (doc x))
it throws a java.lang.Exception: Unable to resolve var: x in this context
[Thrown class clojure.lang.Compiler$CompilerException]
Tuesday, August 11, 2009ask folks to remember, we got an ERROR here
ikouCLOJURE : unresolvable Var error
why are we getting this error?
java.lang.Exception: Unable to resolve var: x in this context[Thrown class clojure.lang.Compiler$CompilerException]
Tuesday, August 11, 2009
Clojure.org: a small vocabulary lesson
DATA STRUCTURES
READER
COMPILER
EVALUATION
SYMBOL
VAR
Tuesday, August 11, 2009
Clojure.org: http://clojure.org/reader
CLOJURE DATA STRUCTURES
“Clojure is a homoiconic language, which is a fancy term describing the fact that Clojure programs are represented by Clojure data structures.”
“Clojure is defined in terms of the evaluation of data structures and not in terms of the syntax of character streams/files.”
“That said, most Clojure programs begin life as text files, and it is the task of the reader to parse the text and produce the data structure the compiler will see. This is not merely a phase of the compiler. The reader, and the Clojure data representations, have utility on their own...”
WTF? AWESOME, right?
Tuesday, August 11, 2009Since itʼs just Clojure data structures, programs can create programsClojure programs could begin life as anything that leads to these data structures.
ninjas
REALULTIMATEPOWER.NET
Tuesday, August 11, 2009this is why macros are possiblethis is why clojure (and lisps) have parens
Clojure.org: http://clojure.org/reader
SRSLY. WHAT DID THAT MEAN?
“One might say the reader has syntax defined in terms of characters,
and the Clojure language has syntax defined in terms of symbols, lists, vectors, maps etc.”
text -> READER -> clojure data structures -> COMPILER -> EVALUATION
Tuesday, August 11, 2009reader reads charaters (as forms)compiler reads clojure language as clojure data structures, which are the objects returned by the reader
Clojure.org: http://clojure.org/Evaluation
EVALUATION
“Evaluation can occur in many contexts: Interactively, in the REPL On a sequence of forms read from a stream, via load or load-file Programmatically, via eval”
text -> READER -> clojure data structures -> COMPILER -> EVALUATION
Tuesday, August 11, 2009
Clojure.org: http://clojure.org/Evaluation
EVALUATION
“ Clojure programs are composed of expressions.
Every form not handled specially by a special form
or macro is considered by the compiler to be an expression, which is evaluated to yield a value... ...a single object is considered by the compiler, evaluated, and its result returned. If an expression needs to be compiled, it will be. There is no separate compilation step, nor any need to worry that a function you have defined is being interpreted. Clojure has no interpreter.”
unresolv’d sym eval boom
var = special
sym - Var - value
Tuesday, August 11, 2009THING TO REMEMBER #1: there are these “special forms”
Clojure.org: http://clojure.org/Evaluation
EVALUATION
SYMBOL Resolution
is namespace qualified? -> binding of named global var is package qualified? -> Java class
else, in order...1. is special form? -> special form stuff2. maps to class in current ns? -> Java class 3. binds locally? -> value of local binding4. maps to Var? -> value of binding of the Var5. error
If you remember, we were trying to figure out why we had that error...
unresolv’d sym eval boom
var = special
sym - Var - value
Tuesday, August 11, 2009THING #2: if symbol maps to Var, then binding of Var is the evaluated valueTHING #3: if symbol is not evaluated to a value, this causes an error
back to the error
so, we had that error...
even this WON’T EVALUATE
(defn bogus-doc [x] (doc x))
it throws a java.lang.Exception: Unable to resolve var: x in this context
[Thrown class clojure.lang.Compiler$CompilerException]
WHY?
Tuesday, August 11, 2009
here’s a hint
“doc” is a macro
which expands like so
user> (macroexpand-1 '(doc map))
(clojure.core/print-doc (var map))
and “print-doc” works like this
user> (print-doc #'map) ------------------------- clojure.core/map ([f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls]) Returns a lazy sequence ...
Tuesday, August 11, 2009
back to the error
so, our offending fn is expands to
(defn bogus-doc [x] (print-doc (var x)))
and it throws a java.lang.Exception: Unable to resolve var: x in this context
[Thrown class clojure.lang.Compiler$CompilerException]
STILL WHY?
Tuesday, August 11, 2009
back to the error
now that we know more about symbol resolution and evaluation
let’s take a closer look
java.lang.Exception: Unable to resolve var: x in this context[Thrown class clojure.lang.Compiler$CompilerException]
Tuesday, August 11, 2009so maybe the var special form in bogus-doc is causing the problem
back to the error
real quick-like...
what does “def” do?
Var without binding user=> (def x) #'user/x user=> x java.lang.IllegalStateException: Var user/x is unbound.
Var with binding user=> (def x 1) #'user/x user=> x 1
unresolv’d sym eval boom
var = special
sym - Var - value
Tuesday, August 11, 2009what then, is the var special form resolving x to (in bogus-doc)?
back to the error
so, it’s the var special form
when “defn bogus-doc” is evaluated
what does (var x) refer to?
(defn bogus-doc [x] (doc x))
aka
(defn bogus-doc [x] (print-doc (var x)))
answer: nothingunresolv’d sym eval boom
var = special
sym - Var - value
Tuesday, August 11, 2009answer: nothinghence the error
an example
you can get my-doc to evaluate properly, if you declare y first
user> (declare y) #'user/y user> (defn my-doc [y]
(doc y)) #'user/my-doc
but, what’s this?
user> (my-doc map) ------------------------- user/y nil nil nil
unresolv’d sym eval boom
var = special
sym - Var - value
Tuesday, August 11, 2009the doc info for “map” is not correct, it appears to be the doc info for user/y
what happened to y?
remember
doc is a macro user> (macroexpand-1 '(doc filter)) (clojure.core/print-doc (var filter))
var is a special form
so...
• doc expands to (print-doc (var y)) at evaluation
• my-doc's print-doc argument is the Var #'user/y
• the "argument y" ISN’T USED
unresolv’d sym eval boom
var = special
sym - Var - value
Tuesday, August 11, 2009
another example
another way to say that (var y) isn’t resolving to the fn’s arg
user> (defn verbose-doc [y] (doc y) (println y))
#'user/verbose-doc user> (verbose-doc map) ------------------------- user/y nil nil #<core$map__4564 clojure.core$map__4564@20cc1fc4> nil
unresolv’d sym eval boom
var = special
sym - Var - value
Tuesday, August 11, 2009
RECUR : ikouCLOJURE
so, how do we fix our original problem?
I wanted to get the doc output for an input string.
Tuesday, August 11, 2009youʼve kicked that dead horse enough
RECUR : ikouCLOJURE
this works... and you don’t need a macro
(defn get-doc [str-name] (with-out-str (print-doc (resolve (symbol str-name)))))
Tuesday, August 11, 2009
RECUR : ikouCLOJURE
why not use "doc" ? doc is a macro that syntactically replaces the form with a (var y) special form when evaluated(var y) will cause an error if it doesn’t resolve to a value
unresolv’d sym eval boom
var = special
sym - Var - value
Tuesday, August 11, 2009
finally
an interesting point
• "doc" info is just meta-data
• but where? on the Var, not on the object
• that's why print-doc takes a Var
Tuesday, August 11, 2009
finally
an example
metadata sits on the var
user> (meta (var map))
{:ns #<Namespace clojure.core>, :name map, :file "clojure/core.clj", :line 1588, :arglists ([f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls]), :doc "Returns a lazy sequence consisting of the result of applying f to the\n set of first items of each coll, followed by applying f to the set\n of second items in each coll, until any one of the colls is\n exhausted. Any remaining items in other colls are ignored. Function\n f should accept number-of-colls arguments."}
Tuesday, August 11, 2009
finally
if that made sense to youthen this might too
user> (declare y) user> (defn huh [y]
(doc y) (binding [map y]
(map "hello") (meta #'map)))
user> (huh println) ------------------------- user/y nil ... hello {:ns #<Namespace clojure.core>, :name map, :file "clojure/core.clj", :line 1588, :arglists ([f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls]), :doc "Returns a lazy sequence consisting of the result of applying f to the\n set of first items of each coll, followed by applying f to the set\n ...}
unresolv’d sym eval boom
var = special
sym - Var - value
Tuesday, August 11, 2009
thank you any questions?
Tuesday, August 11, 2009