Clojure How to deploy a macro in a source file compared to a replica

I will describe macros in Clojure and wonder about the extension of macros. In repl, when I do this:

user=> (defmacro unless [pred ab] `(if (not ~pred) ~a ~b)) #'user/unless user=> (macroexpand-1 '(unless (> 5 3) :foo :bar)) (if (clojure.core/not (> 5 3)) :foo :bar) 

But when I do the same in the clj file:

 (ns scratch-pad.core (:gen-class)) (defmacro unless [pred ab] `(if (not ~pred) ~a ~b)) (defn -main [& args] (prn (macroexpand-1 '(unless (> 5 3) :foo :bar)))) 

and run the code, I get the following:

 $ lein run (unless (> 5 3) :foo :bar) 

How to make code print the same as repl?

+4
source share
1 answer

What's happening

This is due to the way the concept of the current namespace works in Clojure. macroexpand-1 expands its argument in the current namespace.

In REPL, this will be user ; you define a macro in the user namespace, then you call macroexpand-1 in that namespace, and that’s all good.

In the namespace :gen-class'd or even in any other namespace, the current compile-time namespace is the namespace. However, when you later call the code defined in this namespace, the then namespace will be what comes up at that point. It may be a different namespace because it compiles.

Finally, at run time, the current default namespace is user .

To see this, you can move the macro to a separate namespace, which also defines the use-the-macro function and calls this function at the top level; namespace :gen-class ' d then must require or use the macro namespace. Then lein run will print what you expect once (compilation time for the macro namespace), and the unexpanded form twice (when the macro namespace require d over the main namespace, and then when -main calls use-the-macro ).

Decision

Clojure REPL manages the current namespace with binding ; you can do the same:

 (binding [*ns* (the-ns 'scratchpad.core)] (prn (macroexpand-1 ...))) 

You can also use a syntax quote instead of a quote in -main :

 (defn -main [& args] (prn (macroexpand-1 `...))) ^- changed this 

Of course, if characters other than unless were involved, you would need to decide if they should be in the namespace, and possibly prefix them with ~' . This is the point: a syntax quote is good for creating mostly "space-independent" name code (which makes it so great for writing macros, except for convenient syntax).

Another possible β€œfix” (tested on Clojure 1.5.1) adds an in-ns call to -main :

 (defn -main [& args] (in-ns 'scratchpad.core) (prn (macroexpand-1 '...))) ^- no change here this time 

As with binding , you actually get an extension of your original form in your original namespace.

+5
source

Source: https://habr.com/ru/post/1480377/


All Articles