I am a newbie to Clojure and currently trying to invoke a simple macro using defmacro and macroexpand.
(ns tutorial.core
(:gen-class)) ; namespace
(defn -main [& args]
(defmacro Simple [] (println "Hello"))
(macroexpand '(Simple))
)
Is there something I have missed? it seems the program runs without any problems but the results are not coming out as expected.
I expect the results to print out as Hello, but no outputs result from this script.
Preface:
Please see this past question for an overview of the best way to write a macro (IMHO).
Answer:
You shouldn't define the macro in the main function. Try this:
(ns demo.core)
(defmacro happy
[]
`(println "I'm happy!")) ; *** notice the backtick! ***
(defn -main [& args]
(println :expanded (macroexpand '(happy)))
(happy)
)
start up a repl:
~/expr/demo > lein repl
demo.core=> (macroexpand '(happy))
;=> (clojure.core/println "I'm happy!")
and we see that it works. Try running from the command line:
~/expr/demo > lein run
:expanded (happy) ; <= ***** OOOPS! *****
I'm happy!
Try changing the single-quote into a syntax-quote (aka backtick), then run:
(defn -main [& args]
(println :expanded (macroexpand `(happy)))
(happy))
~/expr/demo > lein run
:expanded (clojure.core/println I'm happy!)
I'm happy!
The explanation is that the syntax-quote will fully-qualify the Var happy => demo.core/happy (you can see the same effect on the println Var inside of the happy macro itself because of that syntax-quote). This allows the macroexpand to work properly. Compare with a single-quote:
(defn -main [& args]
(println :expanded (macroexpand '(demo.core/happy)))
(happy))
~/expr/demo > lein run
:expanded (clojure.core/println I'm happy!)
I'm happy!
The cause of this behavior is that, in the REPL, we see from the prompt that we are in the demo.core namespace, so happy is resolved as demo.core/happy. When we use lein run, however, observe:
(defn -main [& args]
(println *ns*)
(println (ns-name *ns*)))
with result:
~/expr/demo > lein run
*ns* => #object[clojure.lang.Namespace 0xb625b00 "user"]
(ns-name *ns*) => user
and we see that the *ns* is set to the user namespace and happy cannot be resolved to the Var demo.core/happy unless we fully qualify it either manually or using syntax-quote in the code.
You can find a list of documentation here. Be sure especially to study the Clojure CheatSheet.
For macros, the book Mastering Clojure Macros is also good.
Your macro function doesn't return code, but does the printing immediately. This is very bad style as it has unforeseen consequences.
If you were to use this is a function:
(defn hello [] (Simple))
It prints "Hello" when the function is created. The code being inserted in the function is the result fo println which is nil, thus you have created this:
(defn hello [] nil)
Then if you call hello 3 times none of the calls will no any printing since your macro doesn't produce anything else than nil. If you change your macro to return structure:
;; The quote that makes all the difference
(defmacro Simple [] '(println "Hello"))
Then if will not print anything during the createion of hello, but the expansion would be (println "Hello") and the same fucntion hello would become:
(defn hello [] (println "Hello"))
Related
When I start a repl with lein repl I can run the function greet and it works as expected.
(ns var-test.core
(:gen-class))
(declare ^:dynamic x)
(defn greet []
(binding [x "Hello World."]
(println (load-string "x"))))
(defn -main [& args]
(greet))
But if run the code via lein run it fails with
java.lang.RuntimeException: Unable to resolve symbol: x in this context.
What am I missing?
Is the var x dropped during compilation, despite being declared, since it is never used outside of the string?
Edit:
Solution
#amalloy's comment helped me understand I need to bind *ns* in order load the string within the expected namespace, instead of a new, empty namespace.
This works as expected:
(ns var-test.core
(:gen-class))
(declare ^:dynamic x)
(defn greet []
(binding [x "Hello World."
*ns* (find-ns 'var-test.core)]
(println (load-string "x"))))
(defn -main [& args]
(greet))
Wow, I've never seen that function before!
According to the docs, load-string is meant to read & load forms one-at-a-time from an input string. Observe this code, made from my favorite template project:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require [tupelo.string :as str]))
(dotest
(def y "wilma")
(throws? (eval (quote y)))
(throws? (load-string "y"))
So it appears that load-string starts with a new, empty environment, then reads and evaluates forms one at a time in that new env. Since your x is not in that new environment, it can't be found and you get an error.
Try it another way:
(load-string
(str/quotes->double
"(def ^:dynamic x)
(binding [x 'fred']
(println :bb (load-string 'x'))) " ))
;=> :bb fred
In this case, we give all the code as text to load-string. It reads and eval's first the def, then the binding & nested load-string forms. Everything works as expected since the working environment contains the Var for x.
Some more code illustrates this:
(spy :cc
(load-string
"(def x 5)
x "))
with result
:cc => 5
So the eval produces the var x with value 5, then the reference to x causes the value 5 to be produced.
To my surprise, the partial load-string works in a fresh REPL:
demo.core=> (def x "fred")
#'demo.core/x
demo.core=> (load-string "x")
"fred"
So load-string must be coded to use any pre-existing
REPL environment as the base environment. When using lein run, there is no REPL environment available, so load-string starts with an empty environment.
tmp2/src/data_readers.clj
{ct/G tmp2.core/foo}
tmp2/src/tmp2/core.clj
(ns tmp2.core)
(defn foo
[x]
(println x "Hello, World!"))
(defn -main
[& args]
(println "Hello, World!")
(foo 123)
(#ct/G "abc"))
When "lein run" in the project directory, it show the result "abc Hello, World!", but raise an error.
Caused by: java.lang.RuntimeException: No dispatch macro for: c
at clojure.lang.Util.runtimeException(Util.java:221)
at clojure.lang.LispReader$DispatchReader.invoke(LispReader.java:851)
at clojure.lang.LispReader.read(LispReader.java:285)
at clojure.lang.LispReader.readDelimitedList(LispReader.java:1398)
at clojure.lang.LispReader$ListReader.invoke(LispReader.java:1243)
at clojure.lang.LispReader.read(LispReader.java:285)
at clojure.lang.LispReader.readDelimitedList(LispReader.java:1398)
at clojure.lang.LispReader$ListReader.invoke(LispReader.java:1243)
at clojure.lang.LispReader.read(LispReader.java:285)
at clojure.lang.LispReader.read(LispReader.java:216)
at clojure.lang.Compiler.load(Compiler.java:7630)
Where should I change the code? Any help would be appreciated
in fact, there are two problems here:
1) your foo function returns nil, and it appears that the reader function can't return literal nil. So you have to actually return something from foo:
(defn foo [x]
(println x "Hello, World!")
:something)
or, in case you really need nil in generated code, you can do this:
(defn foo [x]
(println x "Hello, World!")
'(quote nil))
2) the second problem is that you actually try to call the result of your tag application here:
(#ct/G "abc")
the syntax #tag value is evaluated at read-time with (tag-fn value), and then the result is literally put into the source code. when you use the extra pair of parens, that is what happens in your case:
((foo "abc")) and, as the foo function return foo-return-value, it is then left to be (foo-return-value) , and this, in turn, leads to an error.
so,
(defn foo [x]
(println x "Hello, World!")
:nothing)
(defn -main
[& args]
(println "Hello, World!")
(foo 123)
#ct/G "abc")
should probably work
OK, got it working.
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn foo
[x]
(println :foo-reader x)
x)
(dotest
(println :test-foo-plain)
(foo 123)
(println :test-foo-reader)
(println #ct/G "abc")
(println :test-leave))
Note the location and contents of the Data Readers file. It must be at the ROOT of the classpath. In this case that is src/clj:
~/expr/demo > cat src/clj/data_readers.clj
{ct/G tst.demo.core/foo}
and test results:
> lein test
:foo-reader abc ; <====== NOTICE!
-------------------------------
Clojure 1.10.1 Java 14
-------------------------------
:test-foo-plain
:foo-reader 123
:test-foo-reader
abc
:test-leave
Notice that the foo reader for the tagged literal was invoked when the source code file was read, before it was compiled or the test was executed.
Regarding the root of the classpath
This is controlled for Lein projects by these entries in projects.clj:
:source-paths ["src/clj"]
:java-source-paths ["src/java"]
:test-paths ["test/clj"]
If you don't have the clj and java bits (some projects also segment cljc and cljs files into separate directory trees as well), you will need to modify either project.clj or the file location.
I am trying to separate my cli options into a stand-alone namespace for starting up an HTTP server, and I am getting this error-
clojure.lang.ArraySeq cannot be cast to java.lang.CharSequence
In main.clj, this code works fine-
(ns served.main
(:require [org.httpkit.server :refer [run-server]]
[served.app.core :refer [handler]]
[served.server.cli-options :refer [set-options]]
[clojure.tools.cli :refer [parse-opts]])
(:gen-class))
(def cli-options
[
["-p" "--port PORT" "Port number"
:default 5000
:parse-fn #(Integer/parseInt %)
:validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536"]]
])
(defn -main [& args]
(println "Server starting")
(let [options (get (parse-opts args cli-options) :options)]
;;(let [options (set-options args)]
(println (str options))
(run-server handler options)))
It will work with the default options in (def cli-options) and it compiles correctly if I pass in arguments, such as -p 7000.
When I call the main function with the external namespace served.server.cli-options instead of clojure.tools.cli directly (i.e. switch the comment in main), I get the error only when passing in args.
That is, starting the server without arguments, e.g. lein run compiles fine and will print out the defaults. The error comes with lein run -p 7000.
After deleting (def cli-options) in main to avoid any global conflict, here is served.server.cli-options
(ns served.server.cli-options
(:require [clojure.tools.cli :refer [parse-opts]]))
(def cli-options
[
["-p" "--port PORT" "Port number"
:default 5000
:parse-fn #(Integer/parseInt %)
:validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536"]]
])
(defn set-options [& args]
(let [options (get (parse-opts args cli-options) :options)]
(println (str options))
options))
So far as I can tell, I copied the contents to the new namespace correctly. Here are the docs for parse-opts, here is the example that I am drawing from, and a similar but different SO issue here.
My question - how are the CLI args being transformed to throw casting error, and how do I fix it?
Any help would be greatly appreciated.
Delete the & in:
(defn set-options [& args]
& wraps up any additional arguments in a seq. Since you’ve already wrapped the program arguments once in main, you mustn’t do it again in the call to set-options.
I'm trying to understand the -main and its namespace settings. The default namespace in a -main function seems to be "user", but function defined "above" the main function, in the same file, can be called. Are they referred? I was running this to find out:
(ns ack.doodle)
(defn fo [] "bar")
(defn -main [& args]
(println (fo)) ;; bar
(println *ns*) ;; #<Namespace user>
(println (get (ns-refers *ns*) 'sorted-map)) ;; #'clojure.core/sorted-map
(println (get (ns-refers *ns*) 'fo)) ;; nil
(println (get (ns-map *ns*) 'fo)) ;; nil
(println (get (ns-publics *ns*) 'fo)) ;; nil
(println (get (ns-interns *ns*) 'fo)) ;; nil
(println (get (ns-aliases *ns*) 'fo)) ;; nil
(println (ns-resolve *ns* 'fo))) ;; nil
The call (fo) succeeds, yet apparently the symbol 'fo is not known in the current namespace *ns*. What's going on?
This problem hit me when trying to pass the name of some function, along with some arguments, as command line arguments to -main.
steps to reproduce
paste above code in a file ./src/ack/doodle.clj
create ./project.clj with these contents:
(defproject ack "1" :main ack.doodle)
run it:
lein run
It looks like leiningen runs (-main) by calling it from user namespace like that: (ack.doodle/-main). Thus *ns* is bound to the user namespace.
Try running lein repl in your project root. Then run (-main) and see what happens. :)
Didn't I answer this for you last night in #clojure? If there's something unsatisfying about the answer, you'll have to clarify your question.
I would like to print each of my leiningen test methods as they are running. I have a lein tests file that is relatively simple:
(defn myfixture [b]
(do
(println "start")
(b)
(println "end")
)
)
(deftest test1 [] .....
I want to see "test1" print out at the repl when I run the tests. Is there a simple way to print the method name (either by calling a method of b in myfixture, or, in the invocation of "lein test")?
You can get the name of a function like this:
(defn function-name [f]
(:name (meta f)))
(defn my-func []
(println "Hello, world!"))
(let [f my-func]
(function-name f))
;=> my-func
I don't know if the facility that you are looking for exists in the regular clojure.test but midje has some pretty extensive fixture facilities, this link is maybe worth checking out.