Book Image

Learning ClojureScript

By : W. David Jarvis, Allen Rohner
Book Image

Learning ClojureScript

By: W. David Jarvis, Allen Rohner

Overview of this book

Clojure is an expressive language that makes it possible to easily tackle complex software development challenges. Its bias toward interactive development has made it a powerful tool, enabling high developer productivity. In this book, you will first learn how to construct an interactive development experience for ClojureScript.. You will be guided through ClojureScript language concepts, looking at the basics first, then being introduced to advanced concepts such as functional programming or macro writing. After that, we elaborate on the subject of single page web applications, showcasing how to build a simple one, then covering different possible enhancements. We move on to study more advanced ClojureScript concepts, where you will be shown how to address some complex algorithmic cases. Finally, you'll learn about optional type-checking for your programs, how you can write portable code, test it, and put the advanced compilation mode of the Google Closure Compiler to good use.
Table of Contents (15 chapters)
Learning ClojureScript
Credits
Foreword
About the Authors
About the Reviewer
www.PacktPub.com
Preface

Getting familiar with the ClojureScript ecosystem


At the heart of the ClojureScript's ecosystem lies the compiler. In this section, we'll gain an insight into its internals: what is its underlying architecture, how does it work, and how can its functioning be tweaked in order to allow for leaner ClojureScript development?

Inside the ClojureScript compiler

The ClojureScript compiler is a piece of Clojure software packaged as a JAR along with Clojure itself, so the package is self-contained and can be manipulated easily. As such, the ClojureScript compiler requires the JVM for its operation. Currently, as ClojureScript developers have baked in the compiler, among many other things, an integration mode for Nashorn, the Java 8 embedded JavaScript engine, they recommend using the same version of the JVM. But, Java 7 is sufficient for the sole operation of the compiler.

Note

A bootstrapped version of ClojureScript, that is, one that uses pure ClojureScript for compilation, has recently been released. Cljs-bootstrap ( https://github.com/swannodette/cljs-bootstrap ), at the time of writing this, is still a work in progress, and offers worse performance than the JVM mainstream compiler. Besides, bootstrapped ClojureScript does not allow for the advanced compilation flags that its JVM counterpart offers.

At its most stripped down definition, the compiler accepts ClojureScript code, that is, mainly s-expressions obeying some Clojure subset semantics, and emits JavaScript artifacts that are passed on to the Google Closure Library ( https://developers.google.com/closure/ ) in order to get it "polished".

The Google Closure Library is a set of JavaScript optimizing tools open sourced by Google, which it uses to support the development of its JavaScript-rich applications, such as Gmail or Maps. Using this library has the following benefits:

  • It abstracts away the effort of managing inconsistencies across the many JavaScript engines of the market.

  • It takes advantage of the Google Closure's complete program optimization with features such as JavaScript minification or dead code elimination.

  • It exposes the namespace functionality, which is otherwise unavailable in Vanilla JavaScript. Actually, a ClojureScript namespace maps to a Google Closure namespace.

Now, let's see the ClojureScript compiler in action. To understand its fundamentals, we won't use any build-automation tooling such as Leiningen for the moment, though we'll for sure need it to construct our tooling later on. Let's begin by downloading the latest release of the compiler (1.7.48 as of the time of writing):

Note

Throughout this book, we assume that you are working on a POSIX compatible system, such as Linux or Mac OS X.

Create a new directory for your first project, label it cljs_first_project, and then download into it the compiler JAR:

mkdir cljs_first_project; cd cljs_first_project
wget \
https://github.com/clojure/clojurescript/releases/download/r1.7.48/cljs.jar

Once the compiler JAR is downloaded, you'll need to create a source directory and the path for your first namespace inside the project folder:

mkdir -p src/cljs_first_project; cd src/cljs_first_project

Now it's time to write your first ClojureScript namespace, which must conform to the path we're currently in (note that you should replace the underscores with dashes in the directory name). Type the following code in a file named core.cljs:

(ns cljs-first-project.core) 
(js/alert "Hello world!") 

This code just declares a namespace and the only operation that our program does is showing an alert popup with the familiar "Hello World!" greeting.

Now with the ClojureScript compiler being a Clojure library, we must write some Clojure code in order to trigger the building of the ClojureScript code we just wrote. Create a Clojure file at the root of our project (at the same level as the /src directory) and label it build.clj with the following Clojure code in it:

(require 'cljs.build.api) 
(cljs.build.api/build "src" {:output-to "out/main.js"}) 

Building ClojureScript is a matter of requiring the cljs.build.api namespace and then launching the build function that takes two arguments. The first argument is where to look for the ClojureScript source code to build, that is the src directory in our case, and second one is where to output the result JavaScript; out/main.js as far as this example is concerned.

With this helper Clojure program under our belt, we can launch the ClojureScript compilation process. It is about launching the embedded Clojure from the JAR we downloaded and passing to it the build program we just wrote. When we run Clojure this way, we make sure that the ClojureScript facilities are loaded, especially the cljs.build.api namespace. To be able to achieve this, we'll have to add the JAR we downloaded as well as the src directory to the classpath when we invoke Clojure with the help of the java command:

java -cp cljs.jar:src clojure.main ./src/build.clj

After you've run this command, you'll notice that an out directory containing our target main.js file has just been created. In order to launch the output JavaScript artifact, we'll need an HTML page, which when loaded into our browser will greet us with a popup. On our HTML page, we must surely load the generated main.js file, but we must also bootstrap the Google Closure Library by loading the out/goog/base.js script.

Also, note that the main.js file only contains a description of the different namespaces' dependencies managed by the Google Closure Library and no logic of execution. So, we must explicitly set an entry point to our program by telling the Google Closure Library to require a namespace to start with, and that's our cljs_first_project.core namespace (note how the dashes got transformed to underscores in the HTML page). Here's what the HTML page, which we'll store under the greet.html file, at the root folder of your project, looks like:

<html> 
  <body> 
    <script type="text/javascript" src="out/goog/base.js"></script> 
    <script type="text/javascript" src="out/main.js"></script> 
    <script type="text/javascript"> 
      goog.require("cljs_first_project.core"); 
    </script> 
  </body> 
</html> 

Accessing this page from your browser greets you with a JavaScript alert popup. Congratulations! You've successfully written and compiled your first ClojureScript program!

There are more advanced ways to work with the architecture of the build process. For example, to get rid of all the goog requires in your HTML page, you can tell the compiler in your Clojure build program which namespace should be set as an entry point, as follows:

(require 'cljs.build.api) 
(cljs.build.api/build "src" 
  {:main 'cljs-first-project.core 
    :output-to "out/main.js"}) 

This lets you strip the necessary script declarations in your greet.html page down to the following:

<html> 
  <body> 
    <script type="text/javascript" src="out/main.js"></script> 
  </body> 
</html> 

Another way to optimize the build process is to set the auto-build of your ClojureScript code on. The compiler can be triggered to be on the watch mode, thus recompiling the output JavaScript as soon as it observes any changes in the src directory. Set your Clojure build program to use the watch function instead of build, as shown here:

(require 'cljs.build.api) 
 
(cljs.build.api/watch "src" 
  {:main 'first-cljs-project.core 
    :output-to "out/main.js"}) 

We've taken quite a deep dive inside the compiler. But, to be able to keep the promise of bringing agile development to JavaScript land, ClojureScript ought to offer a REPL to its users, as any decent lisp would do. Let's discover how ClojureScript addresses this matter in the next section.

Working with the ClojureScript REPL

ClojureScript comes bundled with REPL support for the browser, Node.js, Rhino, and Nashorn. The REPL functionality can be triggered through a call to the repl function from the cljs.repl namespace present in the ClojureScript JAR. Just as we did for the building process, we must create a REPL launching Clojure program. In this program, we begin by building our project and then launch the interactive REPL session. Create a repl.clj Clojure program containing the following listing:

(require 'cljs.repl) 
(require 'cljs.build.api) 
(require 'cljs.repl.browser) 
 
(cljs.build.api/build "src" 
  {:main 'cljs-first-project.core 
    :output-to "out/main.js" 
    :verbose true}) 
 
(cljs.repl/repl (cljs.repl.browser/repl-env) 
  :watch "src" 
  :output-dir "out") 

Here, we'll build a REPL with evaluation on the browser, as we've used the cljs.repl.browser namespace. Note how we set the :watch option, so our REPL automatically gets fresh versions of the output JavaScript, providing for interactive ClojureScript code evaluation. The :output-dir directive tells the REPL where to look for generated artifacts so that they can be loaded into the relevant evaluation environment. As the interactive evaluation session goes, output of the compilation goes into out/watch.log, so we can follow along what's going on while the code interacts with the REPL.

Now, you must set a connection to the REPL inside your ClojureScript program, core.cljs. Once built, the resulting JavaScript program will stay in tune with the REPL environment, by pushing to the browser any changes made to the ClojureScript source:

(ns cljs-first-project.core 
  (:require [clojure.browser.repl :as repl])) 
 
(defonce conn 
  (repl/connect "http://localhost:9000/repl")) 
 
(js/alert "Hello world!") 

The connection has been defined with the defonce parameter to make sure that the same connection is used across the many builds that will occur while the user interacts with the REPL and triggers a new build per evaluation.

Now, launch the REPL, preferably using the rlwrap command, so the display on the terminal is properly rendered:

rlwrap java -cp cljs.jar:src clojure.main repl.clj

Be patient while the first build, involving the construction of the connection to the REPL, is completed. When it completes, you'll see the Waiting for browser to connect message in your terminal. Once you see this message, point your browser to the HTML page we prepared before (greet.html) now through http://localhost:9000/greet.html. Accept the first greeting popup and go back to your terminal; you'll see the following output:

Watch compilation log available at: out/watch.log 
To quit, type: :cljs/quit 
cljs.user=>  

Type another greeting to see if it gets automatically executed in your browser. Type in your REPL the following:

cljs.user=> (js/alert "Hello World From REPL!") 

You'll see new greetings from the REPL interactively popping up without hitting refresh on your browser:

So far, were able to come up with a ClojureScript REPL that empowered us to interact with the browser. But, we are far from having a full-fledged development environment yet; the terminal through which we are coding is quite limited, and we lack several essential features such as code completion, syntax coloring, source code exploration, refactoring, or version control management to name a few. We need a much more complete and fluid coding experience and that's what we will strive to achieve in the next sections. We'll begin by exploring the two most promising facilities that permit text editors or integrated development environments to take advantage from the ClojureScript REPL. Then, we'll showcase two Emacs setups based on those facilities-one based on CIDER and another one backed byinf-clojure.