The Nextjournal Clojure Environment

Nextjournal's default Clojure environment provides Clojure version . However, it's easy to run Clojure version 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"}}}
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"

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)
Vector(4) [clojure.core.async.impl.channels.ManyToManyChannel, "0xff47526", "clojure.core.async.impl.channels.ManyToManyChannel@ff47526", Map]

1.2. Plotting

1.2.1. Basic: Using Data From Clojure

Generate some random data for plotly and plot it by selecting the plotly viewer via the :nextjournal/viewer metadata attribute.

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

^{:nextjournal/viewer :plotly}
{: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"}}}

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
Vector(16) [Vector(2), Vector(2), Vector(2), Vector(2), Vector(2), Vector(2), Vector(2), Vector(2), Vector(2), Vector(2), Vector(2), Vector(2), Vector(2), Vector(2), Vector(2), Vector(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 {})
Map {:1971: Vector(4), :1969: Vector(3), :1973: Vector(3), :1972: Vector(3), :1970: Vector(3)}

Second, provide the ploty with the :data and :layout information and plot it by setting the {:nextjournal/viewer :plotly} metadata.

(let [final-sort (albums-by-year albums-year {})]
  ^{:nextjournal/viewer :plotly}
  {: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. 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)
exporting environment
Type: Nextjournal
Environment:
Resources:
Environment Variables:
CLOJURE_VERSION1.10.0.442
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

Install tools.deps.alpha to get add-lib to dynamically add libraries.

clojure -Sdeps '{ :deps {
  org.clojure/tools.deps.alpha {:mvn/version "0.6.496"}
}}' -e "(clojure-version)"