Home Programming Clojure Programming Cookbook

Clojure Programming Cookbook

books-svg-icon Book
eBook $43.99 $29.99
Print $54.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $43.99 $29.99
Print $54.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Live Programming with Clojure
About this book
When it comes to learning and using a new language you need an effective guide to be by your side when things get rough. For Clojure developers, these recipes have everything you need to take on everything this language offers. This book is divided into three high impact sections. The first section gives you an introduction to live programming and best practices. We show you how to interact with your connections by manipulating, transforming, and merging collections. You’ll learn how to work with macros, protocols, multi-methods, and transducers. We’ll also teach you how to work with languages such as Java, and Scala. The next section deals with intermediate-level content and enhances your Clojure skills, here we’ll teach you concurrency programming with Clojure for high performance. We will provide you with advanced best practices, tips on Clojure programming, and show you how to work with Clojure while developing applications. In the final section you will learn how to test, deploy and analyze websocket behavior when your app is deployed in the cloud. Finally, we will take you through DevOps. Developing with Clojure has never been easier with these recipes by your side!
Publication date:
October 2016
Publisher
Packt
Pages
618
ISBN
9781785885037

 

Chapter 1. Live Programming with Clojure

In this chapter, we will cover the following topics:

  • REPL up!

  • Working with primitive data types

  • Using bindings of vars, conditions, loops, and error handling

  • Using and defining functions

  • Using third-party libraries

  • Using namespaces

 

Introduction


Clojure is a blend of Lisp and Java. Clojure allows you to solve what you want quickly and keeps code simple. Once you learn Clojure, it's great fun to use it! Clojure provides the following fantastic features:

  • Clojure is a dialect of Lisp and supports the functional programing style

  • It runs on Java Virtual Machine (JVM) and can use Java's assets seamlessly

  • It also supports immutability and concurrent programming

In this chapter, we will review how to set up a Clojure REPL environment and Clojures' basic structure, including primitive data types, programming flow controls, and functions. Then we will go over how to use third-party libraries and namespaces.

Let's take you to Clojure's fantastic world; we'll begin with REPL up!

 

REPL up!


REPL is the interpreter of Clojure, and it is an acronym of Read Evaluate Print Loop. Unlike other interpreter languages, such as Python or Ruby, Clojure REPL automatically compiles into Java's byte code after the reading expression. Then, REPL evaluates the expression and returns the result of the expression. This dynamic compilation feature of REPL makes Clojure code execution as fast as executing pre-compiled code.

Getting ready

Before you set up your Clojure environment, the Java Development Kit (JDK) is necessary. The JDK version should be 1.6 or later. Throughout the book, we will use JDK 1.8 to develop and test the code.

This is how the command-line result will look, once you type java -version:

How to do it...

Leiningen is a standard build tool for Clojure. It simplifies the Clojure development, including setting up your project, compiling and testing code, and creating libraries for deployment.

It's easy to set up a Clojure environment using Leiningen. There are only a few steps before you can enjoy Clojure in REPL!

Here are the steps we need to perform to run Clojure REPL:

  1. Download and set up Leiningen from http://leiningen.org/.

  2. Download the lein script (or on Windows, lein.bat).

  3.  Place it on your $PATH where your shell can find it (for example, ~/bin):

    $ mv lein ~/bin
    
  4. Set it to be executable:

    $ chmod a+x ~/bin/lein
    
  5. Run lein, and it will download the self-install package:

  6.  Create a new project and go there. Using Leiningen, you can create a project from a project template. This example creates a project called living-clojure:

    $ lein new living-clojure
    

  7.  Run REPL and put Clojure code into it:

How it works...

Here is a very simple code to demonstrate how REPL works. This code simply loops forever with read, eval, and println functions:

user=> (defn simple-repl [] 
  #_=>   (try 
  #_=>     (while true 
  #_=>       (println (eval (read)))  
  #_=>       ) 
  #_=>     (catch Exception e (println "exited..")) 
  #_=>     ) 
  #_=>   ) 
#'user/simple-repl 
user=> (simple-repl) 
(+ 1 1) 
2 
(defn hello [s] (println "Hello world " s)) 
#'user/hello 
(hello "Makoto") 
Hello world  Makoto 
nil 
exited.. 
nil 
user=> 

You can exit simple-repl by entering ^D (Ctrl + D).

There's more...

Leiningen is a very powerful tool for Clojure developers. The lein new living-clojure command generates the following directory structure:

Let's pick up project.clj, which defines the project:

(defproject living-clojure "0.1.0-SNAPSHOT" 
  :description "FIXME: write description" 
  :url "http://example.com/FIXME" 
  :license {:name "Eclipse Public License" 
            :url "http://www.eclipse.org/legal/epl-v10.html"} 
  :dependencies [[org.clojure/clojure "1.8.0"]]) 

In project.clj, the :dependencies section declares the libraries used by your project.

Leiningen internally uses Maven to solve the dependencies of libraries. However, declaring libraries in project.clj is much simpler than doing it in the pom.xml file of Maven.

To use other libraries for your project, add them to the dependency section. We will review how to do this in a later recipe. In the preceding project.clj file, the Clojure library named org.clojure/clojure is declared and automatically downloads in the maven directory. This is the reason why you don't need to download and set up the Clojure library explicitly.

Without Leiningen, you have to do it in a more native way. Here are the steps:

  1. Download clojure-1.8.0.jar.

  2. Download clojure-1.8.0.jar using wget:

    $ wget http://repo1.maven.org/maven2/org/clojure/clojure/1.8.0/
            clojure-1.8.0.jar
    

  3.  Run Clojure and test the Clojure code:

    $ java -jar clojure-1.8.0.jar 
    Clojure 1.8.0
    user=> (+ 1 1)
    2
    

REPL supports the command-line editing feature, like Linux bash shell does, but the preceding way does not. Another difference is that REPL solves library dependency problems in project.clj, but using the native way, you can solve them by yourself.

See also

Please see related web sites as follows:

 

Working with primitive data types


In this recipe, we will review primitive data types that Clojure supports and a few functions that manipulate these types. Here, we will review the following:

  • Using numbers

  • Using strings and characters

  • Using booleans and nil

  • Using keywords and symbols

Getting ready

You only need REPL, as described in the previous recipe, and no additional libraries. Start REPL so that you can review the sample code of primitive types immediately in this recipe.

How to do it...

Let's start with numbers in Clojure data types.

Using numbers

The + function returns the sum of numbers. You can specify any number of numbers as arguments:

(+ 1 2) 
;;=> 3 
(+ 1 2 3) 
;;=> 6 
(+ 1.2 3.5) 
;;=> 4.7 

The - function subtracts the numbers. If a single argument is supplied, it returns the negation of the numbers:

(- 3 2) 
;;=> 1 
(- 1 2 3) 
;;=> -4 
(- 1) 
;;=> -1 

The * function returns the product of numbers:

(* 5 2) 
;;=> 10  
(* 1 2 3 4 5) 
;;=> 120 

The / function returns the numerator divided by all of the denominators. It is surprising for beginners of Clojure that if the numerator is not indivisible by the denominators, it returns a simple fraction. You can check the name of the class by the class function.

In the following example, 10 divided by 3 returns 10/3 and not 3.3333333...

(/ 10 5) 
;;=> 2  
(/ 10 3) 
;;=> 10/3 

To obtain numerical figures after a decimal point, cast the result by a float or double function:

(float (/ 10 3)) 
;;=> 3.3333333 
(double (/ 10 3)) 
;;=> 3.333333333333333 

The quot function returns the quotient of dividing the numerator by the denominator, and the decimal point is suppressed. rem returns the remainder of dividing the numerator by the denominator:

(quot 10 3) 
;;=> 3 
(rem 10 3) 
;;=> 1 

Clojure supports big numbers, and they are Java's BigDecimal. The bigdec function casts any number to BigDecimal. The suffix M denotes BigDecimal. Though it can express very large numbers, it cannot express repeating decimals and causes an error:

(bigdec (/ 10 2)) 
;;=> 5M 
(bigdec (/ 10 3)) 
;; ArithmeticException Non-terminating decimal expansion; no exact representable decimal result.  ;; java.math.BigDecimal.divide (BigDecimal.java:1690) 

Clojure also provides large integers, clojure.lang.BigInt. It is equivalent to Java's java.math.BigInteger:

(= (bigint 10) 10N) 
;;=> true 

Using strings and characters

Clojure strings are Java's String(java.lang.String) enclosed by double quotes (""):

"Hello world ! " 
;;=> "Hello world ! " 

Clojure has good interoperability with Java, and it's easy to call Java's member methods from Clojure. In the following example, the code calls the concat method in Java's String class:

(.concat "Hello " "world !") 
;;=> "Hello world !" 

The following syntax is how to call Java's member methods from Clojure:

(.method-name object arg1 arg2 ...) 

The first argument is a dot (.) prefix followed by the method name, then the object and its arguments.

The equivalent Java code is as follows:

String str = "Hello ".concat("world !"); 

The str function is similar to the concat method, but it can take an arbitrary number of arguments:

(str "Hello " "world !" " Clojure") 
;;=> "Hello world ! Clojure" 

To examine the length of the string, use the length method or count function:

(.length "Hello world !") 
;;=> 13 

clojure.string is a built-in Clojure library to manipulate strings. Here, we show some functions in clojure.string:

(clojure.string/blank? "   ") 
;;=> true 
(clojure.string/trim "  Hello ") 
;;=> "Hello" 
(clojure.string/upper-case "clojure") 
;;=> "CLOJURE" 
(clojure.string/capitalize "clojure") 
;;=> "Clojure" 
(clojure.string/lower-case "REPL") 
;;=> "repl" 

Let's go to Clojure character type. Characters in Clojure are java.lang.Character and are preceded by a backslash (\):

\a 
;;=> \a 

The int function gets the integer value of a character. Meanwhile, char gets an integer value from a character:

(int \a) 
;;=> 97 
(char 97) 
;;=> \a 

The seq function returns a sequence of characters from a string, and str makes a string from characters:

(seq "Hello world!") 
;;=> (\H \e \l \l \o \space \w \o \r \l \d \!) 
(str \C \l \o \j \u \r \e) 
;;=> "Clojure" 

Using booleans and nil

Clojure's booleans are java.lang.Boolean, and their values are true and false:

true 
;;=> true 
false 
;;=> false 
(= 1 1) 
;;=> true 
(= 1 2) 
;;=> false 
(= "Hello" "Hello") 
;;=> true 
(= "Hello" "hello") 
;;=> false 

The not function returns true if an expression is logically false. It returns false otherwise:

(not true) 
;;=> false 
(not false) 
;;=> true 
(not= 1 2) 
;;=> true 
(not true) 
;;=> false 

The true? function returns true if the expression is true; otherwise, it returns false. The false? function returns false if the expression is true; otherwise, it returns true:

(true? (= 1 1)) 
;;=> true 
(false? (= 1 1)) 
;;=> false 

The nil function means nothing, or the absence of a value. The nil function in Clojure is almost equivalent to the null function in Java. nil is logically false in Clojure conditionals. nil is often used as a return value to indicate false:

(if nil true false) 
;;=> false 

nil is the return value used to indicate an empty sequence:

(seq []) 
;;=> nil 

nil is returned by default when a map collection looks up and can't find a given key:

(get {:a 1 :b 2} :c) 
;;=> nil 

Using symbols and keywords

The function def binds global symbols to their values. The next example shows the def binding the pi symbol to a value (3.14159265359):

(def pi 3.14159265359) 
;;=> #'user/pi 
pi 
;;=> 3.14159265359 
pi-not-bind 
;;=> CompilerException java.lang.RuntimeException: Unable to resolve symbol: 
;;    pi-not-bind in this context, compiling:(/tmp/form-init1426260352520034213.clj:1:7266) 

Keywords are similar to symbols, but keywords evaluate to themselves:

:key1 
;;=> :key1 

Keywords are used to identify things, so they are often used in Clojure's map:

(def person {:name "John McCarthy" :country "USA"}) 
;;=>#'living-clojure.core/person 

How it works...

Clojure primitive data types are mostly Java classes, but some data types are Clojure's own classes.

For numbers, Clojure's integers are java.lang.Long and floating point numbers are java.lang.Double. bigInt and ratios are clojure.lang.BigInt and clojure.lang.Ratio respectively:

(class 1) 
;;=> java.lang.Long 
(class 1.0) 
;;=> java.lang.Double 
(class (float 1.0)) 
;;=> java.lang.Float 
(class 5.5M) 
;;=> java.math.BigDecimal 
(class 1N) 
;;=> clojure.lang.BigInt 
(class (/ 10 3)) 
;;=> clojure.lang.Ratio 

Clojure's strings and characters are java.lang.String and java.lang.Character respectively:

(class "Hello world ! ") 
;;=> java.lang.String 
(class \a) 
;;=> java.lang.Character 
(class true) 

Clojure's booleans are java.lang.Boolean:

(class true) 
;;=> java.lang.Boolean 
(class false) 
;;=> java.lang.Boolean 

Clojure's keywords and symbols are Clojure's own classes:

(class :key) 
;;=> clojure.lang.Keyword 
(class (quote a)) 
;;=> clojure.lang.Symbol 
(class nil) 
;;=> nil 

There's more...

Clojure supports hexadecimal and octal notations:

0xff 
;;=> 255 
0400 
;;=> 256 

Clojure also supports flexible numeral bases. You can specify in any base with a radix from 2 to 36:

2r11111 
;;=> 31 
16rff 
;;=> 255 
8r11000 
;;=> 4608 
7r111 
;;=>57 

There are some specially named character literals, as follows:

  • \space

  • \tab

  • \newline

  • \return

  • \formfeed

  • \backspace

Unicode characters are represented by \uNNNN in Clojure, like in Java:

\u0031\u0032\u0061\u0062 
;;=> \1 
;;=> \2 
;;=> \a 
;;=> \b 

Functions in Clojure are also symbols and bind to their functional definitions. The next example shows the value of +:

+ 
;;=> #object[clojure.core$_PLUS_ 0x3c2ace8a "clojure.core$_PLUS_@3c2ace8a"] 

To evaluate symbol and return itself, use quote:

(quote pi-not-bind) 
;;=> pi-not-bind 

To use the math library in Clojure, try clojure.math.mumeric-tower. Here are the steps:

  1. Add the following dependency to project.clj:

          [org.clojure/math.numeric-tower "0.0.4"] 
    
  2. You can use expt and sqrt in the library:

          (require '[clojure.math.numeric-tower :as math]) 
          (math/expt 2 10) 
          ;;=> 1024 
          (math/sqrt 10) 
          ;;=> 3.1622776601683795 
    
 

Using bindings of vars, conditions, loops, and error handling


In this recipe, we will review Clojure programming control structures related to vars and values, conditions, iterations, and loops. We will use the following special forms, macros, and functions:

  • def and let

  • if and if-not

  • when and when-not

  • case and cond

  • do and dotimes

  • loop and recur

  • try... catch... throw

Getting ready

You only need REPL, as described in the first recipe in this chapter, and no additional libraries. Start REPL so that you can test the sample code immediately in this recipe.

How to do it...

Let's start with how to use def and let to bind vars.

def and let

def is a special form that binds symbols in the global scope in their namespace. def requires var and value:

(def var val) 

This sample binds x to 100:

(def x 100) 
;;=> 100 

Whereas let binds symbols in its local scope. You can put multiple expressions in a let clause. let evaluates them and returns the last expression:

(let [var-1 val-1 var-2 val-2 ...] 
    expr-1 
    expr-2 
    .... 
    ) 

In this example, let binds x to 3 and y to 2. Then, it evaluates two expressions consecutively and returns the second expression:

(let [x 3 y 2] 
  (println "x = " x ", y = " y) 
  (* x y) 
  ) 
;;=> x =  3 , y =  2 
;;=> 6 

if and if-not

if takes three arguments; the third argument (else-expression) is optional:

(if condition then-expression else-expression) 

In the next example, the code returns the absolute value of numbers:

(let [x 10] 
  (if (> x 0)  x  (- x)) 
  ) 
;;=> 10 
(let [x -10] 
  (if (> x 0)  x  (- x)) 
  ) 
;;=> 10 

If there's no third parameter and if the test results as false, it returns nil:

(let [x -10] 
  (if (> x 0)  x)) 
;;=> nil 

if-not is opposite to if. It returns then-expression if the test fails:

(if-not condition then-expression else-expression) 

The example should return false:

(if-not true true false) 
;;=> false 

when and when-not

The when function is similar to if, but it evaluates one or more expressions if the condition is evaluated to true; otherwise, it is false:

(when condition expr-1 expr-2 ...) 

The first expression prints out x = 10 and returns 100. The second one only returns nil:

(let [x 10] 
  (when (> x 0) 
    (println "x = " x) 
    (* x x))) 
;;=> x = 10 
;;=> 100 
(let [x -10] 
  (when (> x 0) 
    (println "x = " x) 
    (* x x))) 
;;=> nil 

when-not is the opposite of when:

(when-not condition expr-1 expr-2 expr 3 ...) 

The next code uses when-not and does the same thing as the preceding function:

(let [x 10] 
  (when-not (<= x 0) 
    (println "x = " x) 
    (* x x))) 
;;=>x = 10 
;;=> 100 
(let [x -10] 
  (when-not (<= x 0) 
    (println "x = " x) 
    (* x x))) 
;;=> nil 

case and cond

case tests whether there is a matched value. If so, case evaluates the corresponding expression. If there is no matched value, it returns otherwise-value. If there is no otherwise-value specified, it returns nil:

(case condition 
    value1  expr-1 
    value2  expr-2 
    otherwise-value 
    ) 

In the first expression, the condition matches the value 2 and "two" is returned. The second expression returns a string, "otherwise":

(let [x 2] 
  (case x 
    1 "one" 
    2 "two" 
    3 "three" 
    "otherwise" 
        )) 
;;=> "two" 
(let [x 4] 
  (case x 
    1 "one" 
    2 "two" 
    3 "three" 
    "otherwise" 
        )) 
;;=> "otherwise" 

cond is a macro and is similar to case. cond has been heavily used in the Lisp language. cond is more flexible than case.

cond takes a set of condition/expr pairs and evaluates each condition. If one of the conditions is true, cond evaluates the corresponding expression and returns it. Otherwise, it returns the expression of :else:

(cond 
    condition-1 expr-1 
    condition-2 expr-2 
    ... 
    :else expr-else 
    ) 

The next sample code acts the same as the preceding one:

(let [x 10] 
  (cond 
    (= x 1) "one" 
    (= x 1) "two" 
    (= x 3) "three" 
    :else "otherwise" 
        ) 
  ) 

do and dotimes

do evaluates the expressions in order and returns the last:

(do expr-1 expr-2 ...) 

In the next sample, do evaluates the first expression and prints x = 10, then it evaluates the second and returns 11:

(def x 10) 
;;=> #'living-clojure.core/x 
(do 
  (println "x = " x) 
  (+ x 1)) 
;;=> x = 10 
;;=> 11 

dotimes repeats the expression while var increments from 0 to (number-exp - 1):

(dotimes [var number-exp] 
    expression 
) 

This example prints the square of x where x is 0 to 4:

(dotimes [x 5] 
  (println "square : " (* x x))) 
;;=> square :  0 
;;=> square :  1 
;;=> square :  4 
;;=> square :  9 
;;=> square :  16 

loop and recur

You may sometimes want to write a program that loops with a condition. Since Clojure is an immutable language, you cannot change a loop counter, unlike in imperative languages such as Java.

The combination of loop and recur is used in such a situation. Their forms are as follows:

(loop [var-1 val-1 var-2 val-2 ...] 
  expr-1 
  expr-2 
  ... 
  ) 
(recur expr-1 expr-2   ... ) 

The next very simple example shows how loop and recur work. In the loop, x is set to 1 and increased until it is smaller than 5:

(loop [x 1] 
  (when (< x 5) 
    (println "x = " x) 
    (recur (inc x)) 
    )  ) 
;;=> x =  1 
;;=> x =  2 
;;=> x =  3 
;;=> x =  4 
;;=> nil 

The next example calculates the sum of 1 to 10 using loop and recur:

(loop [x 1 ret 0] 
  (if (> x 10) 
    ret 
    (recur (inc x) (+ ret x))  
    ) 
) 
;;=> 55 

try... catch... throw

Clojure uses an error handler borrowed from Java:

(try exp-1 exp 2 ... 
  (catch class-of-exception var exception  
  (finally finally-expr) 
 ) 

Inside try, there are one or more expressions. finally is optional. The following example emits an exception and returns a string generated in the catch:

(try 
  (println "Let's test try ... catch ... finally") 
      (nth "Clojure" 7) 
  (catch Exception e 
      (str "exception occured: " (.getMessage e))) 
  (finally (println "test finished")) 
  ) 
;;=> Let's test try ... catch ... finally 
;;=> test finished 
;;=> "exception occured: String index out of range: 7" 

How it works...

Clojure's lexical scope hides the outside bindings of vars inside bindings of vars. The next example shows the scopes of a nested let. The inside let binds x to 10 and y to 10. Thus, inside println prints 100. Similarly, the outside let binds x to 3 and y to 2. Thus, it prints 6:

(let [x 3 y 2] 
  (let [x 10 y 10] 
    (println "inside : " (* x y)) 
    ) 
  (println "outside : " (* x y))  
  ) 
;;=> inside :  100 
;;=> outside :  6 
;;=>  nil 

Similarly, a local binding of a var hides the global binding of a var:

(def x 1) 
;;=> #'living-clojure/x 
;;=> 1 
(println "global x = " x) 
;;=> global x =  1 
(let [x 10] (println "local x = " x)) 
;;=>local x =  10 
(println "global x = " x) 
;;=> global x =  1 

The when is a macro using the if special form. You can see how the when is defined using macroexpand:

(macroexpand 
  '(when (> x 0) 
    (println "x = " x) 
    (* x x))) 
;;=> (if (> x 0) (do (println "x = " x) (* x x))) 

if-not is also a macro using if:

(macroexpand '(if-not true true false)) 
;;=> (if (clojure.core/not true) true false) 
 

Using and defining functions


In this recipe, we will review Clojure's function definitions:

  • Defining simple functions

  • Defining variadic functions

  • Defining multiple arity functions

  • Defining functions that specify arguments using a keyword

  • Defining functions with a pre-condition and a post-condition

Getting ready

You only need REPL, as described in the first recipe in this chapter, and no additional libraries. Start REPL so that you can test the sample code immediately in this recipe.

How to do it...

Here, we will learn how to define functions using Clojure. Let's start with a simple function which returns Hello world:

Defining simple functions

Let's start with a minimum function definition. Here is a minimal syntax of defn:

(defn funtion-name [arg1 arg2 ...] 
  expr-1  
  expr-2 
  .. 
  expr-n 
  ) 

defn is a special form. The first argument is a function name and is followed by a vector of one or more arguments, then one or more expressions. The last expression is returned to the caller.

Here, we define a very simple function. The hello function returns a Hello world string:

(defn hello [s] 
  (str "Hello world " s " !")) 
;;=> #'living-clojure.core/hello 
(hello "Nico") 
;;=> "Hello world Nico !" 
(hello "Makoto") 
;;=> "Hello world Makoto !" 

The next sample defines a simple adder function:

(defn simple-adder [x y] 
  (+ x y) 
  ) 
;;=> #'living-clojure.core/simple-adder 
(simple-adder  2 3) 
;;=> 5 

Defining variadic functions

A variadic function allows a variable number of arguments. The next example defines another adder. It may have an arbitrary number of arguments:

(defn advanced-adder [x & rest] 
  (apply + (conj rest x)) 
  ) 
;;=> #'living-clojure.core/advanced-adder 
(advanced-adder 1 2 3 4 5) 
;;=> 15 

Defining multiple arity functions

Here, we will introduce the multiple arity function. The following function defines a single argument function and a couple of argument functions with the same defn:

(defn multi-arity-hello 
  ([] (hello "you")) 
  ([name] (str "Hello World " name " !"))) 
;;=> #'living-clojure.core/multi-arty-hello 
(multi-arity-hello) 
;;=> Hello World you ! 
(multi-arity-hello "Nico") 
  ;;=> Hello World Nico ! 

Defining functions that specify arguments using a keyword

Sometimes, specifying a keyword is useful, since it is not necessary to remember the order of arguments.

The next example shows how to define such a function. The options are :product-name, :price, and :description. The :or expression supplies default values if any values in keys are omitted:

(defn make-product-1 
  [serial & 
   {:keys [product-name price description] 
    :or {product-name "" price nil description "no description !"} 
    }  
   ] 
   {:serial-no serial :product-name product-name 
    :price price :description description} 
  ) 
;;=> #'living-clojure.core/make-product-1 
 
(defn make-product-2 
  [serial & 
   {:keys [product-name price description] 
    :or {:product-name "" :description "no description !"} 
    }    
   ] 
   {:serial-no serial :product-name product-name 
    :price price :description description} 
  ) 
;;=> #'living-clojure.core/make-product-2 
 
(make-product-1 "0000-0011") 
;;=> {:serial-no "0000-0011", :product-name "", :price nil, :description "no description !"} 
(make-product-2 "0000-0011") 
;;=> {:serial-no "0000-0011", :product-name nil, :price nil, :description nil} 

Defining functions with pre-condition and post-condition

Clojure can define functions with pre-condition and post-condition. In the following defn, :pre checks whether an argument is positive. :post checks whether the result is smaller than 10:

(require '[clojure.math.numeric-tower :as math]) 
;;=> nil 
(math/sqrt -10) 
;;=> NaN 
(defn pre-and-post-sqrt [x] 
  {:pre  [(pos? x)] 
   :post [(< % 10)]} 
   (math/sqrt x)) 
;;=> #'living-clojure.core/pre-and-post-sqrt 
(pre-and-post-sqrt 10) 
;;=> 3.1622776601683795 
(pre-and-post-sqrt -10) 
;;=> AssertionError Assert failed: (pos? x)  user/pre-and-post-sqrt (form-init2377591389478394456.clj:1) 
(pre-and-post-sqrt 120) 
AssertionError Assert failed: (< % 10)  user/pre-and-post-sqrt (form-init2377591389478394456.clj:1) 

Moreover, in this recipe, we will show a more complicated function. The make-triangle function prints a triangle with a character. If this function is called without a :char argument, it prints a triangle made of asterisks. If it is called with a :char argument, it prints a triangle comprising characters specified by :char:

(defn make-triangle 
  [no & {:keys [char] :or {char "*"}}] 
  (loop [x 1] 
    (when (<= x no)  
      (dotimes  
          [n (- no x)] (print " ")) 
      (dotimes  
          [n  
           (if (= x 1)  
             1     
             (dec (* x 2)))] 
        (print char)) 
      (print "\n") 
      (recur (inc x)) 
      ) 
    ) 
  ) 
(make-triangle 5)     
;;=>     * 
;;=>    *** 
;;=>   ***** 
;;=>  ******* 
;;=> ********* 
;;=> nil 
(make-triangle 6 :char "x"))     
;;=>      x 
;;=>     xxx 
;;=>    xxxxx 
;;=>   xxxxxxx 
;;=>  xxxxxxxxx 
;;=> xxxxxxxxxxx 

How it works...

We have already reviewed how to define functions and how to use them. You should understand how they work after reviewing the previous section.

To define functions using defn is the same as vars bind to functions by fn as follows:

(defn pow-py-defn [x] (* x x)) 
;;=> #'living-clojure/pow-py-defn 
(def pow-by-def (fn [x] (* x x))) 
;;=> #'living-clojure/pow-by-def 
(pow-py-defn 10) 
;;=> 100 
(pow-by-def 10) 
;;=> 100 

There's more...

clojure.repl has some useful functions to use with other functions. To get a symbol in the specific namespace, use clojure.repl/dir:

(require 'clojure.string) 
;;=> nil 
(clojure.repl/dir clojure.string) 
;;=> blank? 
;;=> capitalize 
;;=> escape 
;;=> join 
;;=> lower-case 
;;=> re-quote-replacement 
;;=> replace 
;;=> replace-first 
;;=> reverse 
;;=> split 
;;=> split-lines 
;;=> trim 
;;=> trim-newline 
;;=> triml 
;;=> trimr 
;;=> upper-case 
;;=> nil 

To get the documentation of a function, use clojure.repl/doc:

(clojure.repl/doc clojure.string/trim) 
------------------------- 
;;=> clojure.string/trim 
;;=> ([s]) 
;;=>   Removes whitespace from both ends of string. 
;;=> nil 

To get symbols that have a specific string, use clojure.repl/apropos:

(clojure.repl/apropos "defn") 
;;=> (clojure.core/defn 
;;=>  clojure.core/defn- 
;;=>  deps.compliment.v0v2v4.compliment.sources.local-bindings/defn-like-forms 
;;=>  deps.compliment.v0v2v4.deps.defprecated.v0v1v2.defprecated.core/defn) 
 

Using third-party libraries


So, you have found someone else's code that you would like to use, and you are trying to take their work and use it in your project, great! This is what this recipe is all about. There are a few ways to get the code closer to you.

Getting ready

This recipe will introduce you to the art of adding dependencies, packaged as JAR files, to your project and how to reference them, as well as using them from your Clojure code. We will go from downloading the file and starting Clojure REPL manually, to using dependency management tools. Lastly, we will also present how to add new dependencies at runtime, so you do not need to restart your live programming environment.

How to do it...

Each minor section in this recipe shows you how to add the dependency, using different ways in different scenarios.

Adding the JAR file manually to your classpath

Say we have found the following library, clj-tuples, and we want to add this to our REPL session. Most JARs for Clojure are available on either mvnrepository.com or clojars.org.

clj-tuples is on clojars, and since clojars is a regular Maven repository, we can navigate directly through the file. Download and save it (https://clojars.org/repo/ruiyun/tools.timer/1.0.1/tools.timer-1.0.1.jar), and now let's start a Clojure REPL:

java -cp .:clj-tuple-0.2.2.jar:clojure-1.8.0-beta1.jar  clojure.main

And let's quickly have fun with our new library code...mmmmm, tuples:

user=> (use 'ruiyun.tools.timer) 
; nil 
user=> (run-task!  
#(println "Say hello every 5 seconds.") :period 5000); #object[java.util.Timer 0x45a4b042 "java.util.Timer@45a4b042"] 
user=> Say hello every 5 seconds. 
   user=> (cancel! *1) 

Ok, great!

Using Leiningen and a project.clj file

So, that was fun, but maybe you have more than one person's code you want to steal, and also, you just noticed that some of the stolen code is also stealing code from somebody else's code...what do you do?

Leiningen is a command-line tool that will, among other things, help you maintain stolen code. This recipe will not look into installing Leiningen because there is documentation all around that does this, so we just want to make sure at this stage that you have a recent version:

NicolassMacBook:chapter01 niko$ lein version

Leiningen 2.5.1 on Java 1.8.0_45 Java HotSpot(TM) 64-Bit Server VM

To make Leiningen understand what we want, most of the time, we would give it a project.clj file with a DSL that looks mostly like a gigantic Clojure map. If we want to import the same dependency as we did previously, this is the way we would write it:

(defproject chapter01 "0.1.0" 
  :dependencies  
  [[org.clojure/clojure "1.8.0-beta1"] 
   [clj-tuple "0.2.2"]]) 

At its root, declaring a dependency on a third-party library is done through this mini DSL, where a two element vector points to a name and a version:

[name "version"] 

So here:

[clj-tuple "0.2.2"] 

Dependencies are declared as Clojure vectors in the project map, with :dependencies as its key. We now have access to billions of libraries of somewhat different quality depending on the author, but anyway, it's done. Leiningen also uses clojars by default, so there's no need to define repository definitions yet. The same code as before works:

(use 'clj-tuple) 

Viewing dependencies

Using the project.clj file, we can directly see what our code depends on, and in particular the version of things our code depends on. The project.clj file of clj-tuple is located at https://github.com/Ruiyun/tools.timer/blob/master/project.clj and the portion we are interested in is as follows:

(defproject clj-tuple "0.2.2" 
  :description "Efficient small collections." 
  :dependencies [] 
  ...) 

This means that clj-tuples does not depend on anything else and is a well behaved self-contained library.

This is obviously not always the case, and while we are at it we can look at another library, named puget.

Puget has the following dependencies defined:

:dependencies 
    [[fipp "0.6.2"] 
    [mvxcvi/arrangement "1.0.0"] 
    [org.clojure/clojure "1.8.0"]] 

But the great thing is that we don't need to write all those dependency lines ourselves. Transitive dependencies are pulled properly by Leiningen, so our project.clj file will now look simply like this:

(defproject chapter01 "0.1.0" 
  :dependencies [ 
                 [org.clojure/clojure "1.8.0-beta1"] 
                [clj-tuple "0.2.2"]                  
                 [mvxcvi/puget "0.9.1"]]) 

The first time we launch a new REPL we will notice all the dependencies being downloaded:

NicolassMacBook:chapter01 niko$ lein repl 
Retrieving mvxcvi/puget/0.9.1/puget-0.9.1.pom from clojars 
Retrieving fipp/fipp/0.6.2/fipp-0.6.2.pom from clojars 
Retrieving org/clojure/core.rrb-vector/0.0.11/core.rrb-vector-0.0.11.pom from central 
Retrieving mvxcvi/arrangement/1.0.0/arrangement-1.0.0.pom from clojars 
Retrieving org/clojure/core.rrb-vector/0.0.11/core.rrb-vector-0.0.11.jar from central 
Retrieving fipp/fipp/0.6.2/fipp-0.6.2.jar from clojars 
Retrieving mvxcvi/arrangement/1.0.0/arrangement-1.0.0.jar from clojars 
Retrieving mvxcvi/puget/0.9.1/puget-0.9.1.jar from clojars     

This only applies the first time. The second time it occurs without extra messages, and you can now check for yourself that the library is there, and you get the expected colorized output, but not in this book:

user=> (require '[puget.printer :as puget]) 
nil 
user=> (puget/pprint #{'x :a :z 3 1.0}) 
#{1.0 3 :a :z x} 
nil 
user=> (puget/cprint #{'x :a :z 3 1.0}) 
#{1.0 3 :a :z x} 

one-off

Sometimes it is great to just try things out, and have a one-off REPL to try out the new dependencies. This is where we can use a Leiningen plugin named try.

Plugins for Leiningen can be installed globally on your machine with a file named profiles.clj located here:

$HOME/.lein/profiles.clj

The file is a simple map with user-defined settings; here, we just want to add a plugin, so we add it to the vector:

{:user {:plugins [ [lein-try "0.4.3"] ]}} 

That's it. From anywhere on your computer you can now try dependencies. To make things new, we will look at a new useful dependency named env, which makes it easy to retrieve environment settings.

This is the usual Leiningen definition of env, and the following code would be used in project.clj, as seen before:

[adzerk/env "0.2.0"] 

Here, we just type the following:

lein try adzerk/env  

And we can see the dependencies coming along locally (provided you have an Internet connection):

Retrieving adzerk/env/0.2.0/env-0.2.0.pom from clojars 
Retrieving adzerk/env/0.2.0/env-0.2.0.jar from clojars 

And we can now use the require macro:

(require '[adzerk.env :as env]) 

And try it. env returns all the env variables available, whether through Java or Shell:

user=> (env/env) 
; ... 

If you have the chance to run the preceding command where project.clj was located, the dependencies from the project are also available, so we can combine dependencies:

user=> (require '[puget.printer :as puget]) 
 
user=> (puget/cprint (env/env))  
{"Apple_PubSub_Socket_Render" "/private/tmp/com.apple.launchd.jI7P2DRL6X/Render", 
 "HOME" "/Users/niko", 
 "JAVA_ARCH" "x86_64", 
 "JAVA_CMD" "java", 
 "JAVA_MAIN_CLASS_3927" "clojure.main", 
 "JVM_OPTS" "", 
 ... 

Voila! Now we have combined a temporary dependency with our main project dependencies.

New dependencies at runtime

So far we have seen how to add dependencies offline, in the sense that we need to stop our REPL in order for the new dependencies to be handled properly. Now we will see how to add a dependency on the fly.

The dependency we will look at now is named pomegranate and it does just that, add dependencies on the fly.

In the same way we added a Leiningen plugin earlier on, we will add a global dependency to our runtimes by adding a dependency to the same profiles.clj file:

{:user  
    {:dependencies  
    [[com.cemerick/pomegranate "0.3.0"]] }} 

Those dependencies will be ready to be loaded through all your Leiningen-based projects, so be careful not to add too many. With a new REPL loaded, we can now load pomegranate, and the only method we need is happily named add-dependencies:

(require '[cemerick.pomegranate :refer [add-dependencies]])  

The newly required function takes a vector of coordinates using the same pattern we have seen so far, and a map of repositories, with a name for the key and a URL to a Maven-like repository for the value:

(add-dependencies  
                 :coordinates '[[active-quickcheck "0.3.0"]] 
                 :repositories  {"clojars" "http://clojars.org/repo"}) 

The first time this is called, the dependency and all the underlying will be downloaded and be ready for use. We took active-quickcheck as a sample, a library that can be used to generate random tests based on some given predicates. The library still needs to be required in the current namespace:

(use 'active.quickcheck) 
 
; sample dependency test, we will not go in the details here 
(quickcheck (property [a integer 
                   b integer] 
           (= (+ a b) (+ b a)))) 

So we have pretty much seen all the different ways to add dependencies to our Clojure environment, whether through:

  • Direct JAR file

  • Leiningen's project.clj

  • Leiningen's profiles.clj

  • Leiningen's try plugin

  • Using pomegranate

 

Using namespaces


Clojure organizes code via namespaces, or small units of code. Each namespace contains a set of define functions. One of the recurring questions is to figure out how to do the layout of your namespaces, and how to organize your different namespaces so they relate to each other in a clean way. This is what this recipe will go through.

Getting ready

This recipe does not require any special installation steps apart from a running REPL. Also, it is recommended to keep the Clojure namespaces page open so we can refer to it quickly.

How to do it...

We will go through the steps of creating a Clojure namespace, referencing code from other namespaces, as well as loading and reloading namespaces on demand. Lastly, we will go briefly through a few concepts of how to organize those namespaces properly.

Creating a new namespace

We start here by looking at how namespaces are created. While working in the REPL, making changes to or creating a namespace mostly resorts to using the in-ns function from clojure.core:

(in-ns 'hello) 

And next time we define a var using def, we see it bound to that namespace:

hello=> (in-ns 'hello) 
#object[clojure.lang.Namespace 0x3b5fad2d "hello"] 
hello=> (def a 1) 
#'hello/a 

Great. Now, since the function resolves to var as well, we can define the function in our namespace. We need to give the full path to fn now to define the definition, so clojure.core/fn:

hello=> (def b (clojure.core/fn[c] (clojure.core/inc c))) 
#'hello/b 
hello=> (b 2) 
3 

Inspecting namespaces

Namespaces are regular objects, so on the JVM we can see their internals very easily. find-ns tells us what the object is, and on the JVM we then call .getMappings from the namespace object:

(find-ns 'hello) 
; #object[clojure.lang.Namespace 0x3b5fad2d "hello"] 
(.getMappings *1) 
.... 

Namespaces are listed and retained statically in a static field of this class on the JVM so we can refer to things later.

While in-ns helps us define namespaces, we will actually use a higher-level function named ns:

helloagain=> (ns helloagain) 
nil 
helloagain=>  

If you want to go and have a look, ns actually does quite a bit for us. The two main keywords that can be used are imports and refers.

Adding functions with :import and :refer

:import will import classes from the hosting virtual machine (JVM, JavascriptVM, and .NET VM for now). Say in Java we want to use the raw Random Java object directly, we will import it in the namespace with the following:

(ns helloagain (:import [java.util Random])) 
(Random.) 
; #object[java.util.Random 0x6337c201 "java.util.Random@6337c201"] 

We can also check the imports defined in each namespace with ns-imports:

(ns-imports 'helloagain) 
;...  

By default, Clojure does some work for us, to make sure the basic Java objects are already available in each new namespace.

In the same way as we can import Java objects, we can go along and require other Clojure namespaces in the current namespace. This is done through the :require keyword in the namespace definition:

helloagain=>  (ns helloagain (:require [clojure.string :refer :all])) 
WARNING: reverse already refers to: #'clojure.core/reverse in namespace: helloagain, being replaced by: #'clojure.string/reverse 
WARNING: replace already refers to: #'clojure.core/replace in namespace: helloagain, being replaced by: #'clojure.string/replace 
nil 
helloagain=> (reverse "helloagain") 
"niagaolleh" 

The newly referred functions can be seen using ns-refers:

(ns-refers 'helloagain) 
; ... (somewhere clojure.string ...)     

Loading namespaces from files

Now, effectively, the ns calls will mostly be located at the top of each file. Let's say we want to keep the code we have now on this helloagain namespace; we will create a helloagain.clj file and copy the following content:

(ns helloagain  
      (:import [java.util Random]) 
     (:require [clojure.string :refer :all])) 
 
(println  
     (clojure.string/reverse (str (.getName *ns*)))) 
 
 (println (Random.)) 

The namespace and thus the file can be loaded through require. For this to work, we need to validate our classpath, so let's review the way we started the REPL earlier on:

java -cp .:clojure-1.8.0-beta1.jar clojure.main  

The . makes the files in the current folder available on the classpath, so then require can look through the defined classpath and find the file helloagain.clj we have just defined:

user=>  (require 'helloagain) 
niagaolleh 
nil 
user=>  

You will notice the code is executed when calling require on the command line. The ' is required because helloagain should not be evaluated, but instead kept as a symbol.

Reloading namespaces

Now, here's something slightly more complicated. We want to add a function to the helloagain namespace:

(defn only-one [] 
   (println "only one")) 

We want to use this function in a different user namespace, so we can call the ns macro again to do this, or we can also use require directly. Supposing we have added the preceding function to our filename; simply calling require should do it for us:

user=> (require 'helloagain)  
nil 
user=>  (helloagain/only-one) 
CompilerException java.lang.RuntimeException: No such var: helloagain/only-one, compiling:(NO_SOURCE_PATH:4:2)  

Or not. What happened there? The file was not reloaded from disk, and we just got a reference to the already created namespace. The :reload keyword gives us a chance to specify that we want to reload from disk:

(require 'helloagain :reload) 
user=> (helloagain/only-one) 
; only one 

IDEs such as IntelliJ will actually, most of the time, reload the full current file from the buffer, and so the definition of the namespace can be updated.

When loading and reloading namespaces, state management becomes problematic, so we will focus on that a little bit later.

How to organize namespaces

There is great science and research being done on how to organize namespaces; even Albert Einstein had a biweekly meeting to make sure namespaces would be organized in a compatible way.

Here is a list that will help you focus on creating namespaces around coherent goals:

  • Group architectural layers with different causes for concern

  • Functional modules that have to define contracts to communicate with each other

  • Define a public API on top of internal low-level functions

A public API namespace will have well defined contracts and extensive documentation, while low-level functions maybe be more tested but could be less well documented.

Some people have suggested grouping functions depending on the kind of data they are handling. This is also good when you do not have to deal with the relationships of those namespaces; for example, you defined a User namespace and a ShoppingList namespace, but then Users with many ShoppingList items make the relationship management between the two namespaces cumbersome. Only use this way of organization if the data handled is straightforward and simple.

Possibly the most interesting motivation to separate namespaces is the public API method.

There's more...

In the previous section, we saw that having a namespace for the public API of your library is important, so the following will describe, with the potemkin library as an example, how to extract just what you need from your code to present the API you want to present:

tools.namespacepotemkin

Namespace for public API

potemkin has a very interesting method, named import-vars, that allows something like a copy/paste of a function from a different namespace to a current one. If we remember the previous recipe, we can simply try the potemkin dependency as follows:

lein try potemkin "0.4.1" 

Then we can select on-demand functions and clean up our public-facing namespace:

user=> (require 'potemkin) 
(potemkin/import-vars [clojure.string reverse]) 
 (user/reverse "hello")  

This makes selecting functions for our namespace clean, without any code, mostly documentation.

This also makes a case for versioning our namespace, in case some breaking changes are introduced but there is no need to make a completely new version of your code.

tools.namespace

Now, we saw just a few moments ago that we can force :reload on a namespace, but things get complicated when namespaces depend on each other, and you are not sure which one was reloaded and which one was not. This is where tools.namespace makes it easier for you to track all this for you. Let us start a REPL and try this out:

lein try org.clojure/tools.namespace "0.2.11" 

As per the doc, the refresh function will scan all the directories on the classpath for Clojure source files, read their ns declarations, build a graph of their dependencies, and load them in dependency order. (You can change the directories it scans with set-refresh-dirs.)

So, at the REPL, the function we mostly need from tools.namespace is refresh:

(require '[clojure.tools.namespace.repl :refer [refresh]]) 

Now, supposing our file helloagain.clj is in the src folder so, src/helloagain.clj, and lein can find and load the file with require:

(require '[helloagain :refer :all]) 
user=> (only-one) 
only one 

We will quickly add a new function to our file:

 (defn only-two [] 
    (println "only two"))   

And call refresh to make sure we have the latest code:

user=> (refresh) 
; :reloading (helloagain) 
user> (helloagain/only-two) 
; only two 

And that is all. We have pulled the latest from our namespace code, and the required namespaces will be reloaded as needed.

 

What's next?


In this chapter, we have seen how to create namespaces and how to inspect them. We also looked at how to organize those namespaces, and how to make sure we always have the code we want at the place we want. We remembered that a public API is a key point for creating namespaces as well as organizing code into functional modules.

The next step is to look at the workflow for reloading a full application that has dependent components. Those components will require a workflow to manage their state and the now famous component library has been established as the way to go.

Maybe this could also be part of a future Clojure version, in which the need to break namespaces into something smaller than files will be handled.

Latest Reviews (1 reviews total)
Very good coverage of the material and covers the current specification and usage patterns.
Clojure Programming Cookbook
Unlock this book and the full library FREE for 7 days
Start now