Book Image

Clojure Programming Cookbook

Book Image

Clojure Programming Cookbook

Overview of 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!
Table of Contents (16 chapters)
Clojure Programming Cookbook
Credits
About the Authors
About the Reviewer
www.PacktPub.com
Preface

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)