

My Clojure Workflow, Reloaded - mark_l_watson
http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded

======
outworlder
> It is dangerously easy, when changing and reloading code at the REPL, to get
> an application into a state which could not have been reached by the code it
> is currently running.

This is the key point people some comments have been missing. Since you can
evaluate code whenever you want, the running instance might become a
frankenstein, containing code and data that shouldn't be around anymore.

I'd like to see the point of view of Smalltalk users (who develop against the
images).

~~~
pjmlp
I only used Smalltalk at the university back in the day, before Java was born
and it was a very nice experience.

All the talks about live editing is nothing more than recovering the workflows
we already had in Smalltalk and Lisp machines.

If you destroy the environment, just restart not a big deal.

If you already saved the environment into the image, just rollback to the
previous image snapshot, not a big deal specially nowadays that image based
source control systems are available, like Monticello and Metacello.

------
mwcampbell
It seems to me that the central idea of this post, having all application
state rooted in one top-level object/structure with no singletons/globals,
with the ability to set up and cleanly tear down multiple instances any number
of times, is applicable well beyond Clojure. Looks like all-around good design
to me. I suppose some might argue, though, that it's all an elaborate work-
around for a slow-starting runtime environment.

~~~
bad_user
When working in Clojure, you tend to experiment a lot with the REPL. And the
environment (Emacs, clojure-mode, nRepl and Lein) feels like having a dynamic
IDE ... so you write code in your text file and with a single short-cut, you
recompile your code and then move over to the nRepl window that you keep open
in Emacs and you invoke whatever you wrote, to see if it works.

So it's like working with Ruby's irb, or with iPython, except that the
integration with Emacs is so freaking nice. So the whole workflow ends up
being a back and forth between the code buffer and the repl buffer. And it
happens really fast too.

Clojure does have a slow startup, thanks to the JVM. It's not so bad on
capable computers though. But that's not the point. The point is that having
to restart a repl session interrupts your flow.

Note: I only played with Clojure, haven't done anything serious in it. My
favourite is Scala and it would be nice if the Scala repl would do a fast
reload on file changes. I don't need it in Scala though, as in Scala you tend
to rely less on the repl.

------
moonboots
I was experimenting with integrated tools.namespace reloading in elisp as
well, and I found a slightly nicer way to send commands to nrepl:

    
    
      (defun nrepl-reset ()
        (interactive)
        (nrepl-interactive-eval "(user/reset)"))
    
    

The original elisp function:

    
    
      (defun nrepl-reset ()
        (interactive)
        (set-buffer "*nrepl*")
        (goto-char (point-max))
        (insert "(user/reset)")
        (nrepl-return))

~~~
jmw74
Note the former technique will display the output in the minibuffer, the
latter will display it in the repl.

------
moomin
I've been edging towards something similar in my own code. Tomorrow, I'm going
to just rip off this stuff wholesale. Maybe it could make a nice lein plugin.

------
dj-wonk
The linked blog post (above) is a nice write-up and elaboration from Stuart's
Clojure/West talk this year: <http://clojurewest.org/sessions#sierra>

------
tom_b
wow - the workflow mgmt hacking Stuart has done seems much more in-depth and
hacked on than any Clojure I've actually written.

I mainly play with Clojure for little things, but still, . . . I'm impressed.
Heck, I barely use two or three commands out of nrepl.el

------
kleiba
I didn't know that Clojure puts function comments between the function name
and the parameters. I personally find that a bit awkward - wouldn't it make
more sense to have it after the parameters, to allow to simply glance at the
function definition and see its signature, especially when comments extend
over multiple lines?

~~~
Tuna-Fish
Functions can have several different parameter lists and separate
implementations for each one. In practice, this is done by grouping the
parameter list of each version with it's body. Where would you put the
docstring in:

    
    
        (defn foo
            ([x] "bar")
            ([x y] "baz"))
    ?

~~~
kleiba
I'd probably put it after the parameter lists:

    
    
        (defn foo
            ([x] *docstring* "bar")
            ([x y] *docstring* "baz"))
    

because you want to be able to document the different implementations
differently. The semantics would be: If the function body starts with a string
literal, the latter serves as the documentation. If the body consists of any
other code following the string literal, it will be ignored for run-time
processing; if it's the only thing in the body, it also serves as the return
value.

~~~
Tuna-Fish
> because you want to be able to document the different implementations
> differently.

I disagree with this. Since the normal functions cannot dispatch on type⁺,
only arity, the different implementations are typically very related and
should share same documentation. Your proposal would lead to massive
duplication, and reduce the likelihood of people actually documenting the
functions.

⁺ There are two separate mechanisms for providing richer function dispatch,
both allow more granular documentation.

~~~
kleiba
Yeah, I guess you've got a point there.

However, if this only applies to different arities, please also compare to the
following style:

    
    
        (defun function (a b &optional c d e)
          "Documentation string"
          "return value"))
    

I still think that the function signature is more readable when the doc string
comes after the parameters, but perhaps it's just a matter of taste.

The above is obviously not Clojure. But as I commented originally, I just
wasn't aware that this is the way Clojure does it.

~~~
Tuna-Fish
That could be done as: (defn function "doc" [a b & rest] "body")

However, matching by arity at the front really helps make simple functions
clean. I really like the multiple body approach, as in my code there's
typically one "standard" case, and the rest are special cases that just call
the standard one with new params. Having to deal with a rest arg with
potentially multiple possible lengths just doesn't look as clear in practice.

It's all tradeoffs. I can see why you'd like the arglist first -- I have some
Haskell background, and I do miss it's types in Clojure, just for the
documentation they provide.

------
snikeris
Made me think of Racket's custodians:

[http://docs.racket-lang.org/reference/eval-
model.html#%28par...](http://docs.racket-lang.org/reference/eval-
model.html#%28part._custodian-model%29)

------
_pmf_
It's a bit ridiculous[1] that one has to jump through hoops with Clojure to
get the benefits that Java hotswapping provides out-of-the-box (i.e. changing
a method and transparently recompiling and changing to the new implementation
in the running JVM) with even second rate IDEs like Eclipse (which I am
using). Note that this is without any third-party plugins and Oracle's
standard VM.

[1] And by "a bit ridiculous", I mean completely archaic.

~~~
Kaali
Clojure has excellent support for hot swapping functions and data; which in my
experience is far ahead of anything that Java has. The system introduced in
this article is for reloading code but with a consistent state.

For example, if your application includes some state from previous actions.
Then you change a function that acts on that state, but with a different
protocol (e.g. data structure expectations changed in the function). Then your
application would not work, as the old state is invalid for the new function.
With techniques like explained in the article, you can quickly "reboot" your
state to be consistent with your new code by reseeding your system.

~~~
reddit_clone
This is equivalent to restarting the JVM along with your application but only
quicker?

I really don't see a good pain/gain ratio here. Clojure being a lisp you are
already good for reloading functions. Only if you change data structures or
macros you need to reboot. That I would think is a much less frequent
operation for which a JVM restart is acceptable. (You also know that
absolutely everything got reset.)

~~~
craigching
Note it's not just restarting the JVM, it's also restarting your repl as well,
which can be as big a PITA as restarting your JVM.

If restarting your JVM is ok with you, go for it! But I like what Stuart has
presented and find it interesting, I'm all about minimizing interruptions
while I'm working on something and will be trying to adapt his workflow to
mine.

