├── .gitignore ├── repls-title.tex └── repls.org /.gitignore: -------------------------------------------------------------------------------- 1 | repls.tex 2 | *.epub 3 | *.mobi 4 | *.pdf 5 | *.html 6 | _minted-repls -------------------------------------------------------------------------------- /repls-title.tex: -------------------------------------------------------------------------------- 1 | \usepackage{textcomp} 2 | \renewcommand\maketitle{ 3 | 4 | \begin{titlepage} 5 | \begin{center} 6 | {\huge\bfseries The Ultimate Guide to Clojure REPLs\\} 7 | % ---------------------------------------------------------------- 8 | \vspace{1.5cm} 9 | {\Large\bfseries Arne Brasseur}\\[5pt] 10 | arne@lambdaisland.com\\[14pt] 11 | % ---------------------------------------------------------------- 12 | \vspace{2cm} 13 | {Downloaded from Lambda Island} \\[5pt] 14 | \emph{{https://lambdaisland.com}}\\[5pt] 15 | {High-Quality Screencasts on Clojure and ClojureScript} \\[2cm] 16 | {Read it on-line at} 17 | \emph{{https://lambdaisland.com/guides/clojure-repls}}\\[2cm] 18 | {Version 1.0, August 2016}\\ 19 | {\textcopyright Arne Brasseur 2016} 20 | \end{center} 21 | \end{titlepage}% 22 | } 23 | -------------------------------------------------------------------------------- /repls.org: -------------------------------------------------------------------------------- 1 | #+TITLE: The Ultimate Guide To Clojure REPLs 2 | #+AUTHOR: Arne Brasseur 3 | #+EMAIL: arne@lambdaisland.com 4 | #+LaTeX_CLASS: report 5 | #+LaTeX_HEADER: \usepackage[margin=1in]{geometry} 6 | #+LATEX_HEADER: \input{repls-title} 7 | #+LaTeX_HEADER: \renewcommand*\rmdefault{ppl} 8 | #+LaTeX_HEADER: \usepackage{minted} 9 | #+LaTeX_HEADER: \usemintedstyle{emacs} 10 | #+LaTeX_HEADER: \newminted{common-lisp}{fontsize=\footnotesize} 11 | #+LaTeX_HEADER: \DeclareUnicodeCharacter{2588}{\textblock} 12 | 13 | * Export :noexport: 14 | 15 | #+BEGIN_SRC emacs-lisp 16 | (lambdaisland/export-guides) 17 | #+END_SRC 18 | 19 | #+RESULTS: 20 | 21 | * Introduction 22 | 23 | /Version 2016-09-22, 7966 words. Reading time: 50 minutes./ 24 | 25 | Interactive development is at the very heart of Clojure's philosophy. 26 | Exploration, fast feedback, poking at and into a running system, these are the 27 | bread and butter of Clojure programming, and it all starts with a humble REPL. 28 | 29 | #+BEGIN_SRC text 30 | user=> █ 31 | #+END_SRC 32 | 33 | As the Clojure ecosystem evolved, its REPLs evolved as well. We now have 34 | network and socket REPLs, browser connected and bootstrapped REPLs, REPLs in 35 | our editors that power our tools. It's easy to lose track of all the options 36 | available. What are they for? How do you use them? 37 | 38 | While writing this guide I kept remembering the first time I went to Berlin's 39 | Clojure Dojo. We didn't have many presentations back then, instead people just 40 | chose a topic and hacked around. This first time I was there the topic was Om. 41 | I was eager to get started, but first I wanted to have my trusty Emacs set up. 42 | I had heard about browser connected REPLs, surely I should be able to set one 43 | up to help me along the way. 44 | 45 | I spent the whole meetup poring over the READMEs of Austin and Piggieback, not 46 | making sense of any it. CIDER was still called nREPL.el, which just added to 47 | the confusion. I went home with a broken setup and a broken heart. 48 | 49 | Things have come a long way since then. So much love and hard work has gone 50 | into the tooling surrounding Clojure it makes me giddy. Unfortunately there is 51 | still a lot of confusion as well, and documentation isn't always 52 | comprehensive. This guide seeks to remedy that. It will give you a map of the 53 | territory, with clear instructions to help you get where you want to be. I 54 | hope you'll enjoy the trip. 55 | 56 | ** What's a REPL? 57 | 58 | A REPL is an interactive prompt, a program which allows you to type in some 59 | code, and shows you the result of evaluating this code. You are probably 60 | already familiar with several REPLs, such as the browser console, which 61 | evaluates JavaScript, or your Terminal (Console, Command Prompt, Shell), 62 | which evaluates shell code. 63 | 64 | Dynamic programming languages all come with REPLs nowadays, like ~irb~ for 65 | Ruby, or ~iex~ for Elixir, and LISP languages in particular have a long 66 | history of using REPLs for interactive development. 67 | 68 | ** What is it good for? 69 | 70 | REPLs are important because they give you instant feedback. You don't have to 71 | save, compile, then run. Just type the code and press enter. This encourages 72 | experimentation. It's the "let's poke at it with a stick" of programming, a 73 | way of instantly validating, or adjusting, your concept of reality. 74 | 75 | Clojure is especially suitable for this because the representation of its 76 | values is in turn valid Clojure code, so the return value of a function can 77 | be easily read and interpreted. Instead of opaque impenetrable objects we 78 | have simple maps, vectors, sets, with their contents clearly showing. 79 | 80 | ** How does a REPL work? 81 | 82 | REPL stands for Read-Eval-Print-Loop, this bit of Clojure pseudocode 83 | demonstrates how a REPL works. 84 | 85 | #+BEGIN_SRC clojure 86 | (loop [input (read-line-from-stdin)] 87 | (-> input 88 | eval-str 89 | print) 90 | (recur (read-line-from-stdin))) 91 | #+END_SRC 92 | 93 | First some code is READ from the input stream, then that code is EVALuated, 94 | and finally the result is PRINTed on the output stream, before LOOPing back 95 | and starting over again. 96 | 97 | ** Contributing to This Guide 98 | 99 | The source for this book is on Github: [[https://github.com/lambdaisland/lambdaisland-guides][lambdaisland/lambdaisland-guides]]. If 100 | you find a typo, find parts that could be improved or extended, or if you 101 | want to contribute new sections, then please open a pull request. 102 | 103 | This book is written in org-mode format, so it's easiest to edit it with 104 | Emacs and org-mode, although it's just plain text so any editor will do. For 105 | small fixes you can even make the edits directly on Github. 106 | 107 | ** Changelog 108 | 109 | - *2017-02-21* 110 | 111 | Added a section about [[Lumo][Lumo]] and [[Calvin][Calvin]], expanded the section on [[Planck][Planck]]. 112 | 113 | 7966 words. 114 | 115 | - *2016-08-20* 116 | 117 | Added a stub about [[ClojureScript clients for nREPL][ClojureScript clients for nREPL]] 118 | 119 | - *2016-08-19* 120 | 121 | Code fix by [[https://github.com/visibletrap][Nuttanart Pornprasitsakul (visibletrap)]] [[https://github.com/lambdaisland/lambdaisland-guides/pull/1][(pull request)]] 122 | 123 | - *2016-08-17* 124 | 125 | Added [[Monroe][Monroe]], started the sections on [[inf-clojure][inf-clojure]] and [[CIDER][CIDER]]. Small fixes 126 | and cleanup. 127 | 128 | 7158 words. 129 | 130 | - *2016-08-16* 131 | 132 | First version 133 | 134 | ** Todo 135 | 136 | - Dive into ~cljs.repl/doc~ and friends. These are the equivalents of the 137 | ~clojure.repl~ helpers. Planck makes these available in the ~cljs.user~ 138 | namespace, Figwheel does not. Try out the various ClojureScript repls and 139 | see how to access these helpers. 140 | - Add a section on Dirac / cljs-devtools. 141 | - Add a section on boot-cljs-repl 142 | - Gorilla REPL 143 | - crepl 144 | 145 | * Clojure REPLs 146 | ** Clojure's Built-in REPL 147 | *** Starting the REPL 148 | 149 | A natural starting point is the REPL that comes with Clojure itself. This is 150 | not the REPL you get with ~lein repl~ though! To see Clojure's built-in REPL 151 | all we need is the single JAR file which contains Clojure. If you've done any 152 | Clojure before you probably already have it on your system, you can find it 153 | with this command 154 | 155 | #+BEGIN_SRC shell 156 | find ~/.m2 -name clojure-1.*.jar 157 | #+END_SRC 158 | 159 | If that doesn't yield anything, then download Clojure from [[http://clojure.org/community/downloads][the official 160 | download page]], extract the zip file, and you should find the JAR in there. 161 | 162 | Now run this command in your terminal, and you should find yourself looking 163 | at a beautiful Clojure REPL. 164 | 165 | #+BEGIN_SRC shell 166 | java -jar /path/to/clojure-x.y.z.jar 167 | #+END_SRC 168 | 169 | For example 170 | 171 | #+BEGIN_SRC shell 172 | $ java -jar clojure-1.8.0.jar 173 | Clojure 1.8.0 174 | user=> (+ 1 1) 175 | 2 176 | user=> 177 | #+END_SRC 178 | 179 | Starting from Clojure 1.9.0, ~clojure.spec~ jar needs to be added in the classpath: 180 | 181 | #+BEGIN_SRC shell 182 | $ java -cp clojure-1.9.0.jar:spec.alpha-0.1.143.jar clojure.main 183 | Clojure 1.9.0 184 | user=> 185 | #+END_SRC 186 | 187 | If you're curious you can find the implementation in [[https://github.com/clojure/clojure/blob/master/src/clj/clojure/main.clj#L174-L266][clojure.main/repl]]. 188 | 189 | *** REPL Special Features 190 | 191 | When this REPL starts we are in the ~user~ namespace. Besides the usual stuff 192 | from ~clojure.core~ there are a few extra functions available to you in this 193 | namespace. These are all very handy to have around during development, so 194 | make sure you acquaint yourself with them. 195 | 196 | From ~clojure.repl~ 197 | 198 | - ~source~ :: Show the source code of a function. ~(source clojure.main/repl)~. 199 | - ~apropos~ :: Given a string or regular expression, find all functions that match. ~(apropos "some")~ 200 | - ~doc~ :: Show the documentation for a function, macro, or special form. ~(doc map-indexed)~ 201 | - ~dir~ :: Shows a list of functions and other vars in a certain namespace. ~(dir clojure.string)~ 202 | - ~pst~ :: Given an exception, print its message and stacktrace. ~(try (/ 1 0) (catch Exception e (pst e)))~ 203 | - ~find-doc~ :: Searches all docstrings for a string or regex pattern. ~(find-doc "join")~ 204 | 205 | From ~clojure.java.javadoc~ 206 | 207 | - ~javadoc~ :: Opens the documentation for a Java class in your browser. ~(javadoc java.util.regex.Pattern)~ 208 | 209 | From ~clojure.pprint~ 210 | 211 | - ~pprint~ :: Show a "pretty" formatted representation of a value. ~(pprint (mapv #(vec (range % 10)) (range 10)))~ 212 | - ~pp~ :: Pretty print the previous result, same as ~(pprint *1)~ 213 | 214 | There are also a few "magic" variables available 215 | 216 | - ~*1~, ~*2~, ~*3~ :: The result of the last, penultimate, and third to last evaluation 217 | - ~*e~ :: The last uncaught exception 218 | 219 | To exit the REPL, type Ctrl-D. 220 | 221 | *** Line Editing and History Support 222 | 223 | When you try to use the arrow keys in the REPL you might end up with 224 | gibberish ~^[[A~. Clojure's REPL does not come with editing or history 225 | support out of the box. 226 | 227 | Most REPLs rely on the GNU Readline library for these features, and in the 228 | Java world there's JLine which emulates Readline. 229 | 230 | There's a nifty little tool called ~rlwrap~, which can add Readline support 231 | to programs that don't have it themselves. So if you want a Clojure REPL with 232 | full editing and history support, you can start it like this: 233 | 234 | #+BEGIN_SRC shell 235 | rlwrap java -jar clojure-1.8.0.jar 236 | #+END_SRC 237 | 238 | ** Socket REPL 239 | 240 | When a REPL "reads" and "prints", where does it read from, or print to? The 241 | answer is: standard input, and standard output. 242 | 243 | Every process gets a byte stream that it can read from called "standard 244 | input", also called ~STDIN~, ~System.in~ in Java, or ~clojure.core/*in*~ in 245 | Clojure. If you start a program from a terminal, then this input stream 246 | receives your keyboard's keystrokes. 247 | 248 | Each process also starts with two output streams, "standard output" and 249 | "standard error". Standard output (~STDOUT~, ~System.out~, 250 | ~clojure.core/*out*~) is where the REPL prints its results, and so they show 251 | up in your terminal. 252 | 253 | So what if you hook something else up as input and output stream? For 254 | instance, a network socket. Would that still work? 255 | 256 | Turns out it does, and this is what Clojure's Socket REPL can do for you. The 257 | name Socket REPL is actually misleading, because there is nothing 258 | REPL-specific about this feature. Instead it's a general facility that causes 259 | Clojure to start a TCP server, and wait for connections. Whenever a 260 | connection is received, Clojure will connect ~*in*~ and ~*out*~ to the 261 | network connection's input and output streams, and then pass control over to 262 | a chosen function. 263 | 264 | If that function happens to be ~clojure.main/repl~, well, then you've got 265 | yourself a network REPL! 266 | 267 | You tell Clojure to start a socket REPL using the ~clojure.server.repl~ 268 | property, which you can configure like this: 269 | 270 | #+BEGIN_SRC shell 271 | java -cp clojure-1.8.0.jar -Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}" clojure.main 272 | #+END_SRC 273 | 274 | When you run that command you will see a REPL appear, but that's not the 275 | socket REPL we are talking about, it's just the regular REPL started by 276 | ~clojure.main~. To see the socket REPL in action, open a second terminal, and 277 | connect to it with Netcat (alternatively you can use telnet) 278 | 279 | #+BEGIN_SRC shell 280 | nc localhost 5555 281 | #+END_SRC 282 | 283 | You can even add ~rlwrap~ to the mix to get a feature rich REPL experience. 284 | The great thing about this socket REPL is that 285 | 286 | 1. it works over the network, so you can connect to an app running somewhere else 287 | 2. it doesn't require any source code changes, you can have an app that's 288 | already fully compiled and deployed, and give it REPL support just by 289 | adding a command line flag. 290 | 291 | Don't expose this socket REPL to the internet though! Make sure your firewall 292 | blocks outside traffic to this port, then set up an SSH tunnel between the 293 | server and your local machine to connect to it. 294 | 295 | *** Creating Your Own "Socket REPLs" 296 | 297 | We told Clojure to call the ~clojure.core.server/repl~ function each time it 298 | receives a new connection. This is just a small variant of 299 | ~clojure.main/repl~ with some extra initialization. We can also tell Clojure 300 | to use a different function. 301 | 302 | Create a file called ~wrapple.clj~ in the current directory. 303 | 304 | #+BEGIN_SRC clojure 305 | (ns wrapple) 306 | 307 | (defn echo-time [] 308 | (println (.toString (java.util.Date.)))) 309 | #+END_SRC 310 | 311 | Now start a "socket REPL" using this function: 312 | 313 | #+BEGIN_SRC shell 314 | java -cp clojure-1.8.0.jar:. -Dclojure.server.repl="{:port 5555 :accept wrapple/echo-time}" clojure.main 315 | #+END_SRC 316 | 317 | And you've got yourself a network service that echos the time! 318 | 319 | #+BEGIN_SRC shell 320 | $ nc localhost 5555 321 | Thu Jul 07 16:22:17 CEST 2016 322 | #+END_SRC 323 | 324 | We can even make our own REPL loop, like this one, which will turn anything 325 | you give it into uppercase. 326 | 327 | #+BEGIN_SRC clojure 328 | (ns wrapple 329 | (:require [clojure.string :as str])) 330 | 331 | (defn prompt-and-read [] 332 | (print "~> ") 333 | (flush) 334 | (read-line)) 335 | 336 | (defn uprepl [] 337 | (loop [input (prompt-and-read)] 338 | (-> input 339 | str/upper-case 340 | println) 341 | (recur (prompt-and-read)))) 342 | #+END_SRC 343 | 344 | #+BEGIN_SRC shell 345 | java -cp clojure-1.8.0.jar:. -Dclojure.server.repl="{:port 5555 :accept wrapple/uprepl}" clojure.main 346 | #+END_SRC 347 | 348 | #+BEGIN_SRC shell 349 | $ nc localhost 5555 350 | ~> isn't this awesome? 351 | ISN'T THIS AWESOME? 352 | ~> 353 | #+END_SRC 354 | 355 | ** nREPL 356 | 357 | [[https://nrepl.org/][nREPL]] stands for "network REPL", and while this may sound pretty similar to a 358 | "socket REPL", they are completely different animals. 359 | 360 | The REPLs we've seen so far are stream based, they read lines from an input 361 | stream, and write the result (and any output from side effects) to an output 362 | stream. This makes them conceptually simple, but tedious to communicate with 363 | programmatically. 364 | 365 | nREPL seeks to fix this by being message-based, putting program-to-program 366 | first through a client-server architecture, where the client is your editor 367 | or IDE, and the server is the nREPL "REPL". 368 | 369 | The client initiates the interaction by sending a message to the server, and 370 | as a response the server will send one or more messages back to the client. 371 | 372 | The easiest way to start an nREPL server is either through Leiningen (~lein 373 | repl~) or Boot (~boot repl~). In either case the first line of output will 374 | contain a port number that an nREPL client can connect to. 375 | 376 | *** Messages 377 | 378 | A message might look something like this. 379 | 380 | #+BEGIN_SRC clojure 381 | {:id "10" 382 | :op "eval" 383 | :code "(+ 1 1)" 384 | :ns "user"} 385 | #+END_SRC 386 | 387 | nREPL supports a number of "operations", the quintessential being "eval", 388 | which simply evaluates some code. 389 | 390 | Conceptually a message is like a Clojure map, a set of key-value pairs. The 391 | precise keys used depend on the type of message. In the case of ~eval~ it 392 | needs to know the code to evaluate, as well as the namespace to use for 393 | context. 394 | 395 | The response will look something like this. 396 | 397 | #+BEGIN_SRC clojure 398 | {:id "10" 399 | :value "2"} 400 | #+END_SRC 401 | 402 | An editor or IDE could provide a Clojure REPL by implementing Read, Print, 403 | and Loop, and letting nREPL handle the Evaluate part, but it can do much more 404 | than just emulating a traditional REPL. nREPL can power "live" evaluation 405 | (sending forms from an editor buffer directly to a Clojure process), it can 406 | be used to look up documentation, inspect a running program, and much more. 407 | 408 | nREPL provides a range of operations beyond "eval", together they can be used 409 | to offer rich Clojure editing support. Through "nREPL middleware" it is 410 | possible to add support for new operations. 411 | 412 | nREPL supports these operations out of the box 413 | 414 | - ~eval~ :: Evaluate some code, returns the output value 415 | - ~interrupt~ :: Attempt to interrupt the current ~eval~ operation 416 | - ~describe~ :: Returns a list of currently supported operations, as well 417 | version information 418 | - ~load-file~ :: (re-)load a complete source file 419 | 420 | *** Sessions 421 | 422 | nREPL comes with a built-in "session" middleware. This way you can have 423 | multiple REPLs backed by a single nREPL server. The session middleware adds 424 | three operations 425 | 426 | - ~ls-session~ :: list the current sessions 427 | - ~clone~ :: create a new session 428 | - ~close~ :: close a session 429 | 430 | Each message will now carry a session-id. This becomes important when dealing 431 | with the standard in- and output streams, ~*in*~, ~*out*~, and ~*err*~. 432 | 433 | A single ~eval~ operation might cause many lines of output, possibly 434 | asynchronously, so they arrive after the ~eval~ has completed. The session 435 | middleware will intercept this output, and send it back to the client with 436 | the same session-id as the original ~eval~ message. 437 | 438 | Here's an example interaction, evaluating the code 439 | 440 | #+BEGIN_SRC clojure 441 | (dotimes [i 3] 442 | (Thread/sleep 1000) 443 | (println (str "==> " i))) 444 | #+END_SRC 445 | 446 | I'm using the message representation used by CIDER, with ~-->~ indicating a 447 | message to the server, and ~<--~ being a message sent back to the client. 448 | 449 | #+BEGIN_SRC clojure 450 | (--> 451 | ns "user" 452 | op "eval" 453 | session "10299efe-8f84-4b97-814c-21eb6860223b" 454 | code "(dotimes [i 3] (Thread/sleep 1000) (println (str \"==> \" i)))\n" 455 | file "*cider-repl localhost*" 456 | line 53 457 | column 6 458 | id "15" 459 | ) 460 | (<-- 461 | id "15" 462 | out "==> 0\n" 463 | session "10299efe-8f84-4b97-814c-21eb6860223b" 464 | ) 465 | (<-- 466 | id "15" 467 | out "==> 1\n" 468 | session "10299efe-8f84-4b97-814c-21eb6860223b" 469 | ) 470 | (<-- 471 | id "15" 472 | out "==> 2\n" 473 | session "10299efe-8f84-4b97-814c-21eb6860223b" 474 | ) 475 | (<-- 476 | id "15" 477 | ns "user" 478 | session "10299efe-8f84-4b97-814c-21eb6860223b" 479 | value "nil" 480 | ) 481 | (<-- 482 | id "15" 483 | session "10299efe-8f84-4b97-814c-21eb6860223b" 484 | status ("done") 485 | ) 486 | #+END_SRC 487 | 488 | *** Custom Middleware 489 | 490 | Through "nREPL middleware" you can even implement your own operations. Some notable "middlewares" are ~piggieback~, ~cider-nrepl~, and 491 | ~nrepl-refactor~. Here's an example of the "eldoc" operation provided by CIDER-nREPL. 492 | 493 | #+BEGIN_SRC clojure 494 | (--> 495 | op "eldoc" 496 | session "ff822558-e885-49ed-8cf6-b5331bc8553b" 497 | ns "user" 498 | symbol "str" 499 | id "10" 500 | ) 501 | (<-- 502 | docstring "With no args, returns the empty string. With one arg x, returns\n x.toString(). (str nil) returns the empty string. With more than\n one arg, returns the concatenation of the str values of the args." 503 | eldoc (nil 504 | ("x") 505 | ("x" "&" "ys")) 506 | id "10" 507 | name "str" 508 | ns "clojure.core" 509 | session "ff822558-e885-49ed-8cf6-b5331bc8553b" 510 | status ("done") 511 | type "function" 512 | ) 513 | #+END_SRC 514 | 515 | Writing your own middleware isn't hard. Let's make a middleware that adds a 516 | "classpath" operation, which returns the current Java classpath. It's trivial 517 | but perhaps not altogether useless. 518 | 519 | #+BEGIN_SRC clojure 520 | (ns classpath-nrepl.middleware 521 | (:require [nrepl 522 | [middleware :refer [set-descriptor!]] 523 | [transport :as transport]])) 524 | 525 | (defn wrap-classpath [handler] 526 | (fn [{:keys [id op transport] :as request}] 527 | (if (= op "classpath") 528 | (transport/send transport {:id id 529 | :classpath (->> (java.lang.ClassLoader/getSystemClassLoader) 530 | .getURLs 531 | (map str))}) 532 | (handler request)))) 533 | 534 | (set-descriptor! #'wrap-classpath 535 | {:requires #{} 536 | :expects #{} 537 | :handles {"classpath" {:doc "Return the Java classpath"}}}) 538 | #+END_SRC 539 | 540 | At the heart of any nREPL server is the "handler", a function which handles a 541 | single incoming message. A middleware is a function which takes the existing 542 | handler function and "wraps" it, returning a new handler function. 543 | 544 | Our new handler will look at each incoming request, and if the operation is 545 | anything but classpath, it simply calls the old handler. It's basically 546 | saying, "I don't care about this request, you handle it!" 547 | 548 | When it receives a ~classpath~ message however, then it constructs a 549 | response, and sends it back to the client. 550 | 551 | As part of the incoming request the handler received a "transport" object, 552 | which it needs to use to reply to the client. This is how nREPL achieves 553 | asynchronous operation. A handler can send several messages back to the client based on a 554 | single incoming message, possibly much later or from another thread. It only 555 | needs to make sure to use the transport and id of the original message. 556 | 557 | To see this middleware in action, make sure you have ~nrepl~ in your 558 | dependencies, and add some ~:repl-options~ to your Leiningen ~project.clj~ to 559 | insert the middleware into the "middleware stack". 560 | 561 | #+BEGIN_SRC clojure 562 | (defproject middleware-test "0.1.0-SNAPSHOT" 563 | :dependencies [[org.clojure/clojure "1.8.0"] 564 | [nrepl "0.6.0"]] 565 | 566 | :repl-options {:nrepl-middleware [classpath-nrepl.middleware/wrap-classpath]}) 567 | #+END_SRC 568 | 569 | If you're using Boot then you can insert the middleware like this in your ~build.boot~ 570 | 571 | #+BEGIN_SRC clojure 572 | (require 'boot.repl) 573 | 574 | (swap! boot.repl/*default-middleware* 575 | conj 'classpath-nrepl.middleware/wrap-classpath) 576 | #+END_SRC 577 | 578 | If you're starting your own nREPL server directly instead of using 579 | ~lein repl~ or ~boot repl~, then you can pass the middleware as an argument 580 | to the "default handler". 581 | 582 | #+BEGIN_SRC clojure 583 | (require '[nrepl.server :as nrepl]) 584 | (require '[classpath-nrepl.middleware :refer [wrap-classpath]]) 585 | 586 | (nrepl/start-server :handler (nrepl/default-handler #'wrap-classpath)) 587 | #+END_SRC 588 | 589 | Now start a REPL with ~lein repl~, and note the port number that Leiningen 590 | prints in the first line of output, for example ~nREPL server started on port 591 | 41065 on host 127.0.0.1~. Now you can connect to it, either from the same 592 | process, or from a completely different REPL. 593 | 594 | #+BEGIN_SRC clojure 595 | user> (require '[nrepl.core :as repl]) 596 | nil 597 | user> (def conn (repl/connect :port 41065)) 598 | #'user/conn 599 | user> (repl/message (repl/client conn 1000) {:id 1 :op "classpath"}) 600 | ({:classpath ["file:/home/arne/.m2/repository/org/clojure/clojure/1.8.0/clojure-1.8.0.jar" ,,,], :id 1}) 601 | #+END_SRC 602 | 603 | Here's what the interaction looks like: 604 | 605 | #+BEGIN_SRC clojure 606 | (--> 607 | op "classpath" 608 | session "ff822558-e885-49ed-8cf6-b5331bc8553b" 609 | id "1" 610 | ) 611 | (<-- 612 | classpath ("/home/arne/.m2/repository/org/clojure/clojure/1.8.0/clojure-1.8.0.jar" ...) 613 | id "1" 614 | session "ff822558-e885-49ed-8cf6-b5331bc8553b" 615 | status ("done") 616 | ) 617 | #+END_SRC 618 | 619 | **** Notable middleware 620 | 621 | The CIDER-nREPL README has a [[https://github.com/clojure-emacs/cider-nrepl#supplied-nrepl-middleware][list of included middlewares and their supported operations]]. 622 | 623 | Many more operations are added by [[https://github.com/clojure-emacs/refactor-nrepl][refactor-nrepl]]. 624 | 625 | [[https://github.com/nrepl/piggieback][Piggieback]] allows using nREPL with ClojureScript, see the Piggieback section 626 | under ClojureScript REPLs. 627 | 628 | *** Protocol 629 | 630 | Messages between an nREPL client and server are sent over the wire using 631 | "[[https://en.wikipedia.org/wiki/Bencode][BEncode]]" (pronounced B-encode). BEncode was originally developed for use in 632 | BitTorrent, it's a binary format that supports integers, strings, lists, and 633 | dictionaries. 634 | 635 | While BEncode is a binary protocol, not intended for human consumption, it 636 | is kind of readable if you know what you're looking for. 637 | 638 | Take this Clojure Map 639 | 640 | #+BEGIN_SRC clojure 641 | {:id 1 :op "eval" :code "(+ 1 1)\n"} 642 | #+END_SRC 643 | 644 | Here's the BEncoding of it 645 | 646 | #+BEGIN_SRC clojure 647 | "d4:code8:(+ 1 1)\n2:idi1e2:op4:evale" 648 | #+END_SRC 649 | 650 | The way to read this is 651 | 652 | - ~d~ dictionary 653 | - ~4:code~ a 4 byte string: ~"code"~ 654 | - ~8:(+ 1 1)\n~ an 8 byte string: ~"(+ 1 1)\n"~ 655 | - ~2:id~ a 2 byte string: ~"id"~ 656 | - ~i1e~ an integer, ~1~ 657 | - ... 658 | - ~e~ end of dictionary 659 | 660 | *** Clients 661 | 662 | It should be clear by now that communicating with nREPL is best done through 663 | a dedicated client library. The [[https://github.com/nrepl/nrepl][nREPL]] package contains both the nREPL 664 | server and a client implementation that you can use from Clojure. 665 | 666 | - [[https://github.com/clojure-emacs/cider-nrepl][CIDER-nREPL]] contains an nREPL client implemented in Emacs Lisp. If you want to 667 | experiment with nREPL from Emacs you can try this snippet 668 | 669 | #+BEGIN_SRC emacs-lisp 670 | (nrepl-send-request '("op" "classpath") (lambda (&more) ) (car (cider-connections))) 671 | #+END_SRC 672 | 673 | And inspect the ~*nrepl-messages*~ buffer 674 | 675 | - [[https://github.com/technomancy/grenchman][OCaml: grenchman contains an nREPL client]] 676 | - [[https://github.com/sdegutis/LVReplClient][Objective-C: LVReplClient]] 677 | - [[https://github.com/cemerick/nrepl-python-client][Python: python-nrepl-client]] 678 | - [[https://github.com/nullstyle/nrepl][Ruby: experimental client]] 679 | - [[https://www.npmjs.com/package/nrepl-client][Node.js: nrepl-client on npm]] 680 | 681 | The nREPL manual lists [[https://nrepl.org/nrepl/usage/clients.html][editors and tools that support nREPL]]. 682 | 683 | *** ClojureScript clients for nREPL 684 | 685 | /Advanced, you can safely skip this section./ 686 | 687 | /This section is a stub, you can [[Contributing to This Guide][help by expanding it]]./ 688 | 689 | This section is about connecting to an nREPL server from a ClojureScript 690 | client. If instead you want your nREPL server to use a ClojureScript 691 | environment to do evaluation, see the section on [[Piggieback][Piggieback]]. 692 | 693 | By default nREPL communicates over TCP sockets, using Bencode as its data 694 | format. Raw TCP sockets are not available from a browser, but they are 695 | available in Node.js. You can use the [[https://www.npmjs.com/package/nrepl-client][nrepl-client]] package on NPM to connect 696 | from Node.js to an nREPL server. 697 | 698 | The nREPL transport is pluggable, so a more browser friendly alternative 699 | would be to use HTTP over JSON, which is what [[https://github.com/nrepl/drawbridge][Drawbridge]] provides. It is 700 | implemented as a Ring handler, so you need to use it in conjunction with a 701 | Ring server adapter (Jetty, Http-kit, Aleph, ...) 702 | 703 | Drawbridge contains an nREPL client that uses the same transport, but that 704 | one is for Clojure, not ClojureScript. For a ClojureScript client you can 705 | use [[https://github.com/hiredman/drawbridge-cljs][drawbridge-cljs]]. 706 | 707 | 708 | * ClojureScript REPLs 709 | ** ClojureScript Built-in REPLs 710 | 711 | ClojureScript is a variant of the Clojure language, which compiles (or 712 | "transpiles") to JavaScript code. That way you can write Clojure code, but 713 | run it anywhere JavaScript is available. It turns out that's quite a lot of 714 | places. 715 | 716 | When it comes to ClojureScript REPLs the story becomes a bit more involved. 717 | The ClojureScript compiler is written in Clojure, so it lives in the same 718 | environment Clojure lives in: the JVM (Java Virtual Machine). 719 | 720 | To evaluate compiled ClojureScript code, which has now turned into 721 | JavaScript, we need a separate JavaScript environment. 722 | 723 | So a ClojureScript REPL consists of two parts: the first part is written in 724 | Clojure, it handles the REPL UI, and takes care of compiling ClojureScript to 725 | JavaScript. This JavaScript code then gets handed over to the second part, 726 | the JavaScript environment, which evaluates the code and hands back the 727 | result. 728 | 729 | ClojureScript comes bundled with support for three JavaScript environments: 730 | Rhino, Node.js, and the browser. 731 | 732 | *** Rhino 733 | 734 | [[https://en.wikipedia.org/wiki/Rhino_(JavaScript_engine)][Rhino]] is a JavaScript engine written in Java. The project was started by 735 | Netscape in 1997, and is now managed by Mozilla. It comes bundled with Java 736 | (JDK or JRE), so if you have Java you should have Rhino available. 737 | 738 | Assuming your project includes ClojureScript, getting a Rhino-based REPL 739 | going is as easy as 740 | 741 | #+BEGIN_SRC clojure 742 | (require '[cljs.repl :as repl]) 743 | (require '[cljs.repl.rhino :as rhino]) 744 | 745 | (repl/repl (rhino/repl-env)) 746 | #+END_SRC 747 | 748 | Notice how there are clearly two parts, the ~repl/repl~ function takes a 749 | JavaScript environment as its argument. 750 | 751 | Rhino is a good option if you want a quick ClojureScript REPL to experiment 752 | with, but it has its limitations. Since it's not tied to a browser there is 753 | no DOM, and although people are still working on Rhino, don't expect your 754 | favorite HTML5 or ES6 features to be available. 755 | 756 | One fun thing you get with Rhino (a gimmick, really), is Java interop! 757 | That's right, you can use all your favorite Java classes straight from 758 | ClojureScript. 759 | 760 | #+BEGIN_SRC clojure 761 | cljs.user> (def f (js/java.io.File. "/etc/hosts")) 762 | #'cljs.user/f 763 | cljs.user> (.exists f) 764 | true 765 | #+END_SRC 766 | 767 | *** Node.js 768 | 769 | The most popular JavaScript engine outside the browser is without a doubt 770 | Node.js. To get a Node.js-based REPL going you have a couple of options. 771 | There's a [[https://github.com/bodil/cljs-noderepl][leiningen plugin called cljs-noderepl]], and then there is [[Lumo][Lumo]], a 772 | standalone Node.js based REPL which will be covered in the section 773 | on [[Bootstrapped ClojureScript REPLs][Bootstrapped ClojureScript REPLs]]. 774 | 775 | It's easy enough to do it youreslf though. To run your own node-based REPL 776 | simply swap out the Rhino env from the previous section with the Node.js 777 | repl-env. 778 | 779 | #+BEGIN_SRC clojure 780 | (require 'cljs.repl) 781 | (require 'cljs.repl.node) 782 | 783 | (cljs.repl/repl (cljs.repl.node/repl-env)) 784 | #+END_SRC 785 | 786 | Assuming you have a recent enough (>= 0.12.0) Node.js on your system, this 787 | will spin up node in the background and drop into a REPL. There's still no 788 | DOM since we're not targeting a browser, but you should get significantly 789 | better performance, and you have access to the Node APIs, so you can do 790 | things like access the network or work with files. 791 | 792 | If your ClojureScript project targets Node.js, you will want a REPL that has 793 | your project code loaded. The [[https://clojurescript.org/guides/quick-start#running-clojurescript-on-node.js][ClojureScript Quick Start]] page provides more 794 | info. 795 | 796 | *** Browser connected REPL 797 | 798 | ClojureScript also comes with a REPL environment that uses a running web 799 | browser to evaluate expressions typed in to the REPL. This is especially 800 | useful because it allows you to inspect the state of, and interact with, a 801 | running web app. 802 | 803 | Just like with Rhino or Node.js there's a ~repl-env~ function that you can 804 | plug into ~cljs.repl/repl~, this particular ~repl-env~ is defined in 805 | ~cljs.repl.browser~. 806 | 807 | #+BEGIN_SRC clojure 808 | (require 'cljs.repl) 809 | (require 'cljs.repl.browser) 810 | 811 | (cljs.repl/repl (cljs.repl.browser/repl-env)) 812 | #+END_SRC 813 | 814 | This time the REPL won't immediately pop up though, instead it spins up a 815 | web server and waits for the browser app to connect back to it, so somewhere 816 | in the ClojureScript code for your web app you'll have to initiate this 817 | connection. 818 | 819 | #+BEGIN_SRC clojure 820 | (ns repl-test.core 821 | (:require [clojure.browser.repl :as repl])) 822 | 823 | (defonce conn 824 | (repl/connect "http://localhost:9000/repl")) 825 | #+END_SRC 826 | 827 | You'll need a build script (or use something like ~lein-cljsbuild~) to 828 | compile this code, and an ~index.html~ to deliver it to the browser. The 829 | [[https://github.com/clojure/clojurescript/wiki/Quick-Start#browser-repl][relevant section in the ClojureScript Quick Start guide]] goes into greater 830 | depth about how to do this, including stand-alone examples. 831 | 832 | In HTTP interactions are always initiated by the client. To enable the REPL 833 | (the server) to send data back to the browser (the client), it uses a 834 | technique called "long-polling". With this technique the client will 835 | initiate an HTTP request, and wait as long as necessary for the server to 836 | respond. This effectively reverses the role of client and server. 837 | 838 | It's a crude mechanism, but has the benefit that it works across browsers. 839 | Newer ClojureScript REPLs like [[Weasel][Weasel]] and [[Figwheel][Figwheel]] use websockets which are 840 | ideal for this use case, but only supported in more recent browsers. 841 | 842 | The HTTP server that the REPL uses is often not the one serving up your 843 | application, and so it will have a different domain or port. For security 844 | reasons browsers will prevent these two from interacting with each other, 845 | this is called the same-origin policy. To circumvent this restriction 846 | ~cljs.repl.browser~ uses ~CrossPageChannel~, a part of the Google Closure 847 | Library. ~CrossPageChannel~ allows scripts from different origins to 848 | communicate via an ~iframe~. 849 | 850 | ** Piggieback 851 | 852 | If you haven't read the section about [[nREPL][nREPL]] yet you better go and read that 853 | first. 854 | 855 | Piggieback forms the bridge between ClojureScript and nREPL. It's an nREPL 856 | middleware which intercepts "eval" messages, and routes them to a 857 | ClojureScript REPL. Most editors and IDEs for Clojure are based on nREPL, so 858 | you'll need Piggieback to use them with ClojureScript. 859 | 860 | There are two steps to using Piggieback. First make sure the Piggieback 861 | middleware is added to the middleware stack when starting the nREPL server. 862 | Now when you start nREPL Piggieback will be dormant, waiting to be woken up. 863 | 864 | Calling ~(cider.piggieback/cljs-repl (repl-env))~ from the REPL spurs 865 | Piggieback into action. From now on any code that's being evaluated will be 866 | passed to the given ClojureScript REPL env. 867 | 868 | How to add the nREPL middleware depends on how you're starting nREPL. 869 | 870 | In Leiningen the project map takes a ~:repl-options~ key where you can 871 | configure middleware that will be added to the default stack when running 872 | ~lein repl~. 873 | 874 | #+BEGIN_SRC clojure 875 | (defproject cljsrepl "0.1.0" 876 | :dependencies [[org.clojure/clojure "1.8.0"] 877 | [org.clojure/clojurescript "1.9.183"] 878 | [cider/piggieback "0.4.0"]] 879 | :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}) 880 | #+END_SRC 881 | 882 | In Boot ~boot.repl/*default-middleware*~ can be modified to insert 883 | middleware. Here's a sample ~build.boot~. You can also configure this for all 884 | projects at once in ~\~/boot/profile.boot~. 885 | 886 | #+BEGIN_SRC clojure 887 | (require 'boot.repl) 888 | 889 | (swap! boot.repl/*default-dependencies* 890 | concat '[[cider/piggieback "0.4.0"]]) 891 | 892 | (swap! boot.repl/*default-middleware* 893 | conj 'cider.piggieback/wrap-cljs-repl) 894 | #+END_SRC 895 | 896 | If you're starting your own nREPL server then pass the middleware to the 897 | ~default-handler~ 898 | 899 | #+BEGIN_SRC clojure 900 | (require '[nrepl.server :as nrepl] 901 | '[cider.piggieback :as piggieback]) 902 | 903 | (nrepl/start-server :handler (server/default-handler piggieback/wrap-cljs-repl)) 904 | #+END_SRC 905 | 906 | Now start your REPL (whichever way you choose), and evaluate 907 | ~(cider.piggieback/cljs-repl)~, passing in the ClojureScript repl-env you 908 | would like to use. This signals to Piggieback that it should start 909 | intercepting and redirecting "eval" type messages. 910 | 911 | To get your regular Clojure REPL back type ~:cljs/quit~. 912 | 913 | #+BEGIN_SRC clojure 914 | $ boot repl 915 | nREPL server started on port 44543 on host 127.0.0.1 - nrepl://127.0.0.1:44543 916 | REPL-y 0.3.7, nREPL 0.6.0 917 | Clojure 1.7.0 918 | OpenJDK 64-Bit Server VM 1.8.0_91-8u91-b14-3ubuntu1~16.04.1-b14 919 | Exit: Control+D or (exit) or (quit) 920 | Commands: (user/help) 921 | Docs: (doc function-name-here) 922 | (find-doc "part-of-name-here") 923 | Find by Name: (find-name "part-of-name-here") 924 | Source: (source function-name-here) 925 | Javadoc: (javadoc java-object-or-class-here) 926 | Examples from clojuredocs.org: [clojuredocs or cdoc] 927 | (user/clojuredocs name-here) 928 | (user/clojuredocs "ns-here" "name-here") 929 | boot.user=> (require 'cider.piggieback) 930 | nil 931 | boot.user=> (require '[cljs.repl.node]) 932 | nil 933 | boot.user=> (cider.piggieback/cljs-repl (cljs.repl.node/repl-env)) 934 | ClojureScript Node.js REPL server listening on 58146 935 | To quit, type: :cljs/quit 936 | nil 937 | cljs.user=> 938 | 939 | cljs.user=> (+ 1 1) 940 | 2 941 | cljs.user=> :cljs/quit 942 | nil 943 | boot.user=> 944 | #+END_SRC 945 | 946 | ** Austin 947 | 948 | [[https://github.com/cemerick/austin][Austin]] (apparently named after a [[https://en.wikipedia.org/wiki/The_Six_Million_Dollar_Man][sci-fi character from the 70's]]) is an 949 | alternative to the built-in browser REPL, trying to improve upon it in 950 | several ways. Since the project came out some other alternatives have come 951 | onto the scene, notably [[Weasel][Weasel]] and [[Figwheel][Figwheel]], and unless you have specific 952 | reaons to choose Austin for your REPL needs you're probably better off with 953 | something else. Nevertheless Austin introduced some important ideas, which 954 | are worth looking into. 955 | 956 | Browser connected REPLs were introduced as a way to interact with the 957 | application running in the browser. It can however also be useful to have a 958 | independent REPL that still has access to all your project's namespaces, and 959 | that runs in an environment with a DOM, but without booting the app. 960 | 961 | This kind of "project REPL" is good for quickly trying stuff out, playing 962 | around with libraries, or running tests. 963 | 964 | The killer feature of Austin is making it easy to start such a Project REPL. 965 | 966 | Calling ~(cemerick.austin.repls/exec)~ will spin up a PhantomJS (or 967 | compatible) "headless" browser in the background, and connect to that. 968 | PhantomJS uses WebKit, the browser engine used by Safari and (before the 969 | fork) Chrome, so it has a full DOM available, but it's headless, so there is 970 | no browser window. 971 | 972 | Austin can run multiple REPLs in parallel, and will provide specific entry 973 | point URLs for each. This means you don't have to manually add client code to 974 | connect back to the server. 975 | 976 | There's a [[http://www.youtube.com/watch?v=a1Bs0pXIVXc][screencast by Chas Emerick, the Author of Austin and Piggieback]] 977 | that shows off more of its features. Austin still uses the same long-polling 978 | and CrossPageChannel based approach that ~cljs.repl.browser~ uses. 979 | 980 | ** Weasel 981 | 982 | [[https://github.com/tomjakubowski/weasel][Weasel]] was the first to replace the long-polling approach with WebSockets. 983 | While this prevents it from being used in pre-websocket browsers, it does 984 | open up several JavaScript environments which do have websockets, but don't 985 | have a DOM, prohibiting the use of ~CrossPageChannel~'s iframe hack. 986 | 987 | Using Weasel is very similar to using other ClojureScript REPL, it's just 988 | another ~repl-env~ that's made available to you. The Weasel README heavily 989 | hints at using it with Piggieback, but if you don't need nREPL you can use it 990 | with the standard cljs-repl just the same. 991 | 992 | Just like with the built-in browser connected REPL, there's a server and 993 | a client part. 994 | 995 | *Server (Clojure)* 996 | 997 | #+BEGIN_SRC clojure 998 | (cljs.repl/repl (weasel.repl.websocket/repl-env :ip "0.0.0.0" :port 9001)) 999 | #+END_SRC 1000 | 1001 | *Client (ClojureScript)* 1002 | 1003 | #+BEGIN_SRC clojure 1004 | (ns main 1005 | (:require [weasel.repl :as repl])) 1006 | 1007 | (when-not (repl/alive?) 1008 | (repl/connect "ws://localhost:9001")) 1009 | #+END_SRC 1010 | 1011 | ** Figwheel 1012 | 1013 | [[https://github.com/bhauman/lein-figwheel][Figwheel]] is a Leiningen plugin that automatically pushes code changes to the 1014 | browser, so you get instant feedback. Figwheel also ships with its own 1015 | browser connected REPL, making it a great one stop shop for ClojureScript 1016 | development. 1017 | 1018 | Considering the amount of work it takes care of you might think it's 1019 | complicated to set up, but the opposite is true. Figwheel is incredibly easy 1020 | to use, and has really made it easier for people to get an interactive 1021 | ClojureScript environment up and running. 1022 | 1023 | The easiest way to use Figwheel is through its leiningen plugin, just add the 1024 | latest version to your Leiningen's plugin vector 1025 | 1026 | #+BEGIN_SRC clojure 1027 | (defproject ,,, 1028 | :plugins [[lein-figwheel "0.5.4-7"]]) 1029 | #+END_SRC 1030 | 1031 | And start it with ~lein figwheel~. This will do an initial compilation of 1032 | your ClojureScript code, start a browser connected REPL, and it will even 1033 | open the browser for you so you don't have to figure out what URL to connect 1034 | to. 1035 | 1036 | Before you do that you'll need to configure a ClojureScript build though, and 1037 | add ~:figwheel true~ to the build configuration. Check out the [[https://github.com/bhauman/lein-figwheel][Figwheel 1038 | README]] for an example, or if you're starting with a new project use a 1039 | pre-existing template, e.g. ~lein new figwheel my-project~ or ~lein new 1040 | chestnut my-project~. 1041 | 1042 | Using Figwheel with nREPL is a bit more involved, if you need nREPL support, 1043 | e.g. for using Figwheel with CIDER, I can recommend the [[https://github.com/plexus/chestnut][Chestnut]] template. 1044 | For the full low-down check out the [[https://github.com/bhauman/lein-figwheel/wiki/Using-the-Figwheel-REPL-within-NRepl][Figwheel wiki page on using it with nREPL]]. 1045 | 1046 | Figwheel is Piggieback-aware, meaning if you start Figwheel from an nREPL 1047 | based REPL (using the ~figwheel-sidecar~ library), and the piggieback 1048 | middleware is loaded, then Figwheel will automatically do the right thing. 1049 | 1050 | Figwheel uses WebSockets just like [[Weasel][Weasel]]. 1051 | 1052 | ** cljs-nashorn 1053 | 1054 | Java comes bundled not with one, but with two JavaScript environments. We 1055 | already covered the first one, [[Rhino][Rhino]]. The other one is called Nashorn (German 1056 | for "Rhino") and is said to be a faster, more modern implementation. 1057 | 1058 | While ClojureScript comes with Rhino support out of the box, for Nashorn you 1059 | need a third-party implementation. 1060 | 1061 | The [[https://github.com/bodil/cljs-nashorn][cljs-nashorn README]] has all the info you need. You can either use the 1062 | Leiningen plugin, or manually set up the Nashorn environment and plug it into 1063 | ~cljs.repl/repl~. 1064 | 1065 | ** Further Reading 1066 | 1067 | The ClojureScript wiki has several interesting pages about its REPLs 1068 | 1069 | - [[https://github.com/clojure/clojurescript/wiki/Quick-Start][Quick Start]] 1070 | 1071 | A must read for every serius ClojureScript developer. Teaches you how to 1072 | use the compiler and built-in REPLs from the ground up. 1073 | 1074 | - [[https://github.com/clojure/clojurescript/wiki/The-REPL-and-Evaluation-Environments][The REPL and Evaluation Environments]] 1075 | 1076 | Explains the ~IJavaScriptEnv~ Protocol, and has a full example of using 1077 | ~cljs.repl.browser~, as well as explaining several of its implementation 1078 | details. 1079 | 1080 | - [[https://github.com/clojure/clojurescript/wiki/Custom-REPLs][Custom REPLS]] 1081 | 1082 | Explains several things to be aware of when developing ClojureScript REPLs. 1083 | 1084 | * Bootstrapped ClojureScript REPLs 1085 | 1086 | ClojureScript is a compiler (or transpiler, if you will), that turns 1087 | ClojureScript code into JavaScript. This ClojureScript compiler is written in 1088 | Clojure, meaning you still need a Java environment to run it. Only after the 1089 | compilation is done can you forget about Java and just run the target 1090 | JavaScript code on your favorite JavaScript environment. 1091 | 1092 | Clojure and Java have provided a solid foundation for developing 1093 | ClojureScript, much of the Clojure infrastructure could be reused, and the 1094 | Google Closure Compiler, a Java project, has been indispensable for providing 1095 | optimized output. 1096 | 1097 | Still the dependency on a Java environment has some downsides. The startup 1098 | time of the JVM is notorious, and while it's possible to run compiled 1099 | ClojureScript code without a JVM, it's not possible in that case to compile 1100 | new code on the fly. For a long time this prohibited building 1101 | pure-ClojureScript REPLs. 1102 | 1103 | To address these issues, "bootstrapped" ClojureScript was introduced. In the 1104 | context of compilers "being bootstrapped" means that a compiler is able to 1105 | compile its own source. This is also called "self-hosting". If ClojureScript 1106 | compiler can compile itself, than the result is a ClojureScript compiler that 1107 | can run on JavaScript, instead of Java. (remember that Java relates to 1108 | JavaScript like carpet relates to car, we're talking about two very different 1109 | animals here.) 1110 | 1111 | Some key parts of the infrastructure had to be ported to CLJS (or CLJC), but 1112 | now that bootstrapped ClojureScript is a fact this opens up some exciting new 1113 | possibilities. Note that for typical production app, the Java-based 1114 | ClojureScript compiler is still, and probably always will be, the way to go. 1115 | For building ClojureScript REPLs in JavaScript environments however it's a 1116 | perfect choice. 1117 | 1118 | ** Planck 1119 | 1120 | [[http://planck-repl.org/][Planck]] is a ClojureScript REPL that runs in your terminal and is based on 1121 | JavaScriptCore, the JS engine that ships with WebKit. It is available for Mac 1122 | and Linux. 1123 | 1124 | Planck's developer, Mike Fikes, has gone through great lengths to build a 1125 | polished, user-friendly product. This is one of the most "modern" Clojure* 1126 | REPLs, featuring colorized output, pretty printing of results, and 1127 | readline-like line editing and history search based on [[https://github.com/antirez/linenoise][linenoise]]. Planck also 1128 | makes the ~cljs.repl~ helper functions like ~doc~ and ~find-doc~ available, 1129 | which isn't a given in all ClojureScript REPLs. 1130 | 1131 | If you ever wanted to use Clojure to write scripts, but were frustrated by 1132 | how slow it boots up, then Planck is for you. You can even add a "shebang" 1133 | line to make scripts self-contained, and Planck provides a handful of 1134 | namespaces that expose JavaScriptCore APIs to interact with the outside 1135 | world. 1136 | 1137 | To get started [[http://planck-repl.org/setup.html][follow the install instructions]], then simply run ~planck~ and 1138 | try it out! 1139 | 1140 | Planck comes with comprehensive built-in documentation of all supported 1141 | command line flags. For more in-depth info check out [[http://planck-repl.org/guide.html][the Planck User Guide]]. 1142 | 1143 | #+BEGIN_SRC sh 1144 | $ planck -h 1145 | #+END_SRC 1146 | 1147 | Here's an example of a Planck script that uses the Github API to show the 1148 | list of repositories for a given user. 1149 | 1150 | #+BEGIN_SRC clojure 1151 | #!/usr/bin/env planck 1152 | (ns stargazers.main 1153 | (:require [planck.http :as h])) 1154 | 1155 | (defn github-get [path] 1156 | (h/get (str "https://api.github.com" path) 1157 | {:headers {"User-Agent" "Plank 2.0" 1158 | "Accept" "application/vnd.github.v3+json"}})) 1159 | 1160 | (defn get-repos [username] 1161 | (-> (str "/users/" username "/repos") 1162 | github-get 1163 | :body 1164 | js/JSON.parse 1165 | js->clj)) 1166 | 1167 | (defn -main [& args] 1168 | (let [username (first args)] 1169 | (if username 1170 | (doseq [repo (get-repos username)] 1171 | (let [{:strs [name stargazers_count watchers language description]} repo] 1172 | (println (str name " (" language ")\t🟆 : " stargazers_count "\t👓 : " watchers)) 1173 | (when description 1174 | (println description)) 1175 | (println))) 1176 | (println "Usage: stargazers ")))) 1177 | 1178 | (set! *main-cli-fn* -main) 1179 | #+END_SRC 1180 | 1181 | ** Lumo 1182 | 1183 | Lumo has many similarities with Planck. It is a standalone ClojureScript REPL 1184 | suitable for scripting. It is available on Mac, Linux and Windows, so it does 1185 | have a leg up on Planck when it comes to platform support. 1186 | 1187 | Where Planck is based on JavaScriptCore, Lumo is based on Node.js, thus 1188 | holding the promise of integrating and bridging the gap with the NPM 1189 | ecosystem. 1190 | 1191 | It is still early days for Lumo, it lacks the documentation that Planck has, 1192 | and also doesn't yet have any of the "ergonomic" features. Nevertheless it's 1193 | a project to watch. 1194 | 1195 | The easiest way to install Lumo is through npm: 1196 | 1197 | #+BEGIN_SRC sh 1198 | $ npm install -g lumo-cljs 1199 | #+END_SRC 1200 | 1201 | You can have a look at its options with 1202 | 1203 | #+BEGIN_SRC sh 1204 | $ lumo -h 1205 | #+END_SRC 1206 | 1207 | The only other source of information so far is a scant [[https://github.com/anmonteiro/lumo][README in the Lumo 1208 | repository]], and the [[https://anmonteiro.com/2016/11/the-fastest-clojure-repl-in-the-world/][Announcement Blog Post]]. 1209 | 1210 | ** Calvin 1211 | 1212 | [[https://github.com/eginez/calvin][Calvin]] is not a REPL itself, but an invaluable tool when working with Planck 1213 | or Lumo. Both REPLs are able to load ClojureScript namespaces from JARs, but 1214 | you need to manually manage the classpath. 1215 | 1216 | Calvin makes your life easier by parsing the ~project.clj~ file, and figuring 1217 | out the right classpath for the current project, so all the libraries that it 1218 | depends on are available from a ClojureScript REPL. 1219 | 1220 | Calvin is still alpha level software. 1221 | 1222 | ** Replumb 1223 | 1224 | Not a full featured REPL in itself, but still worthy of a mention. Building 1225 | on top of bootstrapped ClojureScript isn't for the faint of heart. There's 1226 | still a lot of infrastructure you need to provide depending on the 1227 | environment you're targeting, and your specific use case. [[https://github.com/Lambda-X/replumb][Replumb]] tries to 1228 | get most of that boilerplate out of the way so you can focus on building cool 1229 | stuff. 1230 | 1231 | ** KLIPSE 1232 | 1233 | [[http://app.klipse.tech/][Klipse]] is a "fiddle" style web-based playground for ClojureScript. Enter some 1234 | ClojureScript, and it will show you the compiled JavaScript, as well as the 1235 | output from running your code. 1236 | 1237 | Klipse is built on [[Replumb][Replumb]]. With the [[https://github.com/viebel/klipse][Klipse plug-in]] you can add make code 1238 | snippets interactive, for example in library documentation or in a blog post. 1239 | 1240 | ** Node.js bootstrapped REPL 1241 | 1242 | We already talked about the [[Node.js][Node.js]] REPL that comes with ClojureScript. That 1243 | one still requires Clojure though, just like the other built-in REPLs. 1244 | 1245 | It's also possible to build a Node.js based REPL with bootstrapped 1246 | ClojureScript, which is what the author of the [[https://www.npmjs.com/package/cljs-repl][cljs-repl NPM package]] did. 1247 | 1248 | The package hasn't been updated since it's first release, and the version of 1249 | ClojureScript it's based on is getting out of date. For most purposes if 1250 | you're on a supported platform you're better of going with [[Planck][Planck]]. cljs-repl 1251 | is still a fun package to play around with though, since it allows you to use 1252 | Node.js' API's from ClojureScript scripts. 1253 | 1254 | * Editor Integration 1255 | 1256 | Editors and IDEs offer various degrees of REPL integration, providing a rich 1257 | and interactive development workflow. 1258 | 1259 | /This section is a stub, you can [[Contributing to This Guide][help by expanding it]]./ 1260 | 1261 | ** Emacs 1262 | *** inf-clojure 1263 | 1264 | Emacs has a built-in mode for communicating with a LISP REPL process, called 1265 | ~inferior-lisp~. It works by spinning up a subprocess, and then sending 1266 | forms to be evaluated to that process's standard input stream. 1267 | 1268 | [[https://github.com/clojure-emacs/inf-clojure][inf-clojure]] follows the same pattern, but is tailored for Clojure REPLs. 1269 | Note that it does not use the nREPL protocol, it simply sends code back and 1270 | forth, and so can't distinguish between a REPL return value, and output 1271 | generated by the code. 1272 | 1273 | *** CIDER 1274 | 1275 | [[https://github.com/clojure-emacs/cider][CIDER]] is the "everything but the kitchen sink" Clojure integration for 1276 | Emacs. It provides an nREPL-based REPL, in-buffer evaluation, but also many 1277 | extra features. It can do "jump-to-symbol" style code navigation, will show 1278 | the signature of the function at point in the minibuffer, has shortcuts for 1279 | consulting docstrings and javadocs, and much more. 1280 | 1281 | Its basic features build upon the standard nREPL functionality, for more 1282 | advanced uses it uses a Leiningen plugins which injects the cider-nrepl 1283 | middleware, providing extra nREPL operations. (See [[Custom Middleware][nREPL: Custom Middleware]]) 1284 | 1285 | *** clj-refactor 1286 | 1287 | [[https://github.com/clojure-emacs/clj-refactor.el][clj-refactor.el on Github]] 1288 | 1289 | Not a REPL but worthy of mention because it's based on nREPL, this Emacs 1290 | extension provides many powerful refactoring tools, "introduce let", 1291 | "extract function", "add project dependency", etc. 1292 | 1293 | Just like CIDER it uses a Leiningen plugin to inject the refactor-nrepl 1294 | middleware. When using ~cider-jack-in~ this plugin will be added 1295 | automatically. If you're not using CIDER or launching your own nREPL server 1296 | then it's your responsibility to make sure this middleware is loaded. 1297 | 1298 | *** Monroe 1299 | 1300 | [[https://github.com/sanel/monroe][Monroe]] is an nREPL client for Emacs that fits into the sweet spot between 1301 | the minimalism of ~inf-clojure~ and the mastodont that is ~CIDER~. 1302 | 1303 | Install it with ~(package-install 'monroe)~ and launch with ~M-x monroe~. It 1304 | will ask you for the location of the nREPL server, which you have to start 1305 | yourself, for instance with ~lein repl~. 1306 | 1307 | Besides a REPL you get some source buffer interaction, like evaluating 1308 | expressions and regions, and looking up documentation. 1309 | 1310 | ** Vim Fireplace 1311 | ** Cursive 1312 | ** NightCode 1313 | ** LightTable 1314 | 1315 | * FAQ :noexport: 1316 | ** Why do/don't I get line editing, history, etc? 1317 | --------------------------------------------------------------------------------