Clojure Digressions - Setting up a playground
Tags: clojure, beginners, programming
Published: 2023-06-23 12:49:06

[Original post](https://fpsd.codes/clojure-digressions---setting-up-a-playground.html)

# Disclaimer

Why a new series named Clojure Digressions?

Clojure Bites is more suited for short introductions to Clojure
related libraries and tools, instead with digressions I'd like to
talk about more general topics, like development environment setup,
good/bad habits and personal development as a Clojure developer.

Like a conversation ice breaker, I start by talking about my personal
take on some specific argument, looking forward to get input from
other, possibly more experienced, developers on some specific topic.


# Overview

A playground is a safe place aimed at experimentation, without any
legacy background limiting your fantasy and willingness to explore
solutions outside your usual comfort zone i.e. all the code you have
already written for your company/product.

It is no secret that as professional developers we are expected to reuse
as much as possible of what we have at hand in our code base, being careful
when adding new dependencies, limit experimentation to when it is really
really needed to avoid introducing unforeseen costs but at the same time we
love to try out new things! What if we had a safe place to experiment,
just taking out a small bit of our business domain and test it against
a different approach?


## Starting from scratch

You want to quickly sketch out something, no REPL is running, the first
thing you can do is to fire up an interactive REPL by running `$ clojure`
or, if you want readline support, `$ clj`; now you have a prompt where you
con evaluate forms using the rich clojure.core library. Nothing too advanced,
the same is available with the default Python, Ruby and many other prompts.
But it is still something.

Lets spice up our REPL by replacing the standard readline lib with
[rebel](<https://github.com/bhauman/rebel-readline>) that will give us autocompletion,
hints and much more. To try it out quickly:

    clojure -Sdeps "{:deps {com.bhauman/rebel-readline {:mvn/version \"0.1.4\"}}}" -m rebel-readline.main

The \`-Sdeps\` is used to pull in more dependencies, the \`-m\` parameter is used to
specify an entry point to be called; this will initialize a REPL session using
rebel readline interface. Learn more [here](<https://github.com/bhauman/rebel-readline>).

If you'll like it you can create an alias in your \`~/.clojure/deps.edn\` file in
order to be able to start a rebel REPL without being forced to remember all the
required parameters, here is an example:

    {
     :aliases {:rebel {:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
                       :main-opts  ["-m" "rebel-readline.main"]}}
    }

And invoke it simply with:

    clojure -M:rebel

The [asciinema](<https://asciinema.org/a/160597>) of the official docs is worth one
billion words ;)


## Beyond throw away sessions

Working directly in the REPL prompt is not really ergonomic in some cases and you may
prefer the comfort of your favorite IDE or editor to hack on some code; the first
step is to start a REPL to which you can attach to. As with rebel readline it not
required to to have an alias in your $HOME/.clojure/deps.edn or project's deps.edn:

    clojure -Sdeps "{:deps {nrepl/nrepl {:mvn/version \"1.0.0\"}}}" -m nrepl.cmdline
    
    nREPL server started on port 39435 on host localhost - nrepl://localhost:39435

Now you can connect your IDE to the REPL and start hacking! Please notice that the
port may change at each invocation. Deciding what to work on is left as an exercise
to the reader :)

Like we did for rebel readline, we can add an alias to our $HOME/.clojure/deps.edn
file to make it easier in future to start a REPL we can attach to:

    {
      :aliases {:rebel {:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
                        :main-opts  ["-m" "rebel-readline.main"]}
                :nrep  {:extra-deps {nrepl/nrepl {:mvn/version "1.0.0"}}
                        :main-opts  ["-m" "nrepl.cmdline"]}}
    }

Starting it with:

    clojure -M:nrepl

At this point I encourage you to test this setup with your own IDE to get a real feel
of it.


## Some more spices

Being able to run a REPL on demand is already something useful but we can get more
out of it, for example being able to pull in new dependencies on demand in the
currently running process using the upcoming [add-lib]() in the [repl.deps]()
module.

Add a new alias to our clojure deps file:

    {
      :aliases {:rebel {:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
                        :main-opts  ["-m" "rebel-readline.main"]}
                :add-libs {:extra-deps {clojure.org/clojure {:mvn/version "1.12.0-alpha3"}}}
                :nrep  {:extra-deps {nrepl/nrepl {:mvn/version "1.0.0"}}
                        :main-opts  ["-m" "nrepl.cmdline"]}}
    }

Now we can add this capability to either rebel or nrepl alias, for example, running:

    clojure -M:add-libs:rebel

will start a new REPL pulling in both the new Clojure version with support to dynamically
load libraries and a rebel readline prompt; the same would apply if replacing rebel with
nrepl, i.e. we would have a nrepl session with the add-libs support.

It is useful to be able to combine more functionalities coming from different aliases when
starting a new REPL process. Again I encourage everyone to play with it and come with a setup
the covers your requirements.

Other useful tools worth considering for your personal toolbox:

-   [Portal](https://github.com/djblue/portal): A clojure tool to navigate through your data.
-   [FlowStorm](https://github.com/jpmonettas/flow-storm-debugger): A tracing debugger for Clojure and ClojureScript.
-   [ClojureStorm](https://jpmonettas.github.io/flow-storm-debugger/user_guide.html#_clojurestorm): Companion FlowStorm Clojure implementation that automatically instruments your code to be used with FlowStorm.

It can be seen as a safe place to play with unknown possible solutions,
in an isolated environment, collect useful information and then decide
if we can introduce them to our daily stack.

How can it be useful?

Which tools should I include in mine?

Is there something obvious that I am missing?


## Are we playground yet?

We have learned how to setup an interactive or a nREPL to connect to from an
IDE and we have added some useful tools to improve our developer experience
but where do we put the results of our experiments? I usually start a new
project each time I want try out something and honestly it is a straight
forward process especially if using tools like [clj-new](https://github.com/seancorfield/clj-new), taking from the project
README:

    
    # one-off to install clj-new as a tool:
    clojure -Ttools install com.github.seancorfield/clj-new '{:git/tag "v1.2.399"}' :as clj-new
    
    # commands to create new projects:
    
    # create a new app:
    clojure -Tclj-new app :name myname/myapp
    # create a new library:
    clojure -Tclj-new lib :name myname/mylib
    # create a new template:
    clojure -Tclj-new template :name myname/mytemplate
    # create a new project from a public template:
    clojure -Tclj-new create :template electron-app :name myname/myelectron-app#+END_SRC

Recently maybe I have created too many throw away projects and creating a full
fledged project structure each time feels like a waste of time; ideally I'd like
to open a new file, connect to a fully capable REPL (or start a new one) and
start hacking. When finished commit the new file or changes to existing ones and
move one.

One advantage I see with this approach is that I can keep all these small projects
in one place, accumulating knowledge that can be easily reused later. An added
benefit is that it is easy to add another powerful tool like [Clerk](https://github.com/nextjournal/clerk) to generate
playbooks to interact with and document the code; two months from now you may
want to get back to your experiment and easily get back to it and its conclusions
without spending too much time.


# Conclusion

We started from a simple REPL, added some tasty juice to it and ended up creating
a safe place to play and experiment. I want to point out that this is just my personal
take and not a default in the Clojure community (or if that is, it is just a coincidence).

I am curious to learn how others are addressing the same problem, if it is a problem anyway.