Professional Documents
Culture Documents
Page 2
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 @ 2010 by Satish Talim Page 3
Prepared exclusively for Clojure 101 Course participants 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 @ 2010 by Satish Talim Page 4
Prepared exclusively for Clojure 101 Course participants 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 @ 2010 by Satish Talim Page 5
Prepared exclusively for Clojure 101 Course participants 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 @ 2010 by Satish Talim Page 6
Prepared exclusively for Clojure 101 Course participants 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 7
Acknowledgements
My interest in Clojure was aroused when Michael Kohl organized the first-ever, free, fourweek, 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 Programming/Concepts
http://en.wikibooks.org/wiki/Clojure_Programming/Concepts
Page 8
Prepared exclusively for Clojure 101 Course participants Functional Programming with Clojure Screencast by PeepCode
http://peepcode.com/products/functional-programming-with-clojure
Page 9
Page 10
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 satish.talim@gmail.com.
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 11
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 @ 2010 by Satish Talim Page 12
Prepared exclusively for Clojure 101 Course participants 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 13
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. @ 2010 by Satish Talim Page 14
leiningen-1.1.0-standalone.jar
and
copy
it
to
the
folder
http://github.com/downloads/technomancy/leiningen/leiningen-1.1.0standalone.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-labrepld6b9759. 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 15
Prepared exclusively for Clojure 101 Course participants 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
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-evalprint 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 16
Prepared exclusively for Clojure 101 Course participants 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/postinstall.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 17
(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
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 18
Prepared exclusively for Clojure 101 Course participants 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. @ 2010 by Satish Talim Page 19
There are two ways in which you can comment in Clojure. A line comment is:
; This is a single line Clojure comment
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 (refer "src/inspector.clj" ) {:a [[1 1 2 :b 2 :c 5 [1 6][7 2 8 3 {:d 9][10 4 11 :e 5 :f [6 7 8]}]}) 'inspector) 3][4 12]])
(inspect-tree (inspect-table )
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 20
Prepared exclusively for Clojure 101 Course participants 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 21
Prepared exclusively for Clojure 101 Course participants 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 22
Prepared exclusively for Clojure 101 Course participants 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 Clojures 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 23
Prepared exclusively for Clojure 101 Course participants 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
Page 24
Prepared exclusively for Clojure 101 Course participants 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 1 (/ (/ 0.00001 0.00001M 1000000000000000000)) ; => 1.0 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 (class (class (* (* (* 100 9000 9000 100 9000 9000 100)) ; => java.lang.Integer 9000)) ; => java.lang.Long 9000 9000 9000 9000 9000 9000)) ; => java.math.BigInteger
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. @ 2010 by Satish Talim Page 25
Prepared exclusively for Clojure 101 Course participants 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"
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 26
Prepared exclusively for Clojure 101 Course participants 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 27
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 28
Prepared exclusively for Clojure 101 Course participants 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 29
Prepared exclusively for Clojure 101 Course participants 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"}
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 keys value, or it will return nil if the key is not found:
(:Clojure inventors) ; => Hickey (inventors :Java) ; => nil
Page 30
Prepared exclusively for Clojure 101 Course participants 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 31
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
Page 32
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)
Page 33
conj, into
(conj (into coll element & elements)
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 (into '(10 '(10 20 20 30) 30) :a) ; => (:a 10 20 30) '(:a :b :c)) ; => (:c :b :a 10 20 30)
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)
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. @ 2010 by Satish Talim Page 34
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
@ 2010 by Satish Talim Page 35
if, if-not Clojures 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 ; => yes (println (if 0 "Zero is true" "Zero is false")) ; => Zero is true (< 34 100) "yes" ))
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)
(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"
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 38
Prepared exclusively for Clojure 101 Course participants 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 39
Prepared exclusively for Clojure 101 Course participants 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] "people." ))
(println
(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
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 40
Prepared exclusively for Clojure 101 Course participants 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 41
Prepared exclusively for Clojure 101 Course participants 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 42
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 functions 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 >
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 43
Prepared exclusively for Clojure 101 Course participants 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 Clojures 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 44
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 '(java.text Random Locale)
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)
Page 45
Prepared exclusively for Clojure 101 Course participants 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))
Accessing methods Random also has a nextInt( ) method that takes an argument. You can call it by:
(import '(java.util Random))
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 objects 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 46
Prepared exclusively for Clojure 101 Course participants doto Sometimes you dont 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) "name" "Stuart") "blue")) "favoriteColor"
(.setProperty (.setProperty
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 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. 2. 3. 4. get a new Random get the next random integer cast it to a long pass the long to the Date constructor
Page 47
Prepared exclusively for Clojure 101 Course participants 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 48
Prepared exclusively for Clojure 101 Course participants Some More Examples
(.toUpperCase "hello") ; => HELLO (.length "hello") ; => 5
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, youll 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. @ 2010 by Satish Talim Page 49
You can use the generic inspect function to look at arbitrary objects:
(inspect (System/getProperties))
Page 50
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 @ 2010 by Satish Talim Page 51
Prepared exclusively for Clojure 101 Course participants 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 52
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 checkout the let documentation.
http://clojure.org/special_forms#let
Page 53
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) (still global binding) = 1 (let [cnt 2] (now local binding) = 2 (fn1)) (back to global binding) = 1 (fn2) ; Global binding value in fn1 = 1 (println "fn2: after let cnt (back to global binding) =" cnt)) ; fn2: after let cnt ; creates local binding cnt that shadows global one ; fn2: in let, cnt (println "fn2: in let, cnt (now local binding) =" cnt) ; fn2: before let cnt
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 54
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. @ 2010 by Satish Talim Page 55
Prepared exclusively for Clojure 101 Course participants 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
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 56
Prepared exclusively for Clojure 101 Course participants 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
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 Lets 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)
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 57
Prepared exclusively for Clojure 101 Course participants 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 58
Prepared exclusively for Clojure 101 Course participants 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
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 59
Prepared exclusively for Clojure 101 Course participants 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 :balance 4000)) account-sm ; => {:name "Daniel", :balance 4000, :title "programmer"} :title "programmer" :name "Daniel"
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"
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 60
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"
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 @ 2010 by Satish Talim Page 61
Prepared exclusively for Clojure 101 Course participants 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 62
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 63
Prepared exclusively for Clojure 101 Course participants 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 standalone 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. @ 2010 by Satish Talim Page 64
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 65
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!
Page 66
and PuneRuby
http://tech.groups.yahoo.com/group/puneruby/
Is a Ruby Mentor
http://rubymentor.rubyforge.org/wiki/wiki.pl?AvailablePureRubyMentors
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.
Page 67