Skip to content

Instantly share code, notes, and snippets.

@rauhs
Last active May 6, 2019 04:47
Show Gist options
  • Save rauhs/c9183e7afa2dca87126e to your computer and use it in GitHub Desktop.
Save rauhs/c9183e7afa2dca87126e to your computer and use it in GitHub Desktop.

This document is my personal writeup of trying to make sense of all the clojure(script) tooling chain. It might contain errors and it actually contains a few questions

Tooling:

Nomenclature/Terminology

REPL: A prompt in its simples form.

  1. Waits for and Reads input,
  2. Evaluates that input,
  3. Prints the result of the evaluation,
  4. Loops, ie Waits for input again.

Middleware: A function.

Will be called with data and transform that data in some kind. Can be composed with other middleware (functions) running before or after. Composition is easy: Middleware is passed a function and will call it.

Clojure REPL

A prompt given from clojure living in a JVM. Only reads writes through STDIN/STDOUT. Integrated into core clojure.

Clojure nREPL:

Same as REPL but with a network protocol as input/output. This allows more flexible use than the unpowerful STDIN/STDOUT of the default REPL. Part of clojure but in a separate tools.nrepl library. Written in clojure and listens on a port. Can take various middleware to change the behaviour of the nREPL. The middleware (can) modify the nREPL packets that it receives (for instance from an editor).

cljs.repl/repl

A Clojure library part of the clojurescript project that provides a REPL but changes the Evaluation part of the REPL. It always has to be started with an environment in which the Javascript is evaluated.

In the following section pay close attention to the confusables cljs.repl.browser vs clojure.browser.repl.

Environment:

The environment evaluates the javascript code that it receives from the REPL prompt. Some environments send the javascript away for evaluation, some do it themselves. For instance, a browser REPL environment will send it to the browser. A rhino environment will create a Rhino javascript virtual machine in the same JVM as the REPL and evaluate it right there.

Environments:

  • cljs.repl.rhino
  • cljs.repl.browser aka brepl
  • cljs.repl.nashhorn
  • cljs.repl.node

Backend:

(Note: This is not called backend in the official docs. I introduce this term to be able to refer to the actual entity that evaluates the javascript code).

Optional. This may be needed depending on your environment. For instance your browser. Backends are usually written in clojurescript (not clojure!). That clojurescript must first be compiled to javascript. This javascript code is then initially loaded and connects to your environment. For instance by loading a browser URL which runs the javascript code as soon as you visit the site. This javascript code then connects the backend (your browser) to the environment (your REPL) and makes the environment functional. After this bootstrapping has happened your environment can send code to your browser (your backend). The backend only understands javascript! The backend connects to your REPL (with the environment) and waits for the REPL to send it commands to evaluate.

Backends:

  • clojure.browser.repl
  • ???

The optional backend (usually your browser) has to communicate with your REPL. Some environments therefore open a port and listen to it. For instance cljs.repl.browser environment starts a HTTP server at port 9000 by default. The backend clojure.browser.repl connects to it after you load the page.

This port is NOT for your IDE (such as Emacs). So far, you still have to type the commands into the STDIN prompt.

The REPL works like this:

First, it compiles the Clojure code it receives by Read into javascript. Then the environment evaluates the javascript. For instance by sending it to the browser.

Q: Does the environment do the Clojure->JS compilation or is that done by cljs.repl/repl for all environments?

Environments are Clojure code. Not clojurescript! They're added to the REPL running in the JVM.

Q: Is this all true?

Example:

https://github.com/clojure/clojurescript/tree/master/samples/repl

IMO this example is confusing: It doesn't explain which code snippet is run where (Browser? Editor? Prompt? JVM?).

cljs.repl.browser/repl aks brepl

A backend, see cljs.repl/repl section.

Piggieback

nREPL Middlware, adds clojurescript support to an nREPL.

This is needed since the default cljs REPL only works with a boring STDIN/OUT prompt. However, for development we want to work with an IDE which talks to an nREPL over the network.

Basically allows you to turn your nREPL into a cljs REPL. This in turn allows for IDE's such as Emacs to send clojurescript forms to the nREPL.

Usually is not used directly but by other tools such as Austin.

(Should eventually disappear. Tapped (taps) into undiscussed details of clojurescript.) Q: Why disappear?

Austin

A bREPL (see above) alternative!

Is a Clojure library that allows starting a CLJS REPL and environments and also their backend that the environment requires. So within your clojure REPL you start a CLJS REPL. It is a nested REPL you can exit out of and get back to your Clojure REPL again.

For instance you can start a headless phantomjs backand and the REPL (with its environment) will automatially connect to it. Or you can start a new Chrome tab (from your REPL running in the JVM) and have the REPL automatically connect to it.

Q: True? Is this the way to go still? Looks like the new templates don't use it.

Weasel

By default the bREPL backend uses clojure.browser.net to communicate with the REPL. (Remember earlier I said the cljs.repl.browser environment starts a HTTP server at port 9000)

The bREPL used to only provide goog.net CrossPageChannel type of communication, which causes problems: See: https://github.com/tomjakubowski/weasel#why

Weasel uses WebSocket for communication between the backend (browser) and the REPL. It seems that cljs natively is (Q: will?) support this (03/2015)?

Tapped (taps) into undiscussed details of clojurescript.

Lein Figwheel

A Leining plugin.

Watches all source files to be saved and thus change contents. It then automatically pushes this source file to the browser. It uses websockets to push the changes.

Requires you to write source files in a way they can be reloaded. (For instance "defonce" your global state)

Can run a hook function to re-setup your app every time this push happens. Again, usually you need to code your app with this in mind.

Not sure exactly where and how this is all hooked in.

Chestnut Leiningen template

Just a leining template ("lein new chestnut my-new-project") that generates a very rich leiningen project.clj.

Fuses together Weasel, Figwheel, piggieback and coordinates them. (reagent-template is very similar)

External tools / Editors

Externals tools like Editors do not communicate directly with your browser. They only know how to talk to an nREPL. The rest if up to what that nREPL can do due to the hooked in middleware.

Cider:

Emacs library written in Elisp.

Communicates with an nREPL!

Cider-nrepl

Clojure library that provides various middleware to enhance the functionality of the less powerful default nREPL.

Not necessarily exclusive to Cider (Emacs). Is used by other editors.

Examples: undef a var, stacktrace anlysis, formatting of data

A typical browser web development setup:

This Example uses Emacs just so that it's a complete example

  1. An Clojure REPL started from the command line. Start an nREPL from that REPL (use Leiningen). nREPL listens on a port with cider-nrepl middleware. Emacs connects to that port and can send commands for evaluation.
  2. Turn the nREPL into a clojurescript REPL with piggieback.
  3. Start Weasel to connect to the browser backend.
  4. Start a browser that connects to the Weasel REPL.

You can now send clojure(script) forms to the nREPL that will turn it into javascript and sends it to the browser over the websocket for evaluation.

The nicer live-reloading is left out since it's not clear what it does exactly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment