How to create a REPL for Clojure for which the code is reloadable?
I can create a new project, and get a REPL up and running:
lein new app stack
cd stack
lein repl
(-main)
Doing the above should get you "Hello, World!".
I would like to stay in the REPL, change the code to println "Howdy partner!", and then just (-main) again. Either auto-reloading or (perhaps even better) simple manual reloading (for instance with a command like (r)) would make the environment complete.
It seems with lein I'm already getting into the right namespace (any namespace but the user namespace from which you then have to (in-ns 'some-ns) is the right namespace!). The only unanswered part is code reloading - either manual or auto.
As it happens I previously asked how to do this with boot.
For manual reloading you can use the same trick as posted in the boot answer, which is to have a function that does the reloading for you:
(defn r [] (require 'stack.core :reload))
Once the above function is part of the stack.core namespace, you can call it from the REPL. Pretty simple - the namespace stack.core has a function in it which reloads itself.
Make code changes from the editor, reload with (r), then run again...
There's also the lein-autoreload plugin for automatic reloading.
Related
While my lein new app project runs merrily inside the Light Table, lein uberjar won't work. Curiously, it behaves exactly like a classic Pascal compiler: it can't resolve references ahead of definitions. Another curiosity: yesterday it worked. I am not aware of fooling with anything sensitive.
Google says that the subj symptoms are quite commonplace; I tried whatever helped other people in the same (?) plight, but to no avail. By the way, usually they blame it on software bugs: "get the latest version of Leiningen and Clojure". I've got 2.5.0 and 1.6.
The project (main file) is here: https://github.com/Tyrn/pcc/blob/master/src/pcc/core.clj
As it is, parsed-args can't be resolved inside build-album; if I move the -main function to the top of the file, 'No such var' happens to cli-options inside -main. No amount of fiddling with explicit use of namespaces makes any difference.
Again, inside the Light Table everything runs fine.
Using def inside of a function is not idiomatic, especially if there is no reason to have it as a global variable. Just pass it as a function parameter:
(let [parsed-args (parse-opts ...)]
...
(build-album parsed-args))
If you really need global state, you can use e.g. a promise (alternatively, an atom):
(defonce parsed-args (promise))
...
(deliver parsed-args (parse-opts ...))
However, Clojure files are read from top to bottom, and yes, functions not having access to bindings introduced later in the file is by design. You can use declare to tell the parser what to expect:
(declare ^:dynamic *parsed-args*)
(defn build-album ...)
(def ^:dynamic *parsed-args* ...)
...
(binding [*parsed-args* (parse-opts ...)]
(build-album))
TL;DR: If not necessary, avoid global state; if necessary, minimize it.
I have defined the following in one ns -
There is a services atom, and another function to add to that atom -
(ns ex.first)
(def services (atom []))
(defn add-service
[fns]
(swap! services conj fns))
In my code in another ns I do this -
(ns ex.second ..)
(add-service [fn1 fn2 fn3])
1) I am assuming that when I run my repl, which compiles the code, it should add the vector to the atom. However when I eval the #services it returns [].
2) The above works if I eval the (add-service [fn1 fn2 fn3]) in the repl.
3) I have also tried using converting the add-service fn to a macro. However still I find the #services to be empty.
So will appreciate if someone can help on these -
1) Why doesnt the add-service call add a vector to the atom on code compilation when -
add-service is defined as a fn.
add-service is defined as a macro.
2) How do I make it work :)
It depends on if you have set :aot to :all in project.clj or not.
If :aot is :all then the function call will execute as soon as repl is started, otherwise you will need to load the ex.second namespace (ex: using use). Loading will cause the ns to be compiled and the corresponding class to be loaded in jvm and the function call will get executed.
Also, the function call doesn't happen at the compile time, it happens when the compiled class (representing the namespace) is loaded by jvm.
UPDATED (based on comment):
If you make it a macro, then also you need to consider the aot thing.
If aot is set to compile the namespace then lein will create a jvm, load your code in it, call clojure compiler, which will read the code, execute the macro and compiles the code, at this time this jvm (which is used for compiling your code) will have services atom filled coz of macro execution, but this jvm was only for compilation. lein will then create another jvm for the run command and load the compiled class in that jvm but this jvm won't have the services filled in coz the classes doesn't have any code that fill it in.
If aot is not set then macro will work because the read,macroexpand,compile process will happen in the lein run jvm but only when you cause loading of the ns.
As far as "without loading the ns" is concern, what you can do is put the ex.second in the :aot of project.clj
I am using Clojure 1.4.0 on Windows XP (JVM=1.6.0).
My help.clj script contains obscure snippets of Clojure code that I find useful.
In the Clojure repl I can access it from the current directory with (load-file "help.clj").
Trying to be fancy I write (defn clojure-help [] (load-file "help.clj")). Now a simple (clojure-help) loads it.
Then I tried putting the clojure-help function in a repl startup script I use.
But now calling (clojure-help) results in the following Exception:
CompilerException java.lang.RuntimeException: Unable to resolve symbol: clojure-help in this context
What am I missing?
Thanks for posting the code in the comments above! Without the code it's impossible to know what's actually going on. Making one simple change seems to fix the problem:
(defn my-prompt [] (printf "\n[%s]> " (ns-name *ns*)))
(defn p1 [] (clojure.main/repl :prompt my-prompt))
(defn clojure-help [] (load-file "help.clj"))
(p1)
The change was moving the (p1) call to the end of the script. If the call isn't at the end of the script then Clojure drops into the REPL before evaluating the rest of the functions in the script, and thus they are not available in the REPL. Since clojure-help was defined after the (p1) call in your script it was not being evaluated until after exiting the REPL.
If you're not using the repl to load other scripts you could do something like this:
clojure --init replstartup.clj --repl
Where replstartup.clj would contain your closure-help function.
Alternatively you could just create a batch file, named clojure-repl.bat (or some such thing) that looks like this.
#echo off
java -cp %userprofile%\clojure-1.4.0.jar clojure.main --init repl-startup.clj --repl
#echo on
I'm trying to create a text based Clojure game (inspired by Land of Lisp).
(def *nodes* {:living-room "you are in the living-room. a wizard is snoring loudly on the couch."
:garden "you are in a beautiful garden. there is a well in front of you."
:attic "you are in the attic. there is a giant welding torch in the corner."})
(defn describe-location [location nodes]
(nodes location))
The code is running in the REPL but if I saved the code to a file and trying to run:
(describe-location :attic *nodes*)
I got:
Exception in thread "main" java.lang.IllegalArgumentException: Wrong
number of args (1) passed to: user$describe-location (wizard-game.clj:
0)
What I'm doing wrong?
Here is the file: http://dl.dropbox.com/u/3630641/wizard-game.clj
You have too many parentheses. Instead of (describe-location(:garden *nodes*)), you want (describe-location :garden *nodes*).
Remember that the name of the function goes after the open paren, not before: you were calling (:garden *nodes*) and then calling describe-location on the result, which failed because describe-location wants two arguments, not one.
one potential problem is that the version of the function that is loaded into the repl in the 'user' name space may not be the one you expect, so you may want to (load "wizard-game.clj") into a fresh REPL. though many people are using leiningen for this these days, except for the good number of people using maven directly.
first give your're game a namespace
(ns com.me.myGame
....)
then you can load it into the repl by running
(use 'com.me.myGame)
and call the functions by either their name-space-qualified names
(com.me.myGame/describe-location :attic)
or from the repl switch into that namespace:
(in-ns 'com.me.myGame)
(describe-location :attic)
or you can use leiningen to create your project and name-space automatically.
leiningen is worth it in this case because it just took me longer to write this sentence than to make a project with lein. There are a lot of good tutorials for leiningen.
lein new wizard-game
and then edit src/wizard-game/core.clj. this will let you add dependencies later with out fuss if when the project grows to world-famous-success
I have a problem with the Enclojure REPL and using clojure modules from it. The Load/Change REPL to file/ns works fine with an isolated clojure file, but breaks with a file which has references to another clojure file which I try to use from my project.
Here are the exact steps:
Create a new project.
Create a clojure module foobar.clj (namespace com.acme.foobar)
Define a function which returns a value in foobar.clj:
(ns com.acme.foobar
(:use com.acme.othermodule))
(defn myfunc1 []
"a")
Open a Netbeans IDE REPL
From foobar.clj's context menu select:
Change REPL to file/ns
Load
From REPL call the (myfunc1) function. This works just fine:
com.acme.foobar=> (myfunc1)
"a"
The problems start when when I try to refer to other files from foobar. Here's what I do:
Create a new clojure module othermodule.clj
(ns com.acme.othermodule)
(defn fromothermodule []
"b")
Now try to call this from foobar.clj:
(defn myfunc2 []
(fromothermodule))
From othermodule.clj's context menu I select:
Change REPL to file/ns
Load
To make the REPL realize that there is new module it should be able to run.
I do same things things to foobar.clj which now refers to othermodule.clj, but I get:
CompilerException java.io.FileNotFoundException: Could not locate com/acme/othermodule__init.class or com/acme/othermodule.clj on classpath: (NO_SOURCE_FILE:50)
com.acme.foobar=>
This error message comes from both "Change REPL to file/ns" and "Load"
What am I missing? Should I do some other tricks to make this happen? Even the desperate measure of Run->Clean and Build the main project doesn't help (that would of course make the REPL business pretty painful anyway).
I am using NetBeans 6.7.1 and enclojure-plugin-2009-11-3.nbm.
Got the right solution from Eric Thorsen in the google group:
There are three ways to create the REPL from window-menu. Don't use any of those, instead right
click on the Project and "Start Project REPL". Now the paths are set up accordingly.
My first recommendation is to move from NetBeans/Enclojure to IDEA/La Clojure. JetBrains recently created an open source version of their IDE, and the Clojure plugin works fine in it. Since discovering this, I ditched NetBeans and Enclojure. I find La Clojure a pleasure to work in, but of course your mileage may vary.
Back from when I did this in NetBeans, I seem to remember Enclojure source code resides in a subdirectory called "lib". I think I solved a similar problem by fiddling with directory prefixes on the name of the file to load. Probably something like "../lib/YourName". I managed this by trial and error, so I can't relate the exact rules and syntax.
Two things that might help:
You can run something like (println (System/getProperty "java.user.dir")) to find out where Clojure thinks it's executing from.
You could start, as I did, with using an absolute path until you find your way to the correct directory name. Something like "/home/carl/NetbeansProjects/MyProject/lib/myfile.clj" .