I am trying to better understand listing 13.3 in The Joy of Clojure. It is a macro that generates other macros (much like how primitive array functions are implemented in Clojure 1.4).
I want to write a macro that, when run, simply prints the suffix of the generated macro. i.e.
user=> (nested-macro joe)
user=> (nested-macro-named-joe)
hello from joe
nil
I am having trouble making this work.
Here is what I've tried:
Attempt 1
(defmacro nested-macro [name]
`(defmacro ~(symbol (str "nested-macro-named-" name))
[]
`(println "hello from " ~name)))
Output:
hello from #<core$name clojure.core$name#502c06b2>
Attempt 2
(defmacro nested-macro [name]
(let [local-name name]
`(defmacro ~(symbol (str "my-custom-macro-named-" ~local-name))
[]
`(println "hello from " ~local-name))))
Error
IllegalStateException Attempting to call unbound fn: #'clojure.core/unquote clojure.lang.Var$Unbound.throwArity (Var.java:43)
Attempt 3:
(defmacro nested-macro [name]
(let [local-name name]
`(defmacro ~(symbol (str "nested-macro-named-" name))
[]
`(println "hello from " ~(symbol local-name)))))
Compiler Error:
CompilerException java.lang.RuntimeException: No such var: joy.dsl/local-name
Just for the heck of it, I've also tried adding # to the local variables, with similar results as above but with "auto" names, such as local-name__1127__auto__ I don't see that being as part of the solution, however.
How can I make this work?
To know what is going wrong with macros I always use macroexpand-1.
From your first example:
(macroexpand-1 '(nested-macro joe))
Results in:
(clojure.core/defmacro nested-macro-named-joe []
(clojure.core/seq
(clojure.core/concat
(clojure.core/list (quote clojure.core/println))
(clojure.core/list "hello from ")
(clojure.core/list clojure.core/name))))
If you look at the last param, it shows that you are using clojure.core/name, which probably is not what you want, as you actually want the parameter named "name".
To fix it, just add another unquote to the name param, but as the name param is actually a symbol, what you really want is to get the name of it as in:
(defmacro nested-macro [the-name]
`(defmacro ~(symbol (str "nested-macro-named-" the-name))
[]
`(println "hello from " ~~(name the-name))))
Related
I have the following macro:
(defmacro my-macro [k]
`(do
(def pair
[
k
~(symbol (str "-" (name k)))]
)))
...which expands to:
(macroexpand-1 `(my-macro :n/k))
(do (def user/pair [user/k -k]))
...but instead I would like it to expand to
(do (def user/pair [:n/k -k]))
How can I make the macro keep the keyword and its namespace?
Thanks!
You need to escape k from the syntax quote using ~k:
(defmacro my-macro [k]
`(def ~'pair [~k ~(symbol (str "-" (name k)))]))
I've made a few other changes here as well:
Idiomatic formatting. Don't put ( or [ at the end of a line -- and put closing ) and ] on the same line as the expression they close.
do is entirely superfluous here.
If you want the macro to expand to (def pair ...), then you need to
escape out of the syntax quote (~)
quote the symbol pair (i.e., 'pair)
Putting this together, you have ~'pair. The reason you have to do this is because, in Clojure, `<symbol> is read as (quote <current-namespace>/foo>), where <current-namespace> stands for the current namespace. But def doesn't take names that are namespaced. Hence the ~' dance.
(But you probably want to parameterize on pair anyway ... otherwise, it's not very useful to use my-macro more than once per namespace.)
Overall, this seems like a very odd macro. I don't know what you're trying to accomplish, but I would probably take a different approach.
you can use the namespace and name function to extract the parts you want from the keyword passed in and combine them as required:
user> (defmacro my-macro [k]
`(do
(def pair
[~(keyword (str (namespace k) "/" (name k)))
~(symbol (str "-" (name k)))])))
#'user/my-macro
user> (macroexpand-1 `(my-macro :n/k))
(do (def user/pair [:n/k -k]))
Revised Answer
There are 2 things a bit confusing about your question & I misread it earlier.
You should use a regular single-quote ' with macroexpand-1, not the back-tic `. The back-tick is normally used only in a macro definition to delineate a piece of "template code".
I just noticed that the arg in the macro definition is k, and the keyword you use in the example is :n/k. These duplicate names will cause confusion.
Let's restate the problem:
(ns clj.demo)
(defmacro my-macro [arg]
`(do
(def pair
[
arg
~(symbol (str "-" (name arg)))]
)))
(println (macroexpand-1 `(my-macro :n/k)))
;=> (do (def clj.demo/pair [clj.demo/arg -k]))
So we are in the clj.demo namespace, which gets applied to the symbols pair and arg. We need to substitue the argument arg using ~:
(ns clj.demo)
(defmacro my-macro [arg]
`(do
(def pair
[
~arg
~(symbol (str "-" (name arg)))]
)))
(println (macroexpand-1 '(my-macro :n/k)))
;=> (do (def clj.demo/pair [:n/k -k]))
Which is what you want.
I would like to be able to define an anonymous function in my Lieningen project using Environ.
Here is what that part of the project file looks like:
{:env {:foo (fn [s]
(count s))}}
Then in my code, I would like to use that function. Something like:
(-> "here are a few words"
(env :foo))
And then to get the size of s.
Environ will simply invoke read-string on the slurped file. The value at :foo will be a list containing the symbol fn followed by a vector with the symbol s inside and so on. i.e. the form has not been evaluated and so you will not be able to invoke the anonymous fn.
see environ.core/read-env-file
Consider this:
(def f (read-string "(fn [s] (count s))"))
(f "test")
;; => ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.IFn
(def evaled-f (eval (read-string "(fn [s] (count s))")))
(evaled-f "test")
;; => 4
Also, your intended use is a little off. -> is a macro that will take the first arg and "thread" it into the first position of the following form.
(macroexpand '(-> "here are a few words" (env :foo)))
;; => (env "here are a few words" :foo)
I think you're looking for something like:
(let [f (eval (env :foo))]
(-> "here are a few words"
f))
(env :foo) returns list. To make function from it you can use eval or better macro like this:
(defmacro defn-env [fn-name env-key]
`(def ~fn-name ~(env env-key)))
(defn-env env-fn :foo) ; => #'user/env-fn
(env-fn [1 2 3]) ; => 3
Note: if (env :foo) returns nil you need to add :plugins [[lein-environ "1.0.0"]] to your project.clj.
I use a dynamic var to make an easy way to use ssh. However, it suddenly stops working in a multi-parameter for!
So, here is my core.clj (which is kind of sketchy now):
(use 'clj-ssh.ssh)
(def the-agent (ssh-agent {}))
(def ^:dynamic *session* nil)
(defmacro on-host [host & body]
`(binding [*session* (clj-ssh.ssh/session the-agent ~host {})]
~#body))
(defn cmd [& args]
(split (:out (ssh *session* {:cmd (join " " args)})) #"\n"))
(defn attempt-1 []
(cmd "ls -a"))
(defn attempt-2 []
(for [f (cmd "ls -a")]
f))
(defn attempt-3 []
(for [r (range 3)
f (cmd "ls -a")]
[r f]))
For some reason, first two trial functions work, and the third doesn't (the hosts and files are censured):
user=> (on-host "(some host)" (attempt-1))
["." ".." ".ackrc" ...]
user=> (on-host "(some host)" (attempt-2))
("." ".." ".ackrc" ...)
user=> (on-host "(some host)" (attempt-3))
IllegalArgumentException No implementation of method: :connected? of protocol: #'clj-ssh.ssh.protocols/Session found for class: nil clojure.core/-cache-protocol-fn (core_deftype.clj:544)
Just in case you need a stacktrace:
user=> (use 'clojure.stacktrace)
nil
user=> (print-stack-trace *e 7)
java.lang.IllegalArgumentException: No implementation of method: :connected? of protocol: #'clj-ssh.ssh.protocols/Session found for class: nil
at clojure.core$_cache_protocol_fn.invoke (core_deftype.clj:544)
clj_ssh.ssh.protocols$eval1554$fn__1566$G__1543__1571.invoke (protocols.clj:4)
clj_ssh.ssh$connected_QMARK_.invoke (ssh.clj:411)
clj_ssh.ssh$ssh.invoke (ssh.clj:712)
census.core$cmd.doInvoke (core.clj:15)
clojure.lang.RestFn.invoke (RestFn.java:408)
census.core$attempt_3$iter__1949__1955$fn__1956.invoke (core.clj:29)
nil
I'm really not sure what it is all about. Can you help me? Thank you!
Use doseq, not for
What you have there is a lazy sequence that will be evaluated after the binding form has returned. doseq forces evaluation.
For further reading:
Difference between doseq and for in Clojure
http://cemerick.com/2009/11/03/be-mindful-of-clojures-binding/
While I may incorrectly interpret the concept of homoiconicity, I've understood it as 'code being data'.
So, I can write code like this:
(def subject "world")
(def helo '(str "Hello " subject))
At this point, helo is only data, but can be executed as code like this:
(eval helo)
which returns "Hello world".
I can also continue to treat helo as data:
(first helo)
(count helo)
which returns respectively str and 3.
So far so good. However, as soon as I wrap the code in a function, I seem to lose the ability to treat code as data:
(defn helofn [subject]
(str "Hello " subject))
How do I decompose helofn? It seems that I can't treat it as data; if I do this:
(count helofn)
I get an exception:
java.lang.UnsupportedOperationException: count not supported on this type: user$helofn
Is there another way to decompose helofn, or am I just expecting too much from homoiconicity?
The helofn definition is data, but you're letting it be evaluated (just as you explicitly evaluated the helo list). If you treated the definition in the same way as helo, then it will remain data, and amenable to whatever transformations you want to apply:
(def helofndata '(defn helofn [subject]
(str "Hello " subject))
=> (second helofndata)
helofn
=> (eval helofndata)
#'user/helofn
defn is just a macro:
(macroexpand '(defn helofn [subject]
(str "Hello " subject)))
(def helofn (clojure.core/fn ([subject] (str "Hello " subject))))
If you define helofn the way you defined helo, you'll be able to treat it as data:
(def helofn '(fn [subject]
(str "Hello " subject)))
Now you can eval and call this function:
((eval helofn) "world")
and to treat it as a data:
(count helofn)
But, when you use defn macro you associates helofn variable with compiled function and not with it's code.
It's not just functions. Let's say you defined hello with the following code:
(def helo (str "Hello " subject))
Now hello is associated with "Hello world" string and not with (str "Hello " subject) code. So, now there is no way to get the code this string was built with.
N.B. If you want to treat clojure code as data you should look into its macros. Any code passed to a macro is treated as data and any data returned by a macro is treated as code.
Homoiconicity is a very powerful concept and I don't think you are expecting too much from it.
defn is actually a macro that uses the def special form to define a function, so:
(defn sq [x]
(* x x))
Is actually equivalent to:
(def sq (fn ([x] (* x x))))
So defn here is receiving the args sq [x] (* x x), then builds the list (def sq (fn ([x] (* x x)))), returns it as the result of the macro and is then eval'ed. This is all done through the manipulation of lists, maps, vectors, symbols, etc., by the defn macro.
The fact that in Clojure you can't get the original list of symbols from which you defined a function, has to do with the fact that in Clojure all code is compiled. This is why evaluating (fn [x] 1) in the REPL returns something like #<user$eval809$fn__810 user$eval809$fn__810#10287d>
. But still, as mentioned in a previous answer, the code that is evaluated is data.
Maybe I'm going too far with this, but if you wanted to have for each function you define, the data from which it was created, you could add it to its metadata by creating your own custom macro.
Here's a naive implementation for such a macro:
(defmacro defn* [x & body ]
(let [form `'~&form
x (vary-meta x assoc :form form)]
`(defn ~x ~#body)))
;=> #'user/defn*
(defn* sq [x]
(* x x))
;=> #'user/sq
(:form (meta #'sq))
;=> (defn* sq [x] (* x x))
&form is an implicit argument (together with &env) that contains the whole (unevaluated) form with which the macro was called (i.e. the data that is evaluated by the compiler).
Hope this helps and it doesn't bring more confusion.
It looks like no based on
get a clojure function's code
and
Can you get the "code as data" of a loaded function in Clojure?
Basically you can get the source from a function defined in a .clj file but there's no reliable way to retrieve the data structures that built a function from the function alone.
EDIT: Also I think you are expecting too much from homoiconicity. The code itself is data yes but it's fairly standard to not be able to retrieve the original source code based on the artifact emitted by that code. Like when I have 2 I have no way of knowing that it was produced by (+ 1 1) or (- 4 2) in the same way a function is a piece of data created by calling fn over some other data structures that get interpreted as code.
(use '[clojure.contrib.trace])
(dotrace [str] (reduce str [\a \b]))
In a nutshell:
That's because trace-fn-call, which is the thing dotrace uses to wrap the functions to be traced, uses str to produce the nice TRACE foo => val output.
Extended explanation:
The dotrace macro does its magic by installing a thread binding for each Var holding a function to be traced; in this case, there is one such Var, clojure.core/str. The replacement looks roughly like so:
(let [f ##'str]
(fn [& args]
(trace-fn-call 'str f args)))
The trace-fn-call, to quote its docstring, "Traces a single call to a function f with args.". In doing so, it calls the traced function, takes note of the return value, prints out a nice informative message of the form TRACE foo => val and returns the value obtained from the traced function so that regular execution may continue.
As mentioned above, this TRACE foo => val message is produced used str; however, in the case at hand, this is actually the function being traced, so a call to it leads to another call to trace-fn-call, which makes its own attempt to produce the tracing output string using str, which leads to another call to trace-fn-call... ultimately leading to the stack blowing up.
A workaround:
The following modified versions of dotrace and trace-fn-call should work fine even in the presence of weird bindings for core Vars (note that futures may not be scheduled promptly; if that's a problem, see below):
(defn my-trace-fn-call
"Traces a single call to a function f with args. 'name' is the
symbol name of the function."
[name f args]
(let [id (gensym "t")]
#(future (tracer id (str (trace-indent) (pr-str (cons name args)))))
(let [value (binding [*trace-depth* (inc *trace-depth*)]
(apply f args))]
#(future (tracer id (str (trace-indent) "=> " (pr-str value))))
value)))
(defmacro my-dotrace
"Given a sequence of function identifiers, evaluate the body
expressions in an environment in which the identifiers are bound to
the traced functions. Does not work on inlined functions,
such as clojure.core/+"
[fnames & exprs]
`(binding [~#(interleave fnames
(for [fname fnames]
`(let [f# #(var ~fname)]
(fn [& args#]
(my-trace-fn-call '~fname f# args#)))))]
~#exprs))
(Rebinding trace-fn-call around a regular dotrace apparently doesn't work; my guess is that's because of clojure.* Var calls still being hard-wired by the compiler, but that's a separate matter. The above will work, anyway.)
An alternative would be to use the above my-dotrace macro together with a my-trace-fn-call function not using futures, but modified to call custom replacements for the clojure.contrib.trace functions using the following in place of str:
(defn my-str [& args] (apply (.getRoot #'clojure.core/str) args))
The replacements are straightforward and tedious and I omit them from the answer.