The Nextjournal Clojure Environment

Default environments for Clojure

Nextjournal's default Clojure environment provides Clojure version 1.10.0-beta8. However, it's easy to run Clojure version 1.9.0 by including it as a dependency in the runtime's deps.edn.

1.
Showcase

1.1.
Package Management

1.1.1.
At Boot via deps.edn

Working with deps.edn is as easy as creating a Code Listing and writing a typical edn configuration map with a top-level key for :deps.

{:deps
 { org.clojure/clojure {:mvn/version "1.10.0-beta8"}}}
deps.edn
Extensible Data Notation

After mounting the Code Listing (here called deps.edn) and restarting the runtime, the dependencies will be available.

(clojure-version)
"1.10.0-beta8"

For details on how to mount deps.edn and restart a runtime, see Clojure Dependencies.

1.1.2.
At Runtime via tools.deps/add-lib

add-lib, part of tools.deps.alpha, dynamically adds libraries to a running REPL session.

The following deps.edn configuration map contains two of the three possible coordinate types: Maven (clojure) and git (tools.deps.alpha); local is the unused third.

{:deps
  { org.clojure/clojure {:mvn/version "1.9.0"}
    org.clojure/tools.deps.alpha {
      :git/url "https://github.com/clojure/tools.deps.alpha.git"
      :sha "f6c080bd0049211021ea59e516d1785b08302515"}}}
deps.edn
Extensible Data Notation

Note the use of Clojure 1.9 in this example rather than the Nextjournal default of 1.10.

(clojure-version)
"1.9.0"

Load tools.deps.alpha and then use add-lib to add core.async on the fly. Note core.async was not included in deps.edn.

(use 'clojure.tools.deps.alpha.repl)
(add-lib 'org.clojure/core.async {:mvn/version "0.4.490"})
true

Require core.async and test.

(require '[clojure.core.async :as async])
(async/timeout 100)
4[clojure.core.async.impl.channels.ManyToManyChannel,"0x56a7ef0c","clojure.core.async.impl.channels.ManyToManyChannel@56a7ef0c",{1}]

1.2.
Plotting

1.2.1.
Basic: Using Data From Clojure

Generate some random data for plotly.

(defn get-coordinates [y] 
	{:x '(0 10 20 30 40) 
	 :y (take 5 (repeatedly #(rand-int y)))
   :type "scatter"
   :text ["one" "two" "three"]})

{:data [(conj (get-coordinates 35) {:name "The Federation"}) 
        (conj (get-coordinates 35) {:name "The Empire"})]
 :layout {:autosize false :width 600 :height 500 
          :xaxis1 {:title "year"} 
          :yaxis1 {:title "revenue"}}}
2{:data[2]:layout{5}}

Use JavaScript interop with ClojureScript to graph the data from Clojure. Roll over to experience the graph's interactive elements.

(.plot js/Nextjournal plotly)

ClojureScript support is currently experimental. A list of features and caveats is available in the ClojureScript template.

1.2.2.
Advanced: Using Data From Datomic

Datomic queries return data that is simple to work with in Clojure and ClojureScript. This section is a brief demonstration.

The following Clojure runtimes transclude a complete Datomic environment and dataset. For a deeper understanding of how to setup Datomic Free with Nextjournal, please read this Datomic article.

1.2.2.1.
Setup

Start the transactor.

/usr/bin/nohup /datomic-free/bin/transactor /datomic-free/config/samples/free-transactor-template.properties &> /datomic-free.log & sleep 1
tail /datomic-free.log

Load a subset of the MusicBrainz dataset. In this case, music-related information from 1968 to 1973.

/datomic-free/bin/datomic restore-db file:/mbrainz-1968-1973 datomic:free://localhost:4334/mbrainz-1968-1973

Require Datomic and get a value of the database from the connection, defined by (d/connect uri). The d/db API returns the latest db value from the specified connection.

(require '[datomic.api :as d])

(def uri "datomic:free://localhost:4334/mbrainz-1968-1973")
(def conn (d/connect uri))

(def db (d/db conn))
user/db
1.2.2.2.
Query

The query, written in Datalog. It is looking for every John Lennon album with a release date from 1968 to 1973.

(def rules
  '[;; Given ?t bound to track entity-ids, binds ?r to the corresponding
    ;; set of album release entity-ids
 [(track-release ?t ?r)
  [?m :medium/tracks ?t]
  [?r :release/media ?m]]])

(def albums-year 
  (into [] (d/q '[:find ?album ?year
 :in $ % ?artist-name
 :where
 [?a :artist/name   ?artist-name]
 [?t :track/artists ?a]
 [?t :track/name    ?title]
 (track-release ?t ?r)
 [?r :release/name  ?album]
 [?r :release/year  ?year]]
     db rules "John Lennon")))

albums-year
16[[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2]]
1.2.2.3.
Plot

First, collect albums by year. This will make them easy to count and display later.

(defn add-album-to-year [m year title]
  (if (contains? m year) 
    (assoc m year (conj (year m) title)) 
    (assoc m year [title])))

(defn albums-by-year [albums-year m]
  (if-let [album (first albums-year)]
    (let [[title year] album
           sorted-albums (add-album-to-year m (keyword (str year)) title)]
	    (albums-by-year (rest albums-year) sorted-albums))
  	  m))

(albums-by-year albums-year {})
5{:1971[4]:1969[3]:1973[3]:1972[3]:1970[3]}

Second, provide the plotting software with the expected :data and :layout information.

(let [final-sort (albums-by-year albums-year {})]
  {:data [{:x (keys final-sort)
           :y (map #(count (second %)) (albums-by-year albums-year {}))
           :type "bar"}]
   :layout {:autosize false :width 600 :height 500
          :type "bar"
          :xaxis1 {:title "Year"} 
          :yaxis1 {:title "# of Albums"}}})
2{:data[1]:layout{6}}

Finally, plot the data using ClojureScript!

(.plot js/Nextjournal albums)

2.
Setup

2.1.
Build Clojure

Now for the Clojure installation. Note that the stable Clojure version is set as an environment variable on the runtime.

Clojure
Clojure (Bash)GraalVM
exporting environment
Type: Nextjournal
Environment:GraalVM
Resources:
Environment Variables:
CLOJURE_VERSION1.9.0.397
Download this environment as a Docker image from:

Install the one missing dependency, rlwrap, as well as git.

apt-get -qq update
apt-get install --no-install-recommends \
  rlwrap
apt-get clean
rm -rf /var/lib/apt/lists/*

Next, download and run the Clojure installer.

wget --progress=bar:force \
  https://download.clojure.org/install/linux-install-${CLOJURE_VERSION}.sh 
/bin/bash linux-install-${CLOJURE_VERSION}.sh
rm ./linux-install-${CLOJURE_VERSION}.sh

Test clojure from the command line.

clojure -e "(clojure-version)"

2.2.
Install Useful Dependencies

Install leiningen.

cd /usr/local/bin
wget --progress=bar:force \
  https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein
chmod +x lein
./lein

Download the latest beta version of Clojure and add-lib to dynamically add libraries.

clojure -Sdeps '{ :deps {
  org.clojure/clojure { :mvn/version "1.10.0-beta8" } 
  org.clojure/tools.deps.alpha {
    :git/url "https://github.com/clojure/tools.deps.alpha.git" 
    :sha "f6c080bd0049211021ea59e516d1785b08302515" }}}' -e "(clojure-version)"
© 2018 Nextjournal GmbH