Clojure forbeginners

67
@ 2010 by Satish Talim

Transcript of Clojure forbeginners

Page 1: Clojure forbeginners

@ 2010 by Satish Talim

Page 2: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 2

Clojure for Beginners

Copyright (c) 2010 Satish Talim http://satishtalim.com/

Warning and Disclaimer

Every effort has been made to make this book as complete and as accurate as possible, but no

warranty of fitness is implied. The information provided is on an "as is" basis. The author shall

have neither liability nor responsibility to any person or entity with respect to any loss or

damages arising from the information contained in this book.

Revised Edition – 27th May 2010 First Edition – May 2010

PLEASE SUPPORT RUBYLEARNING.COM

Page 3: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 3

Clojure Notes

Contents

Acknowledgements .................................................................................................................................... 8

ABOUT THE EBOOK ................................................................................................................................... 10

Assumptions ............................................................................................................................................................................. 10

Concept and Approach ........................................................................................................................................................ 10

How to Use This Clojure eBook ....................................................................................................................................... 10

About the Conventions Used in This Clojure eBook .............................................................................................. 11

Learn Clojure Online and for Free.................................................................................................................................. 11

INTRODUCTION TO CLOJURE .................................................................................................................... 12

What is Clojure? ...................................................................................................................................................................... 12

GETTING STARTED .................................................................................................................................... 14

Labrepl Download and Installation (for MS Windows)....................................................................................... 14

What's Labrepl? ................................................................................................................................ 14

Downloading and Installing Labrepl (for MS Windows) .................................................................... 14

Starting the REPL ............................................................................................................................... 15

Loading a file in the REPL .................................................................................................................. 16

Clojure Box Download and Installation (for MS Windows) .............................................................................. 16

Test if Clojure Box works ................................................................................................................... 17

Loading a file in Clojure Box .............................................................................................................. 17

NetBeans/Enclojure Download and Installation (for MS Windows) ............................................................ 18

To create a Clojure Project in NetBeans............................................................................................ 19

Page 4: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 4

To start the REPL in NetBeans ........................................................................................................... 19

Anatomy of the Enclojure REPL Panel ............................................................................................... 19

Syntax .......................................................................................................................................................................................... 19

Comments .................................................................................................................................................................................. 19

Clojure Coding Guidelines & Naming Convention ............................................................................................. 20

Forms ........................................................................................................................................................................................... 21

Symbols ....................................................................................................................................................................................... 21

Vars and Bindings .................................................................................................................................................................. 22

Literals ......................................................................................................................................................................................... 23

Booleans and nil ................................................................................................................................ 23

Numbers ........................................................................................................................................... 23

Characters and Strings ...................................................................................................................... 25

Keywords .......................................................................................................................................... 26

Exercises ..................................................................................................................................................................................... 26

Exercise 1 .......................................................................................................................................... 26

Exercise 2 .......................................................................................................................................... 27

CHARGING AHEAD .................................................................................................................................... 28

A Quick look at Collections ................................................................................................................................................ 28

Lists ................................................................................................................................................... 28

Vectors .............................................................................................................................................. 29

Sets ................................................................................................................................................... 29

Maps ................................................................................................................................................. 30

Some functions on collections .......................................................................................................... 31

Sequences .................................................................................................................................................................................. 32

seq .................................................................................................................................................... 32

Page 5: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 5

first .................................................................................................................................................... 32

rest .................................................................................................................................................... 33

cons ................................................................................................................................................... 33

next ................................................................................................................................................... 33

conj, into ........................................................................................................................................... 34

range ................................................................................................................................................. 34

repeat ............................................................................................................................................... 34

iterate, take ...................................................................................................................................... 34

concat ............................................................................................................................................... 35

Flow Control ............................................................................................................................................................................. 35

if, if-not ............................................................................................................................................. 36

cond, condp ...................................................................................................................................... 36

when, when-not ................................................................................................................................ 37

do ...................................................................................................................................................... 38

Defining Functions................................................................................................................................................................. 39

Exercise 1 .......................................................................................................................................... 41

Exercise 2 .......................................................................................................................................... 41

Exercise 3 .......................................................................................................................................... 42

Documentation ........................................................................................................................................................................ 43

Using doc .......................................................................................................................................... 43

find-doc ............................................................................................................................................. 43

Documenting a function ................................................................................................................... 44

Clojure API ........................................................................................................................................ 44

CLOJURE AND JAVA ................................................................................................................................... 45

Working with Java ................................................................................................................................................................. 45

Page 6: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 6

Importing multiple classes ................................................................................................................ 45

Create a Java object .......................................................................................................................... 45

Accessing methods ........................................................................................................................... 46

..chains .............................................................................................................................................. 46

doto .................................................................................................................................................. 47

Accessing static fields ........................................................................................................................ 47

Accessing static methods .................................................................................................................. 47

-> macro ............................................................................................................................................ 47

Exception handling ............................................................................................................................ 48

Some More Examples ....................................................................................................................... 49

Source for a function......................................................................................................................... 49

Inspector ........................................................................................................................................... 49

DIVING INTO CLOJURE .............................................................................................................................. 51

Namespaces .............................................................................................................................................................................. 51

in-ns function .................................................................................................................................... 51

ns ...................................................................................................................................................... 51

More on Bindings ................................................................................................................................................................... 53

let ...................................................................................................................................................... 53

binding .............................................................................................................................................. 54

Echo Server ............................................................................................................................................................................... 55

Port ................................................................................................................................................... 55

Socket ............................................................................................................................................... 56

clojure-contrib library ....................................................................................................................... 56

server-socket API .............................................................................................................................. 56

duck-streams API .............................................................................................................................. 57

Page 7: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 7

Step 1: Define a namespace .............................................................................................................. 57

Step 2: Define a port ......................................................................................................................... 58

Step 3: Define a function................................................................................................................... 58

Step 4: Start the server ..................................................................................................................... 58

Step 5: Test the Echo Server ............................................................................................................. 59

StructMaps ................................................................................................................................................................................ 59

Refs ................................................................................................................................................................................................ 61

Transactions ............................................................................................................................................................................. 61

A Simple Accounts example .............................................................................................................. 63

Compiling using Clojure Box .......................................................................................................................................... 64

About the Author ...................................................................................................................................... 67

Page 8: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 8

Acknowledgements

My interest in Clojure was aroused when Michael Kohl organized the first-ever, free, four-

week, online Clojure course on RubyLearning.org –

http://rubylearning.com/blog/2010/03/09/clojure-101-a-new-course/

(for people with some background in Lisp and / or Clojure.)

Having had no previous exposure to Lisp and Clojure, I could not keep up with the others in

the course after the first week. I decided to start making these study notes from the

perspective of a beginner in Lisp and Clojure but with some experience in other programming

languages.

There are a good number of people who deserve thanks for their help and support they

provided, either before or while this eBook is being written, and there are still others whose

help will come after the eBook is released.

I would specially like to thank Baishampayan Ghose, Daniel Solano Gomez and Michael Kohl

for offering suggestions and proofreading these study notes.

The material in these study notes is drawn primarily from the references mentioned below.

My acknowledgment and thanks to all of them.

Clojure Home

http://clojure.org/

Clojure - Functional Programming for the JVM by R. Mark Volkmann –

http://java.ociweb.com/mark/clojure/article.html

Programming Clojure by Stuart Halloway –

http://www.pragprog.com/titles/shcloj/programming-clojure

The Joy of Clojure by Michael Fogus and Chris Houser –

http://www.manning.com/fogus/

Clojure in Action by Amit Rathore –

http://www.manning.com/rathore/

Practical Clojure by Luke Van der Hart –

http://apress.com/book/view/1430272317

Clojure Programming/Concepts –

http://en.wikibooks.org/wiki/Clojure_Programming/Concepts

Page 9: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 9

Functional Programming with Clojure Screencast by PeepCode –

http://peepcode.com/products/functional-programming-with-clojure

Page 10: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 10

ABOUT THE EBOOK

Assumptions

These study notes are for absolute beginners in Clojure.

Some previous programming background is essential.

Some parts require knowledge of Java.

The programs in these study notes have been tested using the Clojure 1.1 version.

Since I have a Windows box, I have mentioned the installation of Clojure and other

dependencies for MS Windows only.

Concept and Approach

I have tried to organize this eBook in which each chapter builds upon the skills acquired in the

previous chapter, so that you will never be called upon to do something that you have not

already learned. This eBook not only teaches you how to do something, but also provides you

with the chance to put those morsels of knowledge into practice with exercises. I have

therefore included several exercises, or assignments, in this eBook so that you will have

opportunities to apply your knowledge.

How to Use This Clojure eBook

I recommend that you go through the entire Clojure eBook chapter by chapter, reading the

text, running the sample programs and doing the assignments along the way. There are no

large applications in this book – just small, self-contained sample programs. This will give you

a much broader understanding of how things are done (and of how you can get things done),

and it will reduce the chance of anxiety, confusion, and worse yet, mistakes.

It is best to read this eBook and complete its assignments when you are relaxed and have time

to spare. Nothing makes things go wrong more than working in a rush. And keep in mind that

Clojure and the assignments in this eBook are fun, not just work exercises. So go ahead and

enjoy it.

Page 11: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 11

About the Conventions Used in This Clojure eBook

Explanatory notes (generally provides some hints or gives a more in-depth explanation of

some point mentioned in the text) are shown shaded like this:

This is an explanatory note. You can skip it if you like - but if you do so, you may miss

something of interest!

Any source code in this Clojure eBook, is written like this:

(defn greet

([] (greet " world"))

([name] (str "Hello " name)))

When there is a sample program to accompany the code, the program name is shown like this:

program.clj

Though we would be discussing Clojure 1.1 on the Windows platform, these notes are

appropriate for Linux/Mac users as well.

If you notice any errors or typos, or have any comments or suggestions or good exercises I

could include, please email me at [email protected].

Learn Clojure Online and for Free

If you want to learn Clojure Programming for free, do join the batch at

http://rubylearning.org/

RubyLearning is the first and only site in the world that teaches Ruby and Clojure

Programming for free. Over 30 mentors help you through the learning process – 24x7.

Page 12: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 12

INTRODUCTION TO CLOJURE

What is Clojure?

Clojure is a dynamically-typed, functional programming language that runs on the JVM (Java 5

or greater) and provides interoperability with Java.

http://en.wikipedia.org/wiki/Functional_programming

"A key characteristic of Clojure is that it is a functional language, which means that functions

are the fundamental building-block for programs rather than instructions, as is the case in

most other programming languages (known as imperative languages). In Clojure, functions are

best thought of as more like their counterparts in mathematics – a function is simply an

operation that takes a number of parameters (also called arguments), and returns a value.

These functions always return the same result when passed the same arguments. Imperative

languages perform complex tasks by executing large numbers of instructions, which

sequentially modify a program state until a desired result is achieved. Functional languages

achieve the same goal through nested function composition – passing the result of one

function as a parameter to the next. By composing and chaining function calls, along with

recursion (a function calling itself), a functional program can express any possible task that a

computer is capable of performing. An entire program can itself be viewed as a single

function, defined in terms of smaller functions. The nesting structure determines the

computational flow, and all the data is handled through function parameters and return

values." - From Practical Clojure.

A major goal of Clojure is managing concurrency. Wikipedia has a great definition of

concurrency: "Concurrency is a property of systems in which several computations are

executing and overlapping in time, and potentially interacting with each other. The

overlapping computations may be executing on multiple cores in the same chip, preemptively

time-shared threads on the same processor, or executed on physically separated processors."

The primary challenge of concurrency is managing access to a shared, mutable state.

Clojure helps you write correct concurrent programs by emphasizing immutability. Clojure

enforces isolating changes in mutable state as either atomic operation with atoms, within a

transaction with refs, or asynchronously with agents. Additionally, there are no explicit locks

in Clojure, so this common source of deadlock is removed.

Michael Fogus, author of the book "The Joy of Clojure" says that - "Clojure was born out of

creator Rich Hickey's desire to avoid many of the complications, both inherent and incidental,

of developing concurrent applications using Java and C++. The Java Virtual Machine is an

amazingly practical platform -- it is mature, fast, and widely deployed. It supports a variety

of hardware and operating systems and has a staggering number of libraries and support tools

Page 13: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 13

available, all of which Clojure can take advantage of, thus allowing prospective developers to

avoid the costs of maintaining yet another infrastructure while leveraging existing libraries."

The following shows briefly the release dates for various versions of Clojure:

1.2 is expected soon.

1.1 (the current version) released in Dec. 2009.

1.0 released in May 2009.

Clojure was first released on October 16, 2007.

Page 14: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 14

GETTING STARTED

Labrepl Download and Installation (for MS Windows)

What's Labrepl?

Labrepl is an environment for exploring the Clojure language. It includes:

a web application that presents a set of lab exercises with step-by-step instructions

an interactive repl for working with the lab exercises

up-to-date versions of Clojure, contrib, incanter, compojure and a bunch of other

libraries to explore

The Labrepl site mentions how one can use Labrepl on other operating systems.

http://github.com/relevance/labrepl

Downloading and Installing Labrepl (for MS Windows)

Download and install Java version 6 - this is a pre-requisite.

http://java.sun.com/javase/downloads/index.jsp

Next, go to the Labrepl site and click on the "Download Source" button on the top of

the page. A .zip file will get downloaded to your computer. Extract this .zip file to

your c: drive. On my computer it created a folder c:\relevance-labrepl-d6b9759.

For Labrepl to work we shall also need Leiningen.

http://github.com/technomancy/leiningen

Leiningen is a build tool for Clojure. Installation of Leiningen on MS Windows is

complicated, because automatic install isn't implemented yet. For MS Windows you

need to download lein.bat script, and put it into the folder c:\leiningen.

http://github.com/technomancy/leiningen/raw/stable/bin/lein.bat

Set your system environment variable "PATH" to include the folder c:\leiningen.

Create a sub-folder c:\leiningen\lib.

Page 15: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 15

Next download leiningen-1.1.0-standalone.jar and copy it to the folder

c:\leiningen\lib.

http://github.com/downloads/technomancy/leiningen/leiningen-1.1.0-

standalone.jar

After this, download the clojure.jar package. A .zip file gets downloaded to your

computer. From this .zip file extract clojure.jar and copy it to the c:\leiningen\lib

folder.

http://code.google.com/p/clojure/downloads/list

Edit lines 14 and 15 of lein.bat to correct the path, namely:

o set LEIN_JAR=c:\leiningen\lib\leiningen-1.1.0-standalone.jar

o set CLOJURE_JAR=c:\leiningen\lib\clojure.jar

Open a new command window and change folder to c:\relevance-labrepl-d6b9759.

Next type lein deps to install all the dependent libraries. This might take some time.

Close the command window.

Starting the REPL

Interaction with Clojure is often performed at the REPL (Clojure read-eval-print loop). To

start a REPL, open a new command window and change folder to c:\relevance-labrepl-

d6b9759. Next type:

c:\relevance-labrepl-d6b9759> script\repl

This starts a new REPL session and you are presented with a simple user> prompt. It is at this

point that REPL waits for user input. The user namespace is the REPL default, like the default

package in Java. You should treat user as a scratch namespace for exploratory development.

After user> type (println "Hello World") and press ENTER, as follows:

user> (println "Hello World")

Hello World

nil

user>

Page 16: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 16

The second line above, Hello World, is the console output you requested. The third line, nil,

is the return value of the call to Clojure's println (the funtion used to display something on

the screen).

For a list of all the functions available in the clojure.core API, see –

http://richhickey.github.com/clojure/clojure.core-api.html.

If you get your REPL into a state that confuses you, the simplest fix is to close the command

window.

Loading a file in the REPL

If you have a block of code that is too large to conveniently type at the REPL, save the code

into a file, and then load that file from the REPL. You can use an absolute path or a path

relative to where you launched the REPL.

Here's how you would load a file in your REPL:

(load-file file-name)

The load-file function sequentially reads and evaluates the set of forms (more on this later)

contained in the file.

A Clojure program is stored in a text file with the extension .clj. Let us load the file test.clj

located in the folder c:\users\talim\myproject\src\test:

(load-file "c:/users/talim/myproject/src/test/test.clj") ; => nil

Clojure Box Download and Installation (for MS Windows)

Note: If you have installed Labrepl then you can ignore this step.

1. Download and install Java version 6 - this is a pre-requisite.

http://java.sun.com/javase/downloads/index.jsp

2. Download and install "Clojure Box". Clojure Box is an all-in-one installer for Clojure on

Windows. You simply install and run this one thing, and you get a REPL (Clojure read-eval-

print loop) and all the syntax highlighting and editing goodies from clojure-mode and

Slime, plus all the power of Emacs under the hood.

http://clojure.bighugh.com/

Page 17: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 17

Note:

You can watch a video of "Install Clojure - ClojureBox".

http://vimeo.com/9219062

Read the post install notes - http://bitbucket.org/shoover/clojure-box/src/tip/post-

install.txt.

Test if Clojure Box works

Interaction with Clojure is often performed at the REPL.

To test whether Clojure works, double-click on the "Clojure Box" icon on your desktop. This

starts a new REPL session and you are presented with a simple user> prompt. It is at this

point that REPL waits for user input. The user namespace is the REPL default, like the default

package in Java. You should treat user as a scratch namespace for exploratory development.

After user> type (println "Hello World") and press ENTER:

user> (println "Hello World")

Hello World

nil

user>

The second line above, Hello World, is the console output you requested. The third line, nil,

is the return value of the call to Clojure's println (the funtion used to display something on

the screen).

For a list of all the functions available in the clojure.core API, see –

http://richhickey.github.com/clojure/clojure.core-api.html.

If you get your REPL into a state that confuses you, the simplest fix is to kill the REPL with

Ctrl-C.

Loading a file in Clojure Box

If you have a block of code that is too large to conveniently type at the REPL, save the code

into a file, and then load that file from the REPL. You can use an absolute path or a path

relative to where you launched the REPL.

Here's how you would load a file in your REPL:

Page 18: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 18

(load-file file-name)

The load-file function sequentially reads and evaluates the set of forms (more on this later)

contained in the file.

A Clojure program is stored in a text file with the extension .clj. Let us load the file test.clj

located in the folder c:\users\talim\myproject\src\test:

(load-file "c:/users/talim/myproject/src/test/test.clj") ; => nil

NetBeans/Enclojure Download and Installation (for MS Windows)

Note: If you have installed Labrepl or Clojure Box then you can ignore this step.

1. Download and install Java version 6 - this is a pre-requisite.

http://java.sun.com/javase/downloads/index.jsp

2. Download and install NetBeans 6.8 (Java SE option).

http://netbeans.org/downloads/index.html

3. Download the latest Enclojure .nbm file.

http://github.com/EricThorsen/enclojure/downloads

In NetBeans go to Tools > Plugins. In the dialog click on Downloaded then click the

"Add Plugins..." button.

Navigate to the location where you saved the NBM file, select it and click the "Open"

button.

Highlight "Clojure Plugin" and click the "Install" button.

Restart the NetBeans IDE.

Next, navigate to Tools > Options > Clojure and at the bottom of the screen select

clojure-1.1.0 for the Clojure platform. Click the "OK" button.

Restart the NetBeans IDE.

Page 19: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 19

To create a Clojure Project in NetBeans

1. Navigate to File > New Project... Select Categories Clojure and click the "Next"

button.

2. I created a folder c:\ClojureProjects on my hard disk.

3. I typed the following:

Project Name: RL

Default namespace: com.rl.hello

Project location: c:\ClojureProjects

4. Click the "Finish" button.

To start the REPL in NetBeans

1. Right click on the newly created project namely RL, which you should be seeing in the

Projects window.

2. Select "Start Project REPL" in the window that pops up.

Anatomy of the Enclojure REPL Panel

See: http://www.enclojure.org/Anatomy+of+the+Enclojure+Repl+Panel

Note: You can watch a video of "Install Clojure - NetBeans".

http://vimeo.com/9220148

Syntax

Clojure is a Lisp dialect and has a syntax that uses parentheses and prefix notation. For

example, in Java one might write foo(a, b, c), whereas in a Lisp dialect this becomes (foo a,

b, c). Since the commas are whitespace and Clojure ignores them, it can be simplified further

to (foo a b c). Many text editors and IDEs highlight matching parentheses, so it isn't necessary

to count them in order to ensure they are balanced.

Clojure is case-sensitive.

Comments

Open a new REPL and try out whatever we discuss, from now on.

Page 20: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 20

There are two ways in which you can comment in Clojure.

A line comment is:

; This is a single line Clojure comment

For block comments:

(comment text)

It should be noted that the block comment form above does have a return value, namely nil.

So you can't just "comment out" a piece of your code with it, because it still leaves a trace.

This form is sometimes used at the end of a source code file to demonstrate usage of an API.

For example, the Clojure inspector library ends with the following comment, demonstrating

the use of the inspector:

(comment

(load-file "src/inspector.clj" )

(refer 'inspector)

(inspect-tree {:a 1 :b 2 :c [1 2 3 {:d 4 :e 5 :f [6 7 8]}]})

(inspect-table [[1 2 3][4 5 6][7 8 9][10 11 12]])

)

Clojure Coding Guidelines & Naming Convention

1. Use two spaces for indentation.

2. Make your names for functions, vars as descriptive as possible, without being overly

verbose.

3. Use lower-case letters for names.

4. The names of predicate functions (returning a truthy (everything not nil or false) or

falsy (false and nil) should typically end with a question mark (?).

5. End the name of a function in an exclamation mark (!) if it is destructive.

The obvious exceptions to the guidelines above are when generating Java code, which

requires that Java naming conventions be observed.

Page 21: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 21

For more details please see the Clojure Library Coding Standards.

http://www.assembla.com/wiki/show/clojure/Clojure_Library_Coding_Standards

The naming convention in Clojure is to use all lowercase with hyphens separating words in

multi-word names, unlike the Java convention of using camelcase.

An example:

(function-name arg1 arg2 arg3)

Forms

Clojure code is composed of Clojure data. When you run a Clojure program, a part of Clojure

called the reader reads the text of the program in chunks called forms and translates them

into Clojure data structures. Clojure then compiles and executes the data structures.

A Clojure form is basically just an expression. It's any piece of code that evaluates down to a

single value.

The Clojure forms are: symbols, literals (i.e. booleans, nil, numbers, characters, strings,

keywords), lists, vectors, maps and sets.

Symbols

Symbols are used to name things. Symbols name all sorts of things in Clojure:

Functions like str and concat

Clojure does not have operators. Characters like + - * / are just functions

Java classes like java.lang.String and java.util.Random

Namespaces like clojure.core and Java packages like java.lang

Data structures and references.

Symbols cannot start with a number and can consist of alphanumeric characters, plus +, -, *,

/, !, ?, ., and _.

You can call a function through a symbol such as println:

(println "Hello Clojure participants") ; => Hello Clojure participants

Page 22: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 22

Rather than calling a function through a symbol, you might choose just to retrieve the

function itself. The literal representation of a function at the REPL is just a mangled name:

println

Sometimes you want to refer to the symbol itself, without retrieving whatever it refers to. To

do this, you can quote the symbol:

(quote println) ; => println

Quoting is so common that that there is a sugared form: simply put a single quote in front of

any symbol to prevent that form from being evaluated:

'println ; => println

Vars and Bindings

The special form (these are forms evaluated in a special way) def creates a var. If the var did

not already exist and no initial value is supplied, the var is unbound. In the code below, x is a

symbol which is used to identify the var in the future. At this point, x has no "value", and so

we can't refer to it:

(def x) ; => #'user/x

x ; => java.lang.IllegalStateException: var user/x is unbound.

If called with an additional parameter it creates a root binding or rebinds the var in case it

was already bound. This binding is like an "invisible" link between the var and the value. For

example, the following def creates a var named user/my-var in the namespace user:

(def my-var 10) ; => #'user/my-var

The symbol user/my-var refers to a var that is bound to the value 10. The initial value of a

var is called its root binding.

Clojure provides bindings which are like constants in other languages, which are not intended

to be changed after a value is assigned. Other threads can then override that binding

temporarily. Threads which do not have a thread local binding will inherit the root binding.

There are global bindings, thread-local bindings, bindings that are local to a function and

bindings that are local to a given form.

Page 23: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 23

The def special form creates a global binding and optionally gives it a "root value" that is

visible in all threads unless a thread-local value is assigned.

In Clojure, the object that your var is referring to can't change. If you want your var to refer

to a different value, you have to rebind it to a new object, a new value. Although vars can be

rebound, it is generally not done in a program except to create thread-local bindings.

Literals

Booleans and nil

Clojure’s rules for booleans (true and false) are easy to understand:

1. true is true, and false is false.

2. In addition to false, nil (meaning 'nothing/no-value'- represents Java null) also

evaluates to false when used in a boolean context.

3. Other than false and nil, everything else evaluates to true in a boolean context.

Numbers

A number consists of only the digits 0-9, a decimal point '.', a sign ('+' or '-'), and an optional 'e'

for numbers written in exponential notation. In addition to these elements, numbers in

Clojure can take either octal or hexadecimal form and also include an optional 'M' (this is used

to explicitly create a BigDecimal).

Clojure supports the following numeric types: integer, floating point, ratio.

Integers comprise the whole number set, both positive and negative. That is, any number

starting with an optional sign or digit followed exclusively by digits is considered and stored

as an integer. Integers in Clojure can theoretically take an infinitely large value, although in

practice the size is limited by the memory available.

Page 24: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 24

Floating point numbers are of the form of some number of digits, a decimal point, followed

by some number of digits. However, floating point numbers can also take an exponential form

where a significant part is followed by an exponent part separated by a lower or uppercase

'E'.

0x7F ; => hexadecimal number whose value is 127

0177 ; => octal number whose value is 127

11.7e-3 ; => 0.0117

(+ 1 2 3 4) ; => 10

(/ 22 7) ; => 22/7

200/5 ; => 40

In the code above, we first use the + function to add the numbers 1, 2, 3 and 4. The result of

(/ 22 7) is surprising as Clojure has a built in clojure.lang.Ratio type. Using ratios help avoid

inaccuracies in long computations. Ratios are represented by an integer numerator and

denominator. Also observe that the ratio 200 / 5 will resolve to the integer 40.

Lets first try a computation of (1/5 * 5/1) as floating point. Later we try the same with Ratio.

(def x (/ 1.0 5.0))

(def y (/ 5.0 1.0))

(* x y) ; => 1.0

(def p (* x x x x x x x x x x))

(def q (* y y y y y y y y y y))

(* p q) ; => 1.0000000000000004

The value of (* p q) above is 1.0000000000000004 instead of 1 which is what we want. This is

due to the inaccuracies of x and y multiplying as we create p and q. You really don't want

such calculations happening in your financial transactions!

The same done with ratios below:

(def x (/ 1 5))

(def y (/ 5 1))

(* x y) ; => 1

(def p (* x x x x x x x x x x))

(def q (* y y y y y y y y y y))

(* p q) ; => 1

Using a floating point literal instead, we get:

(/ 22.0 7) ; => 3.142857142857143

Page 25: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 25

Arbitrary-precision arithmetic

In Clojure, we can do arbitrary-precision arithmetic - a technique whereby calculations are

performed on numbers whose digits of precision are limited only by the available memory of

the host system. Clojure relies on Java's BigDecimal class for arbitrary-precision decimal

numbers and on Java's BigInteger class for arbitrary-precision integers.

http://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic

If you are doing arbitrary-precision math, append M to a number to create a BigDecimal

literal:

(+ 1 (/ 0.00001 1000000000000000000)) ; => 1.0

(+ 1 (/ 0.00001M 1000000000000000000)) ; => 1.00000000000000000000001M

Clojure objects are Java objects and you can use Java's Reflection API methods such as class,

ancestors, and instance? to reflect against the underlying Java object model.

(class (+ 1 (/ 0.00001M 1000000000000000000))) ; => java.math.BigDecimal

Clojure's approach to arbitrary-sized integers is simple: just don't worry about it. Clojure will

automatically upgrade to Long or BigInteger when you need it. Try creating some small and

large integers, and then inspect their class:

(class (* 100 100 100)) ; => java.lang.Integer

(class (* 9000 9000 9000)) ; => java.lang.Long

(class (* 9000 9000 9000 9000 9000 9000 9000 9000)) ; => java.math.BigInteger

format

Round off a number to say 2 decimal places:

(format "%.2f" 1.2345) ; => "1.23"

Characters and Strings

Clojure character literals are preceded by a backslash. Their literal syntax is \{letter}, where

letter can be a letter or the name of a character: backspace, form-feed, newline, return,

space, or tab. These yield the corresponding characters.

Clojure strings are Java strings. They are immutable, and have access to all the underlying

Java methods. They are delimited by double quotes ("), and they can span multiple lines.

Page 26: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 26

Strings are sequences of characters. The function concat returns a list of character literals, of

the elements in the supplied strings:

(concat "Hello" " World") ; => (\H \e \l \l \o \space \W \o \r \l \d)

Clojure's str is a function call that concatenates an arbitrary list of arguments into a string:

(str \h \e \l \l \o \space \w \o \r \l \d) ; => "hello world"

(str "Hello" " " "World") ; => "Hello World"

Standard Java escape characters are supported. If you want to put a double quote inside a

string then you have to escape the double quote. The backslash is the escape character: (println "She replied: \"Hello\" to my greetings.")

; => She replied: "Hello" to my greetings.

Keywords

A keyword is like a symbol, except that keywords begin with a colon (:). Keywords resolve to

themselves:

:foo ; => :foo

The fact that keywords resolve to themselves makes keywords useful as keys.

Exercises

Exercise 1

Write a Clojure program that displays how old I am, if I am 979000000 seconds old.

Sample solution: (println "You are" (/ 979000000 60.0 60 24 365) "years old")

; => You are 31.04388635210553 years old

; Note that println automatically added a space between its arguments

We can improve upon the above program. It's always better to perform the calculation using

exact numbers and then coerce the result if needed (we use float in the example below). This

is an advantage which Clojure offers when compared to other languages; you can perform

Page 27: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 27

calculations with exact numbers instead of floating point approximations. A better solution

would be: (println "You are" (float (/ 979000000 60 60 24 365)) "years old")

; => You are 31.043886 years old

Exercise 2

Write a Clojure program that tells you how many minutes there are in a year (do not bother

right now about leap years etc.).

Sample solution: (println "There are" (* 60 24 365) "minutes in a year")

; => There are 525600 minutes in a year

Page 28: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 28

CHARGING AHEAD

A Quick look at Collections

Clojure provides the collection types list, vector, set and map. The Clojure collection types

are immutable, heterogeneous and persistent. By immutable it means that their contents

cannot be changed. By heterogeneous it means that they can hold any kind of object. By

persistent it means that when a new version of a collection is created by adding or removing

something from it, the new version shares structure with the old one. The old version will be

accessible only when we hold a reference to its head otherwise it will be garbage collected.

Lists

Lists are written with parenthesis. Lists are the traditional lisp singly linked lists. Lists can

contain items of any type, including other collections:

; Empty list

() ; => ()

(class ()) ; => clojure.lang.PersistentList$EmptyList

'(a b c) ; => (a b c)

(class '(a b c)) ; => clojure.lang.PersistentList

A list is "just data," but it is also used to call functions. They are ideal when new items will be

added to or removed from the front. Lists are not efficient for finding items by index.

An example of a list whose first item names a Clojure function:

(+ 1 2)

In Clojure, data and code have the same representation. (a b c) is a call to a function named

a with arguments b and c. To make this data instead of code, the list needs to be quoted. '(a

b c) or (quote (a b c)) is a list of the values a, b and c.

It may be good to note that lists are singly linked lists.

Page 29: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 29

Vectors

Vectors are essentially like dynamically-sized arrays. Vectors store a series of values like lists

do and can be used like lists, except that they can be indexed by integers. Vectors are

written with square brackets:

[1 2 3] ; => [1 2 3]

(class [1 2 3]) ; => clojure.lang.PersistentVector

[ ] ; => [ ]

(class [ ]) ; => clojure.lang.PersistentVector

Vectors are ideal when new items will be added to or removed from the back. Vectors are

efficient for finding or changing items by index i.e. vectors are functions of their indices:

; retrieve an element by its index

(["hello" "world" 1 2 3] 1) ; => "world"

Note: Unless the list characteristic of being more efficient at adding to or removing from the

front is significant for a given use, vectors are typically preferred over lists. This is mainly due

to the vector syntax of [...] being a bit more appealing than the list syntax of '(...). It doesn't

have the possibility of being confused with a call to a function, macro or special form.

Sets

Sets are collections of unique items. Sets are zero or more forms enclosed in braces preceded

by #. The #{} reader macro is only for hash sets:

#{ } ; => #{ }

(class #{ }) ; => clojure.lang.PersistentHashSet

#{:a :b :c} ; => #{:a :b :c}

Sets are preferred over lists and vectors when duplicates are not allowed and items do not

need to be maintained in the order in which they were added. Clojure supports two kinds of

sets, unsorted and sorted. Hash sets are generally preferred over sorted sets as they are much

faster. Sorted sets are important when keeping things sorted is very important. Here are some

more ways to create a set:

(sorted-set "Mandy" "Anita" "Rich") ; #{"Anita" "Mandy" "Rich"}

(hash-set "Mandy" "Anita" "Rich") ; #{"Anita" "Mandy" "Rich"}

; duplicates are removed

(hash-set "Rich" "Mandy" "Anita" "Rich") ; #{"Anita" "Mandy" "Rich"}

Page 30: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 30

Maps

Maps are zero or more key/value pairs where both can be any kind of object and enclosed in

braces. Maps store unique keys and one value per key. Often keywords are used for map keys:

{ } ; => { }

(class { }) ; => clojure.lang.PersistentArrayMap

{:Ruby "Matz" :Clojure "Hickey"} ; => {:Ruby "Matz" :Clojure "Hickey"}

You can use a map directly, it is not necessary to bind it to a name:

(:a {:a 1, :b 2}) ; => 1

We can use def to save the map into a Clojure var:

(def inventors {:Ruby "Matz" :Clojure "Hickey"})

Commas are considered whitespace, and can be used to organize the pairs:

(def inventors {:Ruby "Matz", :Clojure "Hickey"})

Maps are functions. If you pass a key to a map, it will return that key’s value, or it will return

nil if the key is not found:

(:Clojure inventors) ; => Hickey

(inventors :Java) ; => nil

The keys function returns a sequence of the map's keys:

; Usage: (keys map)

(keys inventors) ; => (:Ruby :Clojure)

The vals function returns a sequence of the map's values:

; Usage: (vals map)

(vals inventors) ; => ("Matz" "Hickey")

Page 31: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 31

Some functions on collections

There are many core functions that operate on all kinds of collections; far too many to

describe here. A small subset of them is described next, mostly using vectors and sometimes

lists.

count function

The count function returns the number of items in any collection.

(count [22 "green" false]) ; => 3

; empty collections correctly return 0

(count '()) ; => 0

reverse function

The reverse function returns a sequence of the items in the collection in reverse order.

(reverse [2 42 72]) ; => (72 42 2)

(reverse '(2 42 72)) ; => (72 42 2)

; strings are collections too

(reverse "hello") ; => (\o \l \l \e \h)

apply function

The apply function returns the result of a given function when all the items in a given

collection are used as arguments.

(apply - [34 23 9]) ; => 2

map function

(map f coll)

map takes a source collection coll and a function f, and it returns a new sequence by invoking

f on each element in the coll.

(map * [1 2 3 4] [1 2 3 4]) ; => (1 4 9 16)

Page 32: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 32

Sequences

A sequential collection is one that holds a series of values without reordering them.

A sequence is a sequential collection that represents a series of values that may or may not

exist yet. They may be values from a concrete collection, or values that are computed as

necessary. A sequence may be empty.

Sequences include Java collections, Clojure-specific collections, strings, streams, directory

structures and XML trees. Because so many things are sequences, the sequence library is very

powerful. Remember that the sequence library can be used with all collections (lists, vectors,

sets, maps ...).

Important: Most Clojure sequences are lazy: they generate elements only when they are

actually needed. Clojure sequences are immutable: they never change.

seq

The seq function turns any core collections into something called a seq i.e. the seq function

will return a seq on any seq-able collection (coll):

(seq coll)

seq will return nil if its coll is empty or nil.

An example:

(seq [1 2 3]) ; => (1 2 3)

(seq [ ]) ; => nil

first

You can get the first item in a sequence:

(first aseq)

first returns nil if its argument is empty or nil.

Page 33: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 33

An example:

(first [34 23 15]) ; => 34

(first [ ]) ; => nil

rest

You can get everything after the first item, in other words, the rest of a sequence:

(rest aseq)

rest returns an empty seq (not nil) if there are no more items.

An example:

(rest [34 23 15]) ; => (23 15)

(rest [ ]) ; => ()

(class (rest [ ])) ; => clojure.lang.PersistentList$EmptyList

cons

You can construct a new sequence by adding an item to the front of an existing sequence:

(cons elem aseq)

An example:

(cons 1 [2 3 4]) ; => (1 2 3 4)

Under the hood, first, rest and cons are declared in a Java interface clojure.lang.ISeq.

next

The next function will return the seq of items after the first:

(next aseq)

(next aseq) is equivalent to (seq (rest aseq)).

Page 34: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 34

An example:

(next [1 2 3 4]) ; => (2 3 4)

conj, into

(conj coll element & elements)

(into to-coll from-coll)

conj adds one or more elements to a collection, and into adds all the items in one collection

to another. For lists, conj and into add to the front:

(conj '(10 20 30) :a) ; => (:a 10 20 30)

(into '(10 20 30) '(:a :b :c)) ; => (:c :b :a 10 20 30)

For vectors, conj and into add elements to the back:

(conj [10 20 30] :a) ; => [10 20 30 :a]

(into [10 20 30] [:a :b :c]) ; => [10 20 30 :a :b :c]

range

range produces a sequence from a start to an end, incrementing by step each time. Ranges

include their start, but not their end. If you do not specify them, start defaults to zero, and

step defaults to 1.

(range 10 20 2) ; => (10 12 14 16 18)

repeat

The repeat function repeats an element p n times:

(repeat 5 "p") ; => ("p" "p" "p" "p" "p")

iterate, take

(iterate f x)

iterate begins with a value x and continues forever, applying a function f to each value to

calculate the next.

Page 35: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 35

If you begin with 1 and iterate with inc, you can generate the whole numbers:

(take 10 (iterate inc 1)) ; => (1 2 3 4 5 6 7 8 9 10)

Since the sequence is infinite, you need another new function to help you view the sequence

from the REPL.

(take n sequence)

take returns a lazy sequence of the first n items from a collection and provides one way to

create a finite view onto an infinite collection.

The following example creates a lazy seq of all natural numbers:

<pre class="brush: clojure">

An usage example:

(take 5 natural-numbers) ; => (0 1 2 3 4)

concat

The concat function can be used for plain concatenation of any collection:

(concat [22 "green" false] [33 44]) ; => (22 "green" false 33 44)

; the two collections can be of different types

(concat #{22 "green" false} '(33 44)) ; => ("green" false 22 33 44)

Flow Control

Page 36: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 36

if, if-not

Clojure’s if evaluates its first argument. If the argument is logically true, it returns the result

of evaluating its second argument. If you want to define a result for the "else" part of if, add

it as a third argument (but this is optional):

(println (if (< 34 100) "yes" ))

; => yes

(println (if 0 "Zero is true" "Zero is false")) ; => Zero is true

If the first argument to if is logically false and you didn't specify an else form, it returns nil:

(if (< 50000 100) "yes" )

; => nil

The if-not macro does the inverse of what the if special form does. The general structure of

this macro is:

(if-not test consequent alternative?)

Here, if the test is false, the consequent is evaluated, else if it is true and the alternative is

provided, it is evaluated instead.

cond, condp

cond is like the case statement of Clojure. The general form looks like the following:

(cond & clauses)

Here's an example:

Page 37: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 37

(def x 10)

(cond

(< x 0) (println "Negative!")

(= x 0) (println "Zero!"))

; => nil

(cond

(< x 0) (println "Negative!")

(= x 0) (println "Zero!")

:default (println "Positive!"))

; => Positive!

As you can see, the clauses are a pair of expressions, each of the form test consequent. Each

test expression is evaluated in sequence, and when one returns true, the associated

consequent is evaluated and returned. If none return true, nil is returned. If a :default is

provided, the associated consequent is evaluated and returned instead.

The general form of condp looks like the following:

(condp pred expr & clauses)

The condp macro is similar to a case statement in other languages. It takes a two parameter

predicate (this is mostly = or instance?) and an expression to act as its second argument.

After those it takes any number of value/result expression pairs that are evaluated in order.

If the predicate evaluates to true when one of the values is used as its first argument then

the corresponding result is returned. An optional final argument is the result to be returned if

no given value causes the predicate to evaluate to true. If this is omitted and no given value

causes the predicate to evaluate to true then an IllegalArgumentException is thrown:

(condp = 1

1 "Clojure"

2 "Ruby"

3 "Java"

"Sorry, no match")

; => "Clojure"

(condp = 5

1 "Clojure"

2 "Ruby"

3 "Java"

"Sorry, no match")

; => "Sorry, no match"

when, when-not

Page 38: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 38

The when form is similar to the if form. The differences are that there is no "else" condition,

and more than one expression can be added to the when form for evaluation when the

condition is true.

(when true "do-this-first" "then-that" "finally this") ; => "finally this"

when-not is the opposite of when, in that it evaluates its body if the test returns false.

(when-not true "do-this-first" "then-that" "finally this") ; => nil

(when-not false "do-this-first" "then-that" "finally this") ; => "finally this"

do

The do form is used to execute a number of operations in sequence. Clojure provides the

println form for writing to standard output. In order to use println within an expression

whose return value we care about, we need to put it in a do expression:

(do (println "Hello.") (+ 2 2))

Hello.

4

(do (println "Hello.") (println "Hello again.") (+ 2 2))

Hello.

Hello again.

4

The do operation executes each expression in sequence and returns the result of the last

expression.

do is useful when you want to include a sequence of actions in a position where only a single

form is expected, e.g. for having various statements in a branch of an if.

Here's a contrived example:

(if (odd? 3) (println "First true form") (println "Second true form (will not

print)")) ; => First true form

(if (odd? 3) (do (println "First true form") (println "Second true form (will

print)")))

; => First true form

; => Second true form (will print)

Page 39: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 39

if only accepts one form as if and else branch, so if you want to have several statements in

either, you'll have to wrap them in a do. In the first statement above, the second true from

will not print, since Clojure sees it as the else-branch. In the second it prints, because do is

considered one form.

Defining Functions

The defn macro defines a function. Its arguments are the function name, an optional

documentation string, the parameter list (specified with a vector that can be empty) and the

function body. The result of the last expression in the body is returned. Every function

returns a value, but it may be nil.

(defn my-function

"returns a String"

[name]

(str "Goodbye, " name)) ; concatenation

(println (my-function "Satish")) ; => Goodbye, Satish

Function definitions must appear before their first use. Sometimes this isn't possible due to a

set of functions that invoke each other. The declare special form takes any number of

function names and creates forward declarations that resolve these cases:

(declare function-names)

defn- works just like defn but functions defined with the defn- macro are private and are

hardly secure. This means they are only visible in the namespace in which they are defined. It

is trivial to access a private var and as such, defn- should not be used as a security measure.

You could use defn- to create some helper functions that shouldn't be exposed outside the

namespace.

user> (ns sqr)

(defn- sq [x] (* x x))

(defn sum-of-squares [p q] (+ (sq p) (sq q)))

#'sqr/sum-of-squares

sqr> (sum-of-squares 4 5) ; => 41

sqr> (sq 5)

java.lang.Exception: Unable to resolve symbol: sq in this context

If you call a function with an incorrect number of arguments, Clojure will throw an

IllegalArgumentException.

Page 40: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 40

Functions can take a variable number of parameters (note that there are no commas

delimiting the function parameters). Optional parameters must appear at the end. They are

gathered into a list by adding an ampersand and a name for the list at the end of the

parameter list:

(defn dating [person & who-all]

(println person "are dating" (count who-all) "people." ))

(dating "You" "1" "2" "3") ; => You are dating 3 people.

; dating function called with only the mandatory parameter

(dating "You") ; => You are dating 0 people.

Clojure functions are "first-class" functions i.e. Clojure functions can be stored, passed and

returned just as any other piece of data within that language. Thus, Clojure functions can be

stored in vars, held in lists and other collection types, be passed as arguments to and even

returned as the result of other functions.

Function definitions can contain more than one parameter list and corresponding body. Each

parameter list must contain a different number of parameters. This supports overloading

functions. Note that overloading of Clojure functions are based on arity (arity refers to the

differences in the argument count that a function will accept) and not on type:

(defn greet

([] (greet " world"))

([name] (str "Hello " name)))

Anonymous functions have no name. These are often passed as arguments to a named

function. When an anonymous function is defined using the fn special form, the body can

contain any number of expressions.

((fn [x] (+ x 1)) 9) ; => 10

Functions can be bound to a symbol:

(def plus-one

(fn [x] (+ x 1)))

(plus-one 9) ; => 10

An anonymous function can also be defined in the short way using #(...). The parameters are

named %1, %2, and so on. You can also use % for the first parameter.

(#(apply str %1 %2) "Hello" " World") ; => Hello World

Page 41: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 41

Finally remember that, Clojure function calls are Java method calls.

Exercise 1

Write a method called convert that takes one argument which is a temperature in degrees

Fahrenheit. This method should return the temperature in degrees Celsius.

Sample Solution: (defn convert

[fahr]

(float (* (- fahr 32) 5/9))) ; Note the use of Clojure Ratio

(println ( str "The temperature in Celcius = " (convert 75)))

; => The temperature in Celcius = 23.88889

Exercise 2

Write a method leap-year? It should have as an argument a year value, check whether it's a

leap year and then return true or false. A leap year is every 4 years, but not every 100 years,

then again every 400 years.

Sample Solution:

(defn leap-year?

[input-year]

(or (and (= (rem input-year 4) 0) (> (rem input-year 100) 0)) (= (rem input-year

400) 0)))

(leap-year? 2100) ; => false

Note:

(= x y) - Here = is the equality function and returns true if x equals y, false if not.

(rem num div) - Here rem gives us the remainder of dividing numerator by

denominator.

and macro evaluates exprs one at a time, from left to right. If a form returns logical

false (nil or false), "and" returns that value and doesn't evaluate any of the other

expressions, otherwise it returns the value of the last expr. (and) returns true.

or macro evaluates exprs one at a time, from left to right. If a form returns a logical

true value, "or" returns that value and doesn't evaluate any of the other expressions,

otherwise it returns the value of the last expression. (or) returns nil.

Page 42: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 42

Exercise 3

Create a divides? predicate that takes a dividend and a divisor, and returns true if divisor

evenly divides the dividend:

Sample Solution:

(defn divides?

"Does divisor divide dividend evenly?"

[dividend divisor]

(zero? (rem dividend divisor)))

Note:

zero? returns true if the argument to zero? is zero, else false.

Page 43: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 43

Documentation

Using doc

Nearly all the forms in Clojure have built-in documentation. If you want to read the doc at

the REPL, you can just ask for the function’s doc string:

user > (doc first)

-------------------------

clojure/first

([coll])

Returns the first item in the collection. Calls seq on its

argument. If coll is nil, returns nil.

nil

user >

doc will also tell you if a form is implemented as a macro:

user > (doc and)

-------------------------

clojure.core/and

([] [x] [x & next])

Macro

Evaluates exprs one at a time, from left to right. If a form

returns logical false (nil or false), and returns that value and

doesn't evaluate any of the other expressions, otherwise it returns

the value of the last expr. (and) returns true.

nil

user >

find-doc

The find-doc function will search for anything whose doc output matches a regular expression

or string you pass in. Example:

(find-doc "list")

Regular expressions in Clojure look like strings prepended by an #. Use a regular expression to

find all the find- functions:

(find-doc #"find-\w+")

Page 44: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 44

Documenting a function

Every function can have a doc description (similar to Javadoc) attached to it, like:

user > (defn plus-one

"Returns a number one greater than x"

[x]

(+ x 1))

#'user/plus-one

user > (doc plus-one)

-------------------------

user/plus-one

([x])

Returns a number one greater than x

nil

user >

Clojure API

Clojure’s complete API is documented online at http://clojure.org/api. The right sidebar links

to all functions and macros by name, and the left sidebar links to a set of overview articles on

various Clojure features.

Page 45: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 45

CLOJURE AND JAVA

Working with Java

Note: This section requires you to have knowledge of the Java programming language.

Importing multiple classes

Note: Clojure programs can use all Java classes and interfaces. As in Java, classes in the

java.lang package can be used without importing them. Java classes in other packages can be

used by either specifying their package when referencing them or using the import function

(use import to refer to Java classes in the current namespace).

(import 'java.util.Random)

import also takes a variable number of lists, with the first part of each list being a package

name and the rest being names to import from that package:

(import '(java.util Random Locale)

'(java.text MessageFormat))

Create a Java object

Assuming that you have used the import form as shown above, we can create a Java object of

Random as follows:

(Random.) ; note the .

Note the existence of the (new) special form. The (ClassName.) form is simply a macro for

(new ClassName):

(macroexpand '(Date.)) ; => (new Date)

Note: The macroexpand function returns a macro form's expansion.

http://richhickey.github.com/clojure/clojure.core-api.html#clojure.core/macroexpand

Page 46: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 46

To use a Random, you will need to save it away somewhere. For now, simply use def to save

the Random into a Clojure var:

(import '(java.util Random))

(def rnd (Random.))

Accessing methods

Random also has a nextInt( ) method that takes an argument. You can call it by:

(import '(java.util Random))

(def rnd (Random.))

(.nextInt rnd 10)

Also, note that (.methodName obj) is a macro for (. obj methodName):

(macroexpand '(.toString (Date.))) ; => (. (Date.) toString)

If you can't remember all the methods in the Random class, you can pass either a class or an

instance to javadoc:

(javadoc java.util.Random)

..chains

The (..) macro chains together multiple member accesses by making each result the this

object for the next member access in the chain. Thus looking up an object’s code URL

becomes the following:

(.. '(1 2) getClass getProtectionDomain getCodeSource getLocation)

Note:

The .. reads left to right, like Java. The .. macro is great if the result of each

operation is an input to the next.

It is possible to pass arguments in the intermediate function calls.

Page 47: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 47

doto

Sometimes you don’t care about the results of method calls and simply want to make several

calls on the same object. The doto macro makes it easy to make several calls on the same

object. For example, use doto to set multiple system properties:

(doto (System/getProperties)

(.setProperty "name" "Stuart")

(.setProperty "favoriteColor" "blue"))

doto returns the object at the end. This is very useful for Java Swing classes:

(doto (javax.swing.JPanel.) (.setEnabled false) (.setToolTipText "Not available"))

; => #<JPanel

javax.swing.JPanel[,0,0,0x0,invalid,disabled,layout=java.awt.FlowLayout,alignmentX=0.0

,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,preferredSize=]>

Accessing static fields

(Math/PI) ; => 3.141592653589793

(System/getProperty "java.home") ; => "C:\\Program Files\\Java\\jdk1.6.0_20\\jre"

Accessing static methods

For static methods, you can use (Classname/membername):

(System/currentTimeMillis) ; => 1272362112453

-> macro

(import [java.util Date Random])

(Date. (long (.nextInt (Random.)))) ; => #<Date Sat Jan 10 06:37:47 IST 1970>

In the above code, starting from the inside, you:

1. get a new Random

2. get the next random integer

3. cast it to a long

4. pass the long to the Date constructor

Page 48: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 48

The above is a bit confusing for beginners new to Clojure. You don't have to write inside-out

code in Clojure. The -> macro takes its first form, and passes it as the first argument to its

next form. The result then becomes the first argument of the next form, and so on. It is

easier to read than to describe:

(import [java.util Date Random])

(-> (Random.) (.nextInt) (long) (Date.)) ; => #<Date Sat Jan 10 06:37:47 IST 1970>

Exception handling

All exceptions thrown by Clojure code are runtime exceptions. Java methods invoked from

Clojure code can still throw checked exceptions. In Clojure, you are not forced to deal with

checked exceptions. You do not have to catch them or declare that you throw them. The try,

catch, finally and throw special forms provide functionality similar to their Java

counterparts.

Let's look at an example:

(/ 1 0) ; => java.lang.ArithmeticException: Divide by zero

In the above case we see an java.lang.ArithmeticException being thrown. This is a runtime

exception which is thrown by the underlying JVM.

The following shows how runtime exceptions like java.lang.ArithmeticException can be

handled:

(try (/ 1 0)

(catch Exception e (prn "in catch"))

(finally (prn "in finally")))

"in catch"

"in finally"

nil

Note: The pr and prn functions prints the object(s) to the output stream that is the current

value of *out*. pr prints the object(s), separated by spaces if there is more than one. By

default, pr and prn print in a way that objects can be read by the reader. prn is the same as

pr followed by (newline).

Page 49: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 49

Some More Examples

(.toUpperCase "hello") ; => HELLO

(.length "hello") ; => 5

In the next example, let's write a parse helper function:

(defn parse [s]

(try (Double/parseDouble (.trim s))

(catch NumberFormatException e nil)))

(parse "22") ; => 22.0

(parse " 23.45 ") ; => 23.45

(parse "asas") ; => nil

In the following example, the display helper function takes a number, rounds it to the

nearest Integer (because Integers are prettier), and returns a String:

(defn display [n]

(str (Math/round (float n))))

(display 22.5) ; => "23"

(display 22/7) ; => "3"

Source for a function

You can use Clojure to tell you the source for a function. To use this functionality, you’ll need

to import the repl-utils library:

user> (use 'clojure.contrib.repl-utils)

nil

user> (source println)

(defn println

"Same as print followed by (newline)"

[& more]

(binding [*print-readably* nil]

(apply prn more)))

nil

user>

Inspector

One useful utility built into Clojure is the ability to pop up a Swing app that can inspect a

data structure.

Page 50: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 50

http://richhickey.github.com/clojure/clojure.inspector-api.html

To start using it, type:

(use 'clojure.inspector) ; => nil

You can use the generic inspect function to look at arbitrary objects:

(inspect (System/getProperties))

Page 51: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 51

DIVING INTO CLOJURE

Namespaces

Clojure partitions things that are named by symbols into namespaces. There is always a

current default namespace, initially set to "user", and it is stored in the special symbol *ns*.

The "user" namespace provides access to all the symbols in the clojure.core namespace.

Clojure respects Java naming conventions for directories and files, but Lisp naming

conventions for namespace names. So a Clojure namespace com.my-app.utils would live in a

path named com/my_app/utils.clj. Note especially the underscore/hyphen distinction.

in-ns function

The default namespace can be changed by the in-ns function. Let's create a "notes"

namespace:

(in-ns 'notes) ; => #<Namespace notes>

Now you are in the "notes" namespace, and anything you def or defn will belong to "notes".

When you create a new namespace with in-ns, the java.lang package is automatically

available to you.

ns

The general form of the ns macro is:

(ns name & references)

The default namespace can be changed by the ns macro. The name, as mentioned above, is

the name of the namespace being made current. If it doesn't already exist, it gets created.

The references that follows the name are optional, and can be one or more of the following –

use, require, import, load, gen-class, or load.

The ns macro will create a new namespace that contains mappings for the classnames in

java.lang and for the functions in clojure.core.

Note: In order to access items that are not in the default namespace they must be

namespace-qualified. This is done by preceding a name with a namespace name and a slash

(whereas Java package names are separated from a class name with a period). Use “use” to

Page 52: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 52

load and refer Clojure libraries. For example, the round function lives in

clojure.contrib.math. In order to make round available in the current namespace, call use

on round's namespace:

(use 'clojure.contrib.math) ; => nil

The simple form of use shown above causes the current namespace to refer to all public vars

in clojure.contrib.math. This can be confusing, because it does not make explicit which

names are being referred to. Remember to pass the :only option to use, listing only the vars

you need:

(use '[clojure.contrib.math :only (round)]) ; => nil

Now you can call round without having to qualify its name:

(round 1.6) ; => 2

The ns macro, mentioned earlier, changes the default namespace. It is typically used at the

top of a source file. It supports the directives :require, :use and :import (for importing Java

classes) that are alternatives to using their function forms. Using these is preferred over using

their function forms. Here, symbols become keywords, and quoting is no longer required.

(ns notes

(:use [clojure.contrib.math :only (gcd, sqrt)])

(:import (java.text NumberFormat) (javax.swing JFrame JLabel)))

(println (gcd 27 81)) ; => 27

(println (sqrt 7)) ; => 2.6457513110645907

(println (.format (NumberFormat/getInstance) Math/PI)) ; => 3.142

; the code below requires you to have knowledge of the Java programming language

; also see the generated image below

(doto (JFrame. "Hello")

(.add (JLabel. "Hello, World!"))

(.pack)

(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)

(.setVisible true))

Page 53: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 53

More on Bindings

An example:

(def cnt 1) ; cnt is a global binding

(defn fn1

[]

(println "Global binding value of fn1 = " cnt))

(fn1) ; Global binding value of fn1 = 1

let

The let special form creates bindings that are local to that form. Its first argument is a vector

containing name/expression pairs. The expressions are evaluated in order (i.e. let does its

bindings sequentially) and their results are assigned to the names on their left. These

bindings can be used in the expressions that follow them in the vector. They can also be

assigned to more than once to change their value. The remaining arguments to let comprise

the body which is a set of expressions to be evaluated with the new bindings in scope.

Functions that are called in the body cannot see the local bindings created by let. Do check-

out the let documentation.

http://clojure.org/special_forms#let

Page 54: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 54

An example:

(def x 1)

(def y 1)

(let [x 2 y x]

(+ x y)) ; => 4

x ; => 1

y ; => 1

Another example:

(def cnt 1) ; cnt is a global binding

(defn fn1

[]

(println "Global binding value in fn1 = " cnt))

(defn fn2

[]

(println "fn2: before let cnt (still global binding) =" cnt) ; fn2: before let cnt

(still global binding) = 1

(let [cnt 2] ; creates local binding cnt that shadows global one

(println "fn2: in let, cnt (now local binding) =" cnt) ; fn2: in let, cnt

(now local binding) = 2

(fn1)) ; Global binding value in fn1 = 1

(println "fn2: after let cnt (back to global binding) =" cnt)) ; fn2: after let cnt

(back to global binding) = 1

(fn2)

binding

The binding special form is similar to let, but it temporarily gives new, thread-local values to

existing global bindings. The new values are seen inside that form and also in functions called

from inside it. When the binding form exits, the bindings revert to their previous values.

An example:

Observe below that binding does its bindings in parallel.

(def x 1)

(def y 1)

(binding [x 2 y x]

(+ x y)) ; => 3

x ; => 1

y ; => 1

Page 55: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 55

Another example:

(def cnt 1) ; cnt is a global binding

(defn fn1

[]

(println "Global binding value in fn1 = " cnt))

(defn fn3

[]

(println "fn3: before binding cnt (still global binding) =" cnt)

; fn3: before binding cnt (still global binding) = 1

(binding [cnt 3]

; same global binding with new, temporary value

(println "fn3: in binding, cnt (global binding value temporarily changed) =" cnt)

; fn3: in binding, cnt (global binding value temporarily changed) = 3

(fn1))

; Global binding value in fn1 = 3

(println "fn3: after binding cnt (value of global binding reverted back) =" cnt))

; fn3: after binding cnt (value of global binding reverted back) = 1

(fn3)

Symbols that are intended to be bound to new, thread-local values using binding have their

own naming convention. These special symbols have names that begin and end with an

asterisk. People sometimes refer to the asterisks as "earmuffs".

For example, Clojure uses dynamic binding for thread-wide options such as the standard I/O

streams *in*, *out* and *err*. The predefined, special symbols *in*, *out* and *err* are set

to stdin, stdout and stderr by default. Functions that use these bindings are affected by their

values. For example, binding a new value to *out* changes the output destination of the

println function.

Guideline: As far as possible use let. Use binding when you need a thread-local version of a

var.

Echo Server

We shall build a simple server that accepts network connections and simply echoes back

whatever was sent.

Port

A port is not a physical device, but an abstraction to facilitate communication between a

server and a client.

Page 56: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 56

Ports are described by a 16-bit integer value. Hence, a machine can have a maximum of

65536 port numbers (ranging from 0 to 65535). The port numbers are divided into three

ranges: the Well Known Ports, the Registered Ports, and the Dynamic and/or Private Ports.

The Well Known Ports are those from 0 through 1023 (for example, port no. 80 is for http,

port no. 25 is for smtp and so on). The Registered Ports are those from 1024 through 49151.

The Dynamic and/or Private Ports are those from 49152 through 65535.

Socket

A socket is not a physical device but an abstraction. It represents a single connection between

two network applications. These two applications normally run on different computers, but

sockets can also be used for inter-process communication on a single computer. Applications

can create multiple sockets for communicating with each other. Sockets are bidirectional,

meaning that either side of the connection is capable of both sending and receiving data.

clojure-contrib library

The user contributions library, clojure.contrib, is a collection of namespaces each of which

implements features that may be useful to a large part of the Clojure community. Some parts

of clojure.contrib may migrate into clojure.core if they prove to be so generally useful.

See the clojure.contrib API here - http://richhickey.github.com/clojure-contrib/index.html.

server-socket API

To build this simple server, we shall make use of the server-socket API from the

clojure.contrib library. We shall use the following from this API:

http://richhickey.github.com/clojure-contrib/server-socket-api.html

(ns your-namespace

(:require clojure.contrib.server-socket))

(create-server port fun)

The above code creates a server socket on port. When accepting a connection, a new thread

is created which calls:

(fun input-stream output-stream)

Page 57: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 57

duck-streams API

We shall also make use of the duck-streams API from the clojure.contrib library. We shall

use the following from this API:

http://richhickey.github.com/clojure-contrib/duck-streams-api.html

(ns your-namespace

(:require clojure.contrib.duck-streams))

This library defines "duck-typed" I/O utility functions for Clojure. The 'reader' and 'writer'

functions will open and return an instance of java.io.BufferedReader and

java.io.PrintWriter, respectively, for a variety of argument types - filenames as strings,

URLs, java.io.File's, etc. 'reader' even works on HTTP URLs.

spit function

Let’s make a little diversion. We shall have a look at the spit function from the

clojure.contrib library. This function is not used in the Echo Server.

Usage:

(spit f content)

Opens f with writer, writes content, then closes f.

Here's an example:

(use '[clojure.contrib.duck-streams :only (spit)])

(spit "rubylearning.out" "Hello RubyLearning participants.")

You should now find a file at rubylearning.out with contents Hello RubyLearning

participants.

Step 1: Define a namespace

(ns echo

(:use [clojure.contrib server-socket duck-streams]))

Page 58: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 58

Step 2: Define a port

(def port 2222)

Our server socket would be listening on this port defined by the port variable.

Step 3: Define a function

Our function handle-client takes two arguments named in and out. In this case it will be

streams attached to the client's socket.

binding binds names to new values temporarily. in and out are the default streams that the

standard IO functions read from (stdin) and write to (stdout). But within the body of this

binding block, they will now refer to readers and writers for the client socket. This way we

can use functions like read-line and println to communicate with the client. In fact, in our

block we read a line and print it out. loop begins the loop and recur returns to the start of

the loop.

(defn handle-client [in out]

(binding [*in* (reader in)

*out* (writer out)]

(loop []

(println (read-line))

(recur))))

Step 4: Start the server

To start the server, type the following in your REPL:

(def server (create-server port handle-client))

Finally we call create-server with two arguments - the port number and the function we

want to handle the client connections. We keep this in the server var. A lot of the

concurrency issues are handled by Clojure inside the create-server function. We don't have to

worry about firing-up threads or doing other kinds of listeners. It will handle that under the

covers. So that's it for a simple echo server. Our echo server is running at port 2222 on

localhost.

Page 59: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 59

Here's the complete code of our Echo Server:

(ns echo

(:use [clojure.contrib server-socket duck-streams]))

(def port 2222)

(defn handle-client [in out]

(binding [*in* (reader in)

*out* (writer out)]

(loop []

(println (read-line))

(recur))))

(def server (create-server port handle-client))

Step 5: Test the Echo Server

Open a command window and use Telnet to connect to our Echo Server:

telnet localhost 2222

Now anything I type in will be echoed back.

StructMaps

The defstruct macro is used to define a StructMap:

(defstruct account-struct :name :balance)

The structure field names of type keyword or symbols are automatically usable as functions to

access fields of the structure. This is possible as structures are maps and this feature is

supported by maps. This is not possible for other types of field names such as strings or

numbers. It is quite common to use keywords for field names for structures due to the above

reason.

The object returned by defstruct is what is called the structure basis. This is not a structure

instance but contains information of what the structure instances should look like.

Page 60: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 60

New instances of a given StructMap are created using struct. Values must be specified in the

same order as their corresponding keys were specified when the StructMap was defined.

Values for keys at the end can be omitted and their values will be nil:

(def account (struct account-struct "Michael" 5000))

account ; => {:name "Michael", :balance 5000}

(:name account) ; => "Michael"

New keys not specified when the StructMap was defined can be added to instances. However,

keys specified when the StructMap was defined cannot be removed from instances:

(def account-sm (struct-map account-struct :title "programmer" :name "Daniel"

:balance 4000))

account-sm ; => {:name "Daniel", :balance 4000, :title "programmer"}

As structures are maps, new fields can also be added to structure instances using assoc.

account ; => {:name "Michael", :balance 5000}

(def account-new (assoc account :title "programmer")) ; => {:name "Michael", :balance

5000, :title "programmer"}

(:title account-new) ; => "programmer"

assoc can also be used to "update" a structure: account ; => {:name "Michael", :balance 5000}

(assoc account :balance 6000) ; => {:name "Michael", :balance 6000}

account ; => {:name "Michael", :balance 5000}

; note that 'account' is immutable and did not change

dissoc can be used to remove these instance specific keys. Note however that struct base

keys cannot be removed.

account-new ; => {:name "Michael", :balance 5000, :title :address}

(def account-new2 (dissoc account-new :title)) ; this works as :title are instance

specific keys

account-new2 ; => {:name "Michael", :balance 5000}

(dissoc account-new2 :balance) ; this fails. base keys cannot be dissociated

; => java.lang.Exception: Can't remove struct key

Page 61: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 61

Refs

Most objects in Clojure are immutable. When you really want mutable data, you must be

explicit about it, such as by creating a mutable reference (ref) to an immutable object. You

create a ref with this:

(ref initial-state)

For example, you could create a reference to your current residential address:

(def current-addr (ref "Vasant Villa, 759/43 Deccan Gym., Pune"))

The ref wraps and protects access to its internal state. To read the contents of the reference,

you can call deref:

(deref current-addr) ; => "Vasant Villa, 759/43 Deccan Gym., Pune"

The deref function can be shortened to the @ reader macro:

@current-addr ; => "Vasant Villa, 759/43 Deccan Gym., Pune"

An address is an immutable entity. It doesn't change into another address when you move to a

new address. But your current address is a reference to an entity, and it can change.

In Rich Hickey's words - "In languages like Java and C++ there is a conflation of the concept of

an identity with its state, which is a value. Clojure deals primarily with values. Refs are a way

to create identities, which can point to different values (have different states), over time. As

such, unlike the private state of a Java object, the value of a ref is not ‘owned by’ the ref."

Transactions

Because refs are mutable, you must protect their updates. In many languages, you would use

a lock for this purpose. In Clojure, you can use a transaction (STM). Refs can only be modified

inside a transaction. Transactions are wrapped in a dosync. You can change where a

reference points with ref-set.

http://en.wikipedia.org/wiki/Software_transactional_memory

The dosync macro starts a transaction that continues while the expressions in its body are

evaluated. The ref-set function changes the in-transaction value of a ref and returns it. It

must be called inside a transaction, otherwise an IllegalStateException is thrown. The change

Page 62: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 62

will only be visible outside the transaction if and when the transaction commits. This happens

when a dosync exits without an exception being thrown:

(ref-set current-addr "13 Kanchan-Shri, Shirole Road, Pune") ; =>

java.lang.IllegalStateException

; The current-addr reference will now refer to a different address

(dosync

(ref-set current-addr "13 Kanchan-Shri, Shirole Road, Pune")) ; => "13 Kanchan-Shri,

Shirole Road, Pune"

@current-addr ; => "13 Kanchan-Shri, Shirole Road, Pune"

The alter function is used for changes that must be made in a specific order. The commute

function is used for changes whose order is not important and can be performed in parallel.

Both functions must be called inside a transaction. (dosync (alter current-addr (fn [_] "13 Kanchan-Shri, Shirole Road, Pune")))

; => "13 Kanchan-Shri, Shirole Road, Pune"

Note: In the example above we have used an underscore (_), which is used as a placeholder

for function parameters that won't be used and therefore don't need a name.

Another example: Suppose we want to add one to the value of a ref named counter. This

could be implemented as follows, using inc for the update function:

(def counter (ref 0))

(dosync (alter counter inc)) ; => 1

Note:

We could have used any other function (even our own) in place of inc.

The function passed to alter function takes the current value of the ref as the first

argument and must not have side effects as it may be called more than once.

Page 63: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 63

A Simple Accounts example

The following example by Baishampayan Ghose involves a simple account and its transactions.

The comment form in the example explains the usage of this program:

;;; *acc is a temporary var to store the instance

(ns accounts) ; Change the default namespace to accounts

; Create a structure basis called acct-struct

(defstruct acct-struct :name :balance)

(defn make-account

"Create a new account"

[acct-name op-balance]

;; op-balance should be stored in a ref so that we can modify it in

;; a concurrent manner

;; Use struct to create a new instance of StructMap

(struct acct-struct acct-name (ref op-balance)))

(defn account-balance

"Get the balance from an account"

[acc]

;; read the contents of the reference with @

@(:balance acc))

(defn deposit

"Deposit amount into acc"

[acc amount]

(dosync

(commute (:balance acc) + amount)))

(def *min-bal* 500)

(defn withdraw

"Withdraw amount from acc. Minimum balance is 500"

[acc amount]

(dosync

(let [curr (:balance acc)]

(if (> (- @curr amount) *min-bal*)

(alter curr - amount)

:insufficient-funds))))

(comment

(def *acc (make-account "Satish Talim" 5000))

(deposit *acc 500)

(account-balance *acc)

(withdraw *acc 20)

(account-balance *acc)

(binding [*min-bal* 0]

(withdraw *acc 5000)))

Page 64: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 64

Compiling using Clojure Box

When Clojure source files are executed as scripts, they are compiled to Java bytecode at

runtime. These .class files can be used in Java applications.

Shawn Hoover, the creator of Clojure Box has this to say:

"I made Clojure Box mainly so people would have a quick and easy REPL. Of course all of

Emacs is there and people will want to use it. Unfortunately swank-clojure is not as refined

for compilation and running projects like Enclojure might be (I would actually recommend

Enclojure over Clojure Box for beginners these days).

The bottom line is you can achieve what you want, but it takes some manual steps. Note that

M-x means to type Alt-x. C-x is Control-x. C-w is Control-w. M-x allows you to execute a

command by typing a command name. After typing M-x, you are presented with a prompt to

type a command name - make-directory, cd, swank-clojure-project, and shell are commands

you can type at that prompt. Some commands then present an additional prompt related to

the command."

Let us write a test.clj Clojure program as follows:

Select a namespace for the source files to be compiled, for example, test.test.

Make one of the source files have the same name as the last part of the namespace.

We'll call this the main source file. For example, test.clj.

Create a directory structure that works with swank-clojure-project of Clojure Box.

Open a command window and create the following directory structure

c:\users\talim\myproject\src\test and c:\users\talim\myproject\classes. Close this

command window.

The folder c:\users\talim\myproject\src\test will store the source file namely

test.clj.

Specify the namespace at the top of the main source file and include the :gen-class

namespace directive. For example: (ns test.test (:gen-class)). Clojure has a stand-

alone utility for generating Java classes in the gen-class macro. When code containing

these calls is compiled, it generates byte-code for the specified classes and writes

them into class files.

In the main source file, use the load function to load all the other source files in the

same namespace with relative paths. In our case we do not have any other source file

apart from test.clj.

http://richhickey.github.com/clojure/clojure.core-api.html#clojure.core/load

In each of the other source files, use the in-ns function (explained in the next section)

to set their namespace.

Page 65: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 65

Here's the code for the test.clj program:

(ns test.test (:gen-class))

(defn -main

([greetee]

(println (str "Hello " greetee "!")))

([] (-main "world")))

Now at the REPL, type the following:

M-x cd c:\users\talim\myproject

So the above "M-x cd c:\users\talim\myproject" means:

Type Alt-x

At the prompt type cd ENTER

At the next prompt type c:\users\talim\myproject and ENTER

Next type:

M-x swank-clojure-project c:\users\talim\myproject

So the above "M-x swank-clojure-project c:\users\talim\myproject" means:

Type Alt-x

At the prompt type swank-clojure-project ENTER

At the next prompt (Project root:) type the name of the directory that contains

src\test\test.clj i.e. c:\users\talim\myproject and ENTER.

You probably already have a slime repl running, so it will say "Buffer has a running process;

kill it? (yes or no)". Type yes to kill the existing repl and start a new one that has the

classpath set up based on your project directory structure (the classpath must have the

clojure.jar installed with Clojure Box, your src directory, and your classes directory for

compilation to work--these things are all set up automatically by the swank-clojure-project

function).

Page 66: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 66

At the REPL, type:

(compile 'test.test)

; => test.test

To execute the program, type the following at the REPL:

M-x shell ENTER

At the REPL, you will see: c:\users\talim\myproject.

A separate .class file is produced for each function. They are written under the "classes"

directory in a directory structure that corresponds to their namespace. If the compiled

namespace has a function named -main, it can be run as a Java application. Command-line

arguments are passed as arguments to that function.

At the REPL type:

java -cp "c:\Program Files\Clojure Box\lib\clojure.jar";classes test.test

; => Hello world!

That should work.

Page 67: Clojure forbeginners

Prepared exclusively for Clojure 101 Course participants

@ 2010 by Satish Talim Page 67

About the Author

Satish Talim (http://satishtalim.com/) is a senior software consultant based in Pune, India with over 30+ years of I.T. experience. His experience lies in developing and executing business for high technology and manufacturing industry customers. Personally his strengths lie in Business Development and Business Networking apart from new product and solution ideas. Good experience of organization development. Excellent cross disciplinary background in engineering, computer science and management.

He -

Has helped start subsidiaries for many US based software companies like Infonox (http://www.infonox.com/default.shtml - based in San Jose, CA), Maybole Technologies Pvt. Ltd. (http://servient.com/ - Servient Inc. based in Houston, Texas) in Pune, India.

Has been associated with Java / J2EE since 1995 and involved with Ruby and Ruby on Rails since 2005.

also started and manages two very active Java and Ruby User Groups in Pune, India – PuneJava

http://tech.groups.yahoo.com/group/pune-java/

and PuneRuby

http://tech.groups.yahoo.com/group/puneruby/

Is a Ruby Mentor http://rubymentor.rubyforge.org/wiki/wiki.pl?AvailablePureRubyMentors

on rubyforge.org, helping people with Ruby programming.

He lives in Pune, India, with his wife, son and his Labrador Benzy. In his limited spare time he

enjoys travelling, photography and playing online chess.