Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Clojure Programming Cookbook

You're reading from   Clojure Programming Cookbook Handle every problem you come across in the world of Clojure programming with this expert collection of recipes

Arrow left icon
Product type Paperback
Published in Oct 2016
Publisher Packt
ISBN-13 9781785885037
Length 618 pages
Edition 1st Edition
Languages
Arrow right icon
Authors (2):
Arrow left icon
Nicolas Modrzyk Nicolas Modrzyk
Author Profile Icon Nicolas Modrzyk
Nicolas Modrzyk
Makoto Hashimoto Makoto Hashimoto
Author Profile Icon Makoto Hashimoto
Makoto Hashimoto
Arrow right icon
View More author details
Toc

Table of Contents (11) Chapters Close

Preface 1. Live Programming with Clojure 2. Interacting with Collections FREE CHAPTER 3. Clojure Next 4. File Access and the Network 5. Working with Other Languages 6. Concurrency and Parallelism 7. Advanced Tips 8. Web Applications 9. Testing 10. Deployment and DevOps

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) 
lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image