I'm reading in the API docs that the *file* variable should have a value of "the path of the file being evaluated, as a String". However, this feature seems broken in certain cases.
When I execute a file using lein exec, things work as expected:
$ cat test.clj
(println *file*)
$ lein exec test.clj
/path/to/test.clj
Yet when I run a test that contains a call to (println *file*), NO_SOURCE_PATH is printed instead of the file containing that line.
Why am I seeing this behavior, and how can I reliably access the path and filename of the file being evaluated?
*file* is set to the path of the file being compiled, so after your whole program is compiled it is no longer useful to look at the value of *file* (assuming no use of eval).
In your test.clj example, the println is executed while the file is still being compiled. If the reference to *file* is moved into a test or function, it will only be dereferenced at runtime after the value of *file* is no longer useful.
One option is to write a macro that stores the value of *file* when it is expanded, so that the result can be used later. For example, a file example.clj could have:
(defmacro source-file []
*file*)
(defn foo [x]
(println "Foo was defined in" (source-file) "and called with" x))
Then from the REPL or anywhere, (foo 42) would print:
Foo was defined in /home/chouser/example.clj and called with 42
Note that it doesn't matter which file source-file is defined in, only where it was expanded, that is the file where foo is defined. This works because it's when foo is compiled that source-file is run, and the return value of source-file which is just a string is then included in the compiled version of foo. The string is then of course available every time foo executes.
If this behaviour is surprising, it may help to consider what would have to happen in order for *file* to have a useful value inside every function at runtime. Its value would have to change for every function call and return, a substantial runtime overhead for a rarely-used feature.
Related
I read online that Clojure uses the ASM library to generate JVM Bytecode, I also saw that Clojure has a REPL.
I assume each line of code executed by the REPL is compiled into a Java class using ASM and then that class is loaded to execute the code. If this is the case then each line would cause a new class file to be generated, so I'm not sure how local variables declared on one line could be shared with the lines which follow in the REPL.
Does anyone know how Clojure's REPL works? I tried reading the Clojure source code but I don't know much Clojure.
It's not "each line" that is compiled at a time, but "each form".
In the REPL, you are always in some namespace. You can change the current namespace of a REPL by using in-ns. In each namespace, there is a binding between symbols (loosely, "names") and Vars (loosely, a container that holds an immutable value). The "state" of the namespace is in the bindings of that namespace.
For example, if you evaluate the form (def a 17) in the current namespace, that will create a new (if it does not already exist) binding for the name a that points to a Var that contains the value 17. Now, you could later evaluate the form (+ a 25) in the same namespace. That will get the value of a in the namespace and add that to 25 to return 42.
The above is for symbols that are local to the namespace. These symbols are available to all forms evaluated in that namespace. (They also can be accessed from other namespaces, but I'll leave that out for now).
You might take a look at https://clojure.org/reference/evaluation if you have not already. The article at https://clojure.org/reference/vars might also be helpful.
In Common Lisp it's easy
http://clhs.lisp.se/Body/v_ld_pns.htm
because the special variable is being set at the load time.
However, I don't seem to be able to find how to do it in Clojure. Is there a way to find which file was passed to load-file?
The load-file function eventually reaches Compiler.java#L7395, where it dynamically binds the source name (when it exists) to the variable designated by SOURCE (see Compiler.java#L235), a.k.a. clojure.core/*source-path*.
In /tmp/test.clj:
(print clojure.core/*source-path*)
In the REPL:
user=> (load-file "/tmp/test.clj")
test.cljnil
I've set up small project:
project.clj:
(defproject testing-compilation "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.8.0"]]
;; this is important!
:aot :all)
src/core.clj
(ns testing-compilation.core)
(def x (do
(println "Print during compilation?")
1))
Then when I do lein compile in project directory I'm seeing output from a print:
$ lein compile
Compiling testing-compilation.core
Print during compilation?
My question is: why clojure evaluates top level forms during AOT compilation? Shouldn't they be evaluated at program startup?
For reference, Common Lisp doesn't evaluate forms by default and provides ability to tune this behaviour. Anything similar in Clojure? If nothing, does Clojure documentation explicitly state such behaviour?
UPD: Forms are evaluated at startup as well.
After specifying a main namespace and writing main function that prints Hello, world!, I did this:
$ lein uberjar
Compiling testing-compilation.core
Print during compilation?
Created testing-compilation-0.1.0-SNAPSHOT.jar
Created testing-compilation-0.1.0-SNAPSHOT-standalone.jar
$ java -jar target/testing-compilation-0.1.0-SNAPSHOT-standalone.jar
Print during compilation?
Hello world!
The first part of the AOT process is to find the file containing the main namespace and load it by evaluating every expression from top to bottom.
Some of these expressions will be require expressions that will have the effect of loading other namespaces, which recursively load yet more namespaces.
Other will be defn expressions that will have the effect of firing up the compiler and producing class files. One class file is produced for each function.
Other expressions may do some calculation and then do things that produce class files, so it's important to give them a chance to run. Here's a made up example:
user> (let [precomputed-value (reduce + (range 5))]
(defn funfunfun [x]
(+ x precomputed-value)))
#'user/funfunfun
user> (funfunfun 4)
14
It is possible to design a lisp that would not evaluate top level forms at start or, as you mention, make it optional. In the case of Clojure it was decided to keep a single evaluation strategy across both AOT and "non AOT" loading so programs always run the same regardless of how they are compiled. These are personal design choices made by others so I can't speak to their motivations here.
I have been using Tcl language for 2 months. I have a question: what does [list source file] mean? I understand source and list separately, but I do not understand what it means when they are put together.
That would appear to be using list to do command-script construction. I'd bet that the result of that [list source file] is then used with uplevel or namespace eval. Or possibly even interp eval.
The list command makes lists. It also makes substitution-free commands, so that:
eval [list $a $b]
is effectively identical in behaviour to:
$a $b
In your case, we have source instead of $a and file (which I'd lay strong odds on not being that literal) instead of $b. Why would we do this? Well, it ensures that if the file name has Tcl meta-characters in it (e.g., {) then the created script to source the file in won't have any problems at all when evaluated.
Why wouldn't you just write source file directly? Well, the most likely cases are where you want to source into a context other than the current one; the source command reads the file into a string and then effectively does an immediate eval on that string (well, there's some nuances, but it's surprisingly close to that). In particular:
proc foo {} {
source bar.tcl
}
Will run the contents of bar.tcl inside the procedure body of foo, just as if you'd typed the text in there directly. The variables will be local variables (unless you use global or something like that) and so on. Most people don't write Tcl scripts that like that sort of treatment, frankly; to handle this, and make the code evaluate in a defined context, you'd actually write:
proc foo {} {
# Quoted to defeat the Stack Overflow syntax highlighter only!
uplevel "#0" [list source bar.tcl]
}
I am working on an webapp that relies on a certain data file to be slurped at runtime. Without the datafile present I don't seem to be able to compile. Why is this?
This is in my core.clj
(def my-data (slurp "my-file.txt"))
Then when I try to compile:
$ lein ring war
I get this exception
Exception in thread "main" java.io.FileNotFoundException: my-file.txt (No such file or directory), compiling:(core.clj:24:28)
How can I compile my war? I don't need the file to be slurped or even check for existence at compile time. Thanks in advance!
[UPDATE]
This is not specific to war file packaging or ring, for example:
(ns slurp-test.core
(:gen-class))
(def x (slurp "/tmp/foo.txt"))
(defn -main [& args]
(println x))
Then:
$ lein uberjar
Compiling slurp-test.core
(ns slurp-test.core
Exception in thread "main" java.io.FileNotFoundException: /tmp/foo.txt (No such file or directory), compiling:(core.clj:4:8)
How can I fix this?
Compiling a Clojure source file involves evaluating all top-level forms. This is in fact strictly necessary to support the expected semantics -- most notably, macros couldn't work properly otherwise1.
If you AOT compile your code, top-level forms will be evaluated at compile time, and then again at run time as your compiled code is loaded.
For this reason, it is generally not a good idea to cause side effects in code living at top level. If an app requires initialization, it should be performed by a function (typically -main).
1 A macro is a function living in a Var marked as a macro (with :macro true in the Var's metadata; there's a setMacro method on clojure.lang.Var which adds this entry). Macros must clearly be available to the compiler, so they must be loaded at compile time. Furthermore, in computing an expansion, a macro function may want to call non-macro functions or otherwise make use of the values of arbitrary Vars resulting from evaluating any top-level code occurring before the point where the macro is invoked. Removing these capabilities would cripple the macro facility rather badly.