Sometimes I have pretty long REPL sessions where I'm trying to flesh
out some ideas. When I close my instance of Clojure Box (Emacs based)
I lose all the definitions I had worked out over time. Is there any
way to dump namespace(s) to an image? It would be great to be able to
load up some workspace image and pick up where I left off.
Rob
Something similar was discussed recently but didn't come to a solid
conclusion: http://groups.google.com/group/clojure/browse_thread/thread/4efaee2e67a272c6/f24578bfa06e6b9c?lnk=gst&q=printing+and+reading+a+function#f24578bfa06e6b9c
This is kind of a cop-out, but in general my advice would be to work
from a file. Get your Clojure and Emacs set up so that you can compile
your stuff pretty easily and your files are in the namespace-
appropriate folder underneath your classpath. For example, I keep my
Clojure code in ~/Projects/Languages/Clojure and my Emacs config looks
like this:
(setq swank-clojure-extra-classpaths
(cons "/Users/fusion/Projects/Languages/Clojure/classes"
(cons "/Users/fusion/Projects/Languages/Clojure"
(directory-files "~/.clojure" t "\.jar$"))))
(eval-after-load 'clojure-mode '(clojure-slime-config))
(setq swank-clojure-extra-vm-args '("-Dclojure.compile.path=/Users/
fusion/Projects/Languages/Clojure/classes"))
Now if I want to load or compile a Clojure file, it just works.
Next, when I start doodling I make a file in the aforementioned
directory and put my stuff in there and open up a slime session in
another window. C-c C-c sends the current form over Slime to the
running session. Then I do my interactive testing and exploration in
the slime session. Whenever I hit on a form I want to keep, I copy and
paste it over to the file and make it into a function over there. I
might make a function with a dumb name like demo or test and put a
bunch of forms in there, and eventually they get refactored into unit
tests (or not). If I close Emacs and reopen it on a file that doesn't
yet have a namespace and whatnot, I select the stuff I want to
evaluate and do C-c C-r to evaluate the region. It's handy, if less
transparent.
The main advantage to this, apart from keeping the code clean, is that
you avoid the dirty image problem that can happen with Common Lisp or
(I assume) Smalltalk, where the code seems to work but accidentally
depends on cruft in the image that never made it into the source file.
I've had this happen in CL and found it very frustrating. I had a
tumblog which I tried to make faster by saving images and found one
day to my surprise that I couldn't make a fresh image on my server,
which was running a different architecture, because the only reason it
was able to make images on my box was because the first image had some
crap in it that never made it to the source file. Maybe this problem
isn't as prevalent in Smalltalk; maybe the JVM can circumvent this by
being cross-platform, but it's happened more than once in CL. IIRC,
CMU CL for a long time was self-hosting to such a degree it couldn't
be built at all without a running binary of a previous version. That
kind of thing makes porting to new architectures quite difficult.
Just my $0.02,
—
Daniel Lyons
Thanks Daniel, that makes perfect sense, especially about having
random - and forgotten - code in the image. I have a lot of this
during my exploration sessions.
One way of doing this (although you can decide for yourself whether
it's acceptable) is to run clojure inside of the Unix "script"
command.
--
Michael Wood <esio...@gmail.com>
> It seems strange to me that Clojure doesn't support this concept
> natively
Comments are part of the problem. Clojure's reader strips them out
while parsing before compiling the function, so you would lose them
during the first round-trip of read and print. Even a macro doesn't
really solve this problem; the only possible solution is to accept
comments that aren't really comments because the reader sees and
preserves them, or decide that they aren't an important part of the
function definition.
I'm sure there are other hang-ups (closures are probably another) but
this is the first one that comes to mind for me.
—
Daniel Lyons
> The main reason this is an issue for me is during development I
> sometimes find I need another library added to my classpath. Right now
> the only way I know how to modify the classpath in Emacs is to change
> the .emacs file with an add-to-list 'swank-clojure-extra-classpaths
> and reboot. I think my looking for an image solution might be a
> cop-out itself; I need to learn Emacs better so I can figure out how
> to modify the classpath without rebooting.
Unfortunately this is impossible due to the way the classloaders in the
JVM work; you can't modify your classpath at runtime and have it work
consistently.
The solution I've settled on is to have a static classpath; no matter
what the project is, my classpath is always the same:
src/:target/dependency/:target/classes/:test/
When you need to add a new library, instead of putting the jar on the
classpath, just unpack the jar in the target/dependency directory. Then
you can access it without restarting the JVM.
-Phil
> Isn't this why you would use a doc string, and not a comment?
Docstrings aren't the only comments in my code. :)
—
Daniel Lyons
> Doesn't this seem a little crazy though? My day job is Java dev in
> Eclipse and a little IntelliJ and both IDEs allow you to modify
> classpath at any time. It seems like it would be a basic requirement
> for any IDE. Think of how often you do this. If I had to reboot
> Eclipse every time I'd go crazy. Then throw in the lack of image
> support and it becomes a real obstacle.
Oh, sorry; sounds like I was unclear. You don't have to restart Emacs,
just the JVM subprocess.
M-x slime-restart-inferior-lisp
You don't run into this problem with Java dev in Eclipse because Java
doesn't have a REPL. I agree that it sucks though. Unpacking your
dependencies all in one place is a lot nicer anyway; no restarts required.
-Phil
For the record, this is usually termed "dribbling" in the Lisp world.
It's very handy for debugging customer interaction -- just tell them
to turn on dribble, and have them send you the file rather than
copying and pasting:
http://www.franz.com/support/documentation/8.1/doc/introduction.htm#reporting-bugs-1
CLOS (the Common Lisp Object System) has before, around, and after
methods. Advice is similar, and certainly informed the CL standard. So
far as I understand it, advice (in its various historical forms)
provides a way to intercept the call to a function to modify arguments
or otherwise "advise" the function about what to do. The function
continues to do the heavy lifting, but is unaware of the advice.
CLOS around methods can do the same thing, with two differences:
* Advice is -- I think -- intended to be narrower in scope (you don't
put big functionality in advice: you add individual features). Emacs
doesn't follow this, of course :)
* Advice is more modular -- around/before/after methods in CLOS are
run in a defined order, each controlling the invocation of the next
around method; multiple pieces of advice can be toggled on or off.
You'd do things like turn on tracing/logging/profiling using advice.
Interesting reading:
http://p-cos.blogspot.com/2007/12/origin-of-advice.html
> Does Clojure support something like this? I didn't come across it in
> Stuart's book, website, etc. and AOP certainly helps manage complexity
> in Java.
IME AOP in OO languages usually helps because they lack higher-order
functions, first-order functions, and other linguistic features. It's
much easier to define global triggers in Lisps: just have some global
list of function objects that you can call, call a method and allow
other people to define their own arounds, etc.
You can implement something like method combinations in Clojure, but
it's not built in. People have worked on CLOS-style object systems in
Clojure:
http://code.google.com/p/explorersguild/wiki/XGModelAndGF
HTH,
-R
</snip>
> You don't run into this problem with Java dev in Eclipse because Java
> doesn't have a REPL. I agree that it sucks though. Unpacking your
> dependencies all in one place is a lot nicer anyway; no restarts required.
Uhm, that's not exactly true. The classpath is what you give your VM
as initial set of available classes to what it defines internally. The
definition is static, but there are other ways to inject code into the
class lookup mechanism inside the JVM which is where the classloaders
come in. It's perfectly fine to reload any class at runtime, including
the ones in a jar if they are defined in a classloader that you can
control. There are two problems you have to overcome though.
1) If you want to change classes that are already loaded, one strategy
is to hot swap the code (either via JVMTI, that's what you can do when
you're in debug mode in an IDE, or you use JavaRebel (commercial),
which goes a bit further in what you're able to redefine without
killing the JVM, and losing state). Not sure if that's needed for
Clojure code, since I haven't looked much at the bytecode so far, but
it's really handy for Java and Scala for instance).
2) If you really need to add new things to the classpath (e.g. a new
Jar), or you just want to reload a whole bunch of stuff, then you have
to ditch the classloader that has these classes loaded (does not apply
if you're adding new stuff), and add a new classloader that finds the
new dependency at runtime. This is not that hard to do (check
classloader javadoc), but it has quite a few cornercases, so it's not
recommended to try it adhoc. There are systems that implement the
whole system for you. Eclipse itself (the IDE, not the projects in it)
can install plugins at runtime (although it asks you whether you want
to restart), as can Maven (plugins are installed at runtime). The
mechanism that supports this in Eclipse is OSGi (in Maven it's
homegrown - classworlds), which is a nifty specification for dynamic
service management inside the JVM. It can do a lot of things, but what
is most interesting here is that it tracks versioned dependencies of
runtime components. Think Maven or Ivy at runtime, and it's
dynamically loading/unloading code based on what you specify. All of
the OSGi implementations feature a management console (a REPL) where
you can interact with the system at runtime (load and unload
dependencies, it will warn if there are unmet dependencies etc.).
I think making Clojure work well with OSGi is under 'longterm' on the
roadmap, but I don't know what the current status is. I'm definitely
giving it a vote though.
Just to be clear, the classpath you define on the commandline is just
a shortcut to define a classloader that looks up classes inside the
jars and directories you define on the commandline. If you have your
own classloaders (instantiated) you can load your code from a special
directory (that's what the webcontainers (tomcat etc.) do to support
hot redeployment of apps), the web (that's what applets do), a
database (don't know who's doing that), an uncompiled script file
(that's what scripting languages do unless they have precompiled to
class files), or any other storage you can think of. The only
requirement for a classloader is to return a bytearray that is valid
java bytecode according to the spec, and it can only load the code
once (it's cached after that, that's why you have to ditch the
classloader).
(And although the -cp argument supports wildcards since Java 1.6, they
are expanded early and cannot be reevaluated during runtime :( )
More information available at:
http://java.sun.com/javase/6/docs/technotes/tools/windows/classpath.html
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ClassLoader.html
So depending on your approach and what you want to be able to do
(redefine/reload or add), it's a bit of work. The easiest thing is to
unpack your dependencies, but for my taste it's pretty messy.
Cheers,
Daniel
(today seems to be the day of long posts. Sorry guys)