Seven Languages in Seven Months
Language 6: Clojure
Raymond P. de Lacaze
04/29/13
Overview
• Part 1: Introduction to Clojure
Chapter 7 of Seven Languages in Seven Weeks
• Part 2: Advanced Topics
- Macros
- State Management
- Concurrency
- Multimethods
Clojure Introduction
• Clojure is a functional language
• Clojure is a dialect of Lisp
• Clojure runs on the JVM
• Clojure is a strong dynamically typed language
• Clojure uses parenthesized prefix notation
• Clojure encourages immutable objects
• Clojure provides Software Transactional Memory
• Clojure was invented by Rich Hickey
Functional Programming
• In the functional language paradigm,
programs are collections of functions.
• Functions are defined as a composition of
other functions
• All functions return values even those that are
used purely for their side effects like print
statements
• There is no distinction between statements
and function calls.
Higher Order Functions
• These are functions that take other functions
as arguments
• This is a very powerful data & control
abstraction paradigm
• One of the most common uses is to map a
function over a collection of values, essentially
invoking the function on each member of the
collection
Homoiconicity
• This is basically the idea of treating data and code as the
same
• Data structures in the language are used to represent
programs as well as data
• Lisp(s) arguably provides the most elegant and
indistinguishable implementation of this idea
• The source code of a Clojure function is actually a data
structure of type list
• This means that programs can construct and manipulate
code 
• We will see this in action when we talk about Macros
• The only way to do this in most languages is by
manipulating programs as strings of characters 
Installing Clojure
• Official Clojure Site
– http://clojure.org/
• Windows: The Clojure Box
– Easiest way to get up and running
– Downloads, installs & configures Emacs & Clojure
– https://github.com/devinus/clojure-box
• Mac OS X
– http://www.waratuman.com/2010/02/18/setting-up-clojure/
• Leiningen
– Manage Java/Clojure dependencies
– Manage Clojure projects
– http://leiningen.org/
Calling Basic Functions
• Clojure uses prefix notation
(<fn-name><arg1>…<argN>)
• Basic Examples:
user=> (+ 1 2 3)
6
user=> (+ 1 (* 2 3) 4)
11
user=> (+ "a" "b")
java.lang.ClassCastException:
java.lang.String cannot be cast to java.lang.Number (NO_SOURCE_FILE:0)
Strings and Characters
• Strings
– Simply enclosed in double-quotes with C-style escaping
user=> (println "This sentencenspans two lines")
This sentence
spans two lines
nil
– Use str to convert and concatenate things to strings
– If the underlying class of thing is a java class, then this will simply invoke Java
toString method
• Characters
– These are simply expressed by prefixing the character with a backslash
user=> (str c h a r a c t e r s)
"characters"
Boolean & Expressions
• Clojure uses the reserved symbol true & false
user=> (= (str a) "a")
true
user=> (= 1 2)
false
• In Clojure, primitive data types are as much as possible
aligned with the underlying JVM primitive data types
user=> (class true)
java.lang.Boolean
• The values false and nil are both false, every other value is true
Lists
• Lists are arguably the most used data structure in Lisp
which is actually an acronym of sorts for “List
Processing”
• Clojure uses parenthesized expressions to denote lists
• Clojure allows optional commas in lists to improve
readability and in that context commas=whitespace
• The list function is the basic list constructor
user=> (list 1 2 3)
(1 2 3)
user=> '(1 , 2 , 3)
(1 2 3)
Constructing and Manipulating Lists
user=> (def my-list '(1 2 3))
#'user/my-list
• Use first, last & rest to access components of a list
user=> (first my-list)
1
user=> (last my-list)
3
user=> (rest my-list)
(2 3)
• Use cons and conj to add elements to beginning a list
user=> (cons 4 my-list)
(4 1 2 3)
user=> (conj my-list 4)
(4 1 2 3)
• Use concat to append any number of lists together or into for 2 lists
user=> (concat my-list my-list)
(1 2 3 1 2 3)
Vectors & Sets
• Use square brackets to denote vectors
user=> (class [1 2 3])
clojure.lang.PersistentVector
• Use #curly-brackets to denote sets
user=> (class #{:peter :paul :mary})
clojure.lang.PersistentHashSet
• Use sorted-set to create a sorted set.
user=> (class (sorted-set #{:peter :paul :mary}))
clojure.lang.PersistentTreeSet
Maps
• Maps are key-value pairs
• Use curly-brackets to denote maps
user=> (def my-map {1 "one", 2 "two", 3 "three"})
#'user/my-map
• Maps are also functions
user=> (my-map 2)
"two”
• Keywords are also functions
user=> (def my-other-map {:one 1 :two 2 :three 3})
#'user/my-other-map
user=> (:two my-other-map)
2
Defining Functions
• Use defn to define functions
user=> (defn verbose-sum [x y]
(let [sum (+ x y)]
(println (str "The sum of " x " and " y " is " sum))
sum)
#'user/verbose-sum
user=> (verbose-sum 2 3)
The sum of 2 and 3 is 5
5
Destructuring Bindings
• Destructuring supported in both function parameters and let statements
user=> (def board [[:x :o :x][:o :x :o][:o :x :o]])
#'user/board
user=> (defn center [[[a1 a2 a3][b1 b2 b3][c1 c2 c3]]] b2)
#'user/center
user=> (center board)
:x
user=> (defn center [[_ [_ c _] _]] c)
#'user/center
user=> (center board)
:x
Anonymous Functions
• Use fn or # to specify an anonymous function
user=> (map (fn [x] (* 2 x)) '(1 2 3 4 5))
(2 4 6 8 10)
user=> (map #(* 2 %) '(1 2 3 4 5))
(2 4 6 8 10)
• map, apply & filter
– These are three higher order functions that you may frequently use
anonymous functions with
user=> (filter #(< % 4) '(1 2 3 4 5))
(1 2 3)
user=> (apply max (map #(mod % 3) '(1 2 3 4 5 6 7 8 9 10)))
2
user=> (map (fn [x y](+ x y)) '(1 2 3 4 5) '(5 4 3 2 1))
(6 6 6 6 6)
Recursion
• Clojure supports direct recursion but the JVM
does not provide tail-optimization.
• Clojure provides loop & recur to essentially
provide tail-optimized recursion
Sequences
• A sequence Implementation-independent
abstraction over collection types.
• If your data-type supports first, rest and cons
you can wrap it in a sequence.
• Lists, Vectors, Sets and Maps are all Sequences
Common Sequence Operations
• These are all higher order functions
• Existential Functions (Tests)
– every?, some, not-every? and not-any?
user=> (every? odd? '(1 2 3 4 5))
false
user=> (not-every? odd? '(1 2 3 4 5))
True
• Sequence Manipulation
– map, filter, apply, for comprehensions, reduce
Lazy Evaluation
• Use range to generate finite Sequences
user=> (range 1 10)
(1 2 3 4 5 6 7 8 9)
• Infinite Sequences
– take: Returns the first n elements of a sequence
– repeat: Creates an infinite sequence of 1 repeating element
– drop: Drops the first n elements from a sequence
– cycle: Repeats elements in a sequence
– interpose: interpose one element between elements
– interleave: interleaves two infinite sequence
– iterate: applies a function accumlatively to successive elements
Welcome to the middle
of the presentation
Let’s Take a Break!
State Management
• Clojure advocates eliminating state
• Cleanliness, Protection & Parallelization
• But, the real world has state
– e.g. bank accounts
• Problems with locks
– Hard to manage correctly
– Overuse of locks tends to limit concurrency
State and Identity
• Every thing consists of state and identity
• State is immutable
• Identity links states over time
• State can be any Clojure data type
• Identifies are modeled using reference types
– Refs: Used for synchronous, coordinated state
– Agents: Used for asynchronous, independent state
– Atoms: Used for synchronous, independent state
References
• Use ref to create a ref and deref to dereference it
user=> (def my-ref (ref 5))
#'user/my-ref
user=> (deref my-ref)
5
user=> @my-ref
5
• Use ref-set and alter to update a ref within a transaction
• Use dosync to specify a transactional atomic operation
Transactional Memory
• Bank Accounts in STM
user=> (def account1 (ref 1000))
#'user/account1
user=> (def account2 (ref 1500))
#'user/account2
user=> (defn transfer [a1 a2 amount]
(dosync
(alter a1 - amount)
(alter a2 + amount)))
#'user/transfer
user=> (transfer account1 account2 250)
1750
user=> @account1
750
user=> @account2
1750
Atoms
• Use atom to create an atom type reference
• Use de-ref and @ to deference it
• Use swap! and reset! to update it
user=> (def my-atom (atom 5))
#'user/my-atom
user=> (swap! my-atom + 3)
8
user=> (reset! my-atom 1)
1
user=> @my-atom
1
• Several threads updating a guest list, i.e. independent state
Agents
• Use agent to create an agent type ref
• Use send to request an update
• Update semantics
– Actions to same agent applied serially
– Multiple actions to same agent preserve order
– Action-generating actions are not called until the
action completes
– Actions within STM transactions are not called until
the transaction completes
• Concurrency functions: pmap, pvalues & pcalls
State Management Summary
• Use refs for coordinated synchronous updates
• Use atoms for independent synchronous updates
• Use agents for asynchronous updates and
concurrency
• Vars maintain state within a thread
• Validator functions help with data integrity
• Watches trigger events based on identity values
Macros
• Macros expand into code and a macro definition
specifies the code expansion.
• Think of macros as a mechanism that allows you to add
features to a language rather than within a language.
• Macros don’t evaluate their arguments
• Macros are extremely powerful and should be used
with care
• Use defmacro to define macros
• Use macroexpand to debug macros
• Use `, ~ and ~@ to facilitate code generation
• Use “auto-gensym” or gensym to define local vars
Why Macros?
We want add a new language feature:
(unless <test> <expr>)
Using a function we could do:
user=> (defn unless [test expr]
(if test nil expr))
#'user/unless
user=> (unless false (println "This should print."))
This should print.
nil
user=> (unless true (println "This should not print"))
This should not print
nil
Uh oh! The function unless evaluates <expr> regardless of <test>!
A Macro Example
user=> (defmacro unless [test expr](list 'if test nil expr))
#'user/unless
user=> (unless false (println "This should print."))
This should print.
nil
;; This now behaves correctly
user=> (unless true (println "This should not print"))
nil
Macros (cont.)
• Syntax Quote: `
• Unquote: ~
• Splicing Unquote: ~@
;; Old Definition
user=> (defmacro unless [test expr](list 'if test nil expr))
#'user/unless
;; New Definition
user=> (defmacro unless [test expr] `(if ~test nil ~expr))
#'user/unless
Side Effect Safety
• When you define a macro, it is unsafe to evaluate the arguments more
than once in the event that they have side effects.
• Clojure provides auto-gensym and gensym to allow you to define local
variables in safe way and also avoid incorrectly repeated side-effects
;; This is bad
user=> (defmacro unless [test expr]
`(let []
~expr
(if ~test nil ~expr)))
#'user/unless
user=> (unless false (println "foo"))
foo
foo
nil
;; This is good
user=> (defmacro unless [test expr]
`(let [result# ~expr]
result#
(if ~test nil result#))
#'user/unless
user=> (unless false (println "foo"))
foo
nil
Datatypes & Protocols
• Roughly analogous to classes & interfaces Java
but more flexible
• Protocols are sets of methods
• Protocols are defined with defprotocol
• Datatypes are named record types, with a set
of named fields, that can implement records
and interfaces.
• Datatypes are defined with defrecord
Other Topics
• Multimethods
• Java Integration
• Futures & Promises

Clojure 7-Languages

  • 1.
    Seven Languages inSeven Months Language 6: Clojure Raymond P. de Lacaze 04/29/13
  • 2.
    Overview • Part 1:Introduction to Clojure Chapter 7 of Seven Languages in Seven Weeks • Part 2: Advanced Topics - Macros - State Management - Concurrency - Multimethods
  • 3.
    Clojure Introduction • Clojureis a functional language • Clojure is a dialect of Lisp • Clojure runs on the JVM • Clojure is a strong dynamically typed language • Clojure uses parenthesized prefix notation • Clojure encourages immutable objects • Clojure provides Software Transactional Memory • Clojure was invented by Rich Hickey
  • 4.
    Functional Programming • Inthe functional language paradigm, programs are collections of functions. • Functions are defined as a composition of other functions • All functions return values even those that are used purely for their side effects like print statements • There is no distinction between statements and function calls.
  • 5.
    Higher Order Functions •These are functions that take other functions as arguments • This is a very powerful data & control abstraction paradigm • One of the most common uses is to map a function over a collection of values, essentially invoking the function on each member of the collection
  • 6.
    Homoiconicity • This isbasically the idea of treating data and code as the same • Data structures in the language are used to represent programs as well as data • Lisp(s) arguably provides the most elegant and indistinguishable implementation of this idea • The source code of a Clojure function is actually a data structure of type list • This means that programs can construct and manipulate code  • We will see this in action when we talk about Macros • The only way to do this in most languages is by manipulating programs as strings of characters 
  • 7.
    Installing Clojure • OfficialClojure Site – http://clojure.org/ • Windows: The Clojure Box – Easiest way to get up and running – Downloads, installs & configures Emacs & Clojure – https://github.com/devinus/clojure-box • Mac OS X – http://www.waratuman.com/2010/02/18/setting-up-clojure/ • Leiningen – Manage Java/Clojure dependencies – Manage Clojure projects – http://leiningen.org/
  • 8.
    Calling Basic Functions •Clojure uses prefix notation (<fn-name><arg1>…<argN>) • Basic Examples: user=> (+ 1 2 3) 6 user=> (+ 1 (* 2 3) 4) 11 user=> (+ "a" "b") java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number (NO_SOURCE_FILE:0)
  • 9.
    Strings and Characters •Strings – Simply enclosed in double-quotes with C-style escaping user=> (println "This sentencenspans two lines") This sentence spans two lines nil – Use str to convert and concatenate things to strings – If the underlying class of thing is a java class, then this will simply invoke Java toString method • Characters – These are simply expressed by prefixing the character with a backslash user=> (str c h a r a c t e r s) "characters"
  • 10.
    Boolean & Expressions •Clojure uses the reserved symbol true & false user=> (= (str a) "a") true user=> (= 1 2) false • In Clojure, primitive data types are as much as possible aligned with the underlying JVM primitive data types user=> (class true) java.lang.Boolean • The values false and nil are both false, every other value is true
  • 11.
    Lists • Lists arearguably the most used data structure in Lisp which is actually an acronym of sorts for “List Processing” • Clojure uses parenthesized expressions to denote lists • Clojure allows optional commas in lists to improve readability and in that context commas=whitespace • The list function is the basic list constructor user=> (list 1 2 3) (1 2 3) user=> '(1 , 2 , 3) (1 2 3)
  • 12.
    Constructing and ManipulatingLists user=> (def my-list '(1 2 3)) #'user/my-list • Use first, last & rest to access components of a list user=> (first my-list) 1 user=> (last my-list) 3 user=> (rest my-list) (2 3) • Use cons and conj to add elements to beginning a list user=> (cons 4 my-list) (4 1 2 3) user=> (conj my-list 4) (4 1 2 3) • Use concat to append any number of lists together or into for 2 lists user=> (concat my-list my-list) (1 2 3 1 2 3)
  • 13.
    Vectors & Sets •Use square brackets to denote vectors user=> (class [1 2 3]) clojure.lang.PersistentVector • Use #curly-brackets to denote sets user=> (class #{:peter :paul :mary}) clojure.lang.PersistentHashSet • Use sorted-set to create a sorted set. user=> (class (sorted-set #{:peter :paul :mary})) clojure.lang.PersistentTreeSet
  • 14.
    Maps • Maps arekey-value pairs • Use curly-brackets to denote maps user=> (def my-map {1 "one", 2 "two", 3 "three"}) #'user/my-map • Maps are also functions user=> (my-map 2) "two” • Keywords are also functions user=> (def my-other-map {:one 1 :two 2 :three 3}) #'user/my-other-map user=> (:two my-other-map) 2
  • 15.
    Defining Functions • Usedefn to define functions user=> (defn verbose-sum [x y] (let [sum (+ x y)] (println (str "The sum of " x " and " y " is " sum)) sum) #'user/verbose-sum user=> (verbose-sum 2 3) The sum of 2 and 3 is 5 5
  • 16.
    Destructuring Bindings • Destructuringsupported in both function parameters and let statements user=> (def board [[:x :o :x][:o :x :o][:o :x :o]]) #'user/board user=> (defn center [[[a1 a2 a3][b1 b2 b3][c1 c2 c3]]] b2) #'user/center user=> (center board) :x user=> (defn center [[_ [_ c _] _]] c) #'user/center user=> (center board) :x
  • 17.
    Anonymous Functions • Usefn or # to specify an anonymous function user=> (map (fn [x] (* 2 x)) '(1 2 3 4 5)) (2 4 6 8 10) user=> (map #(* 2 %) '(1 2 3 4 5)) (2 4 6 8 10) • map, apply & filter – These are three higher order functions that you may frequently use anonymous functions with user=> (filter #(< % 4) '(1 2 3 4 5)) (1 2 3) user=> (apply max (map #(mod % 3) '(1 2 3 4 5 6 7 8 9 10))) 2 user=> (map (fn [x y](+ x y)) '(1 2 3 4 5) '(5 4 3 2 1)) (6 6 6 6 6)
  • 18.
    Recursion • Clojure supportsdirect recursion but the JVM does not provide tail-optimization. • Clojure provides loop & recur to essentially provide tail-optimized recursion
  • 19.
    Sequences • A sequenceImplementation-independent abstraction over collection types. • If your data-type supports first, rest and cons you can wrap it in a sequence. • Lists, Vectors, Sets and Maps are all Sequences
  • 20.
    Common Sequence Operations •These are all higher order functions • Existential Functions (Tests) – every?, some, not-every? and not-any? user=> (every? odd? '(1 2 3 4 5)) false user=> (not-every? odd? '(1 2 3 4 5)) True • Sequence Manipulation – map, filter, apply, for comprehensions, reduce
  • 21.
    Lazy Evaluation • Userange to generate finite Sequences user=> (range 1 10) (1 2 3 4 5 6 7 8 9) • Infinite Sequences – take: Returns the first n elements of a sequence – repeat: Creates an infinite sequence of 1 repeating element – drop: Drops the first n elements from a sequence – cycle: Repeats elements in a sequence – interpose: interpose one element between elements – interleave: interleaves two infinite sequence – iterate: applies a function accumlatively to successive elements
  • 22.
    Welcome to themiddle of the presentation Let’s Take a Break!
  • 23.
    State Management • Clojureadvocates eliminating state • Cleanliness, Protection & Parallelization • But, the real world has state – e.g. bank accounts • Problems with locks – Hard to manage correctly – Overuse of locks tends to limit concurrency
  • 24.
    State and Identity •Every thing consists of state and identity • State is immutable • Identity links states over time • State can be any Clojure data type • Identifies are modeled using reference types – Refs: Used for synchronous, coordinated state – Agents: Used for asynchronous, independent state – Atoms: Used for synchronous, independent state
  • 25.
    References • Use refto create a ref and deref to dereference it user=> (def my-ref (ref 5)) #'user/my-ref user=> (deref my-ref) 5 user=> @my-ref 5 • Use ref-set and alter to update a ref within a transaction • Use dosync to specify a transactional atomic operation
  • 26.
    Transactional Memory • BankAccounts in STM user=> (def account1 (ref 1000)) #'user/account1 user=> (def account2 (ref 1500)) #'user/account2 user=> (defn transfer [a1 a2 amount] (dosync (alter a1 - amount) (alter a2 + amount))) #'user/transfer user=> (transfer account1 account2 250) 1750 user=> @account1 750 user=> @account2 1750
  • 27.
    Atoms • Use atomto create an atom type reference • Use de-ref and @ to deference it • Use swap! and reset! to update it user=> (def my-atom (atom 5)) #'user/my-atom user=> (swap! my-atom + 3) 8 user=> (reset! my-atom 1) 1 user=> @my-atom 1 • Several threads updating a guest list, i.e. independent state
  • 28.
    Agents • Use agentto create an agent type ref • Use send to request an update • Update semantics – Actions to same agent applied serially – Multiple actions to same agent preserve order – Action-generating actions are not called until the action completes – Actions within STM transactions are not called until the transaction completes • Concurrency functions: pmap, pvalues & pcalls
  • 29.
    State Management Summary •Use refs for coordinated synchronous updates • Use atoms for independent synchronous updates • Use agents for asynchronous updates and concurrency • Vars maintain state within a thread • Validator functions help with data integrity • Watches trigger events based on identity values
  • 30.
    Macros • Macros expandinto code and a macro definition specifies the code expansion. • Think of macros as a mechanism that allows you to add features to a language rather than within a language. • Macros don’t evaluate their arguments • Macros are extremely powerful and should be used with care • Use defmacro to define macros • Use macroexpand to debug macros • Use `, ~ and ~@ to facilitate code generation • Use “auto-gensym” or gensym to define local vars
  • 31.
    Why Macros? We wantadd a new language feature: (unless <test> <expr>) Using a function we could do: user=> (defn unless [test expr] (if test nil expr)) #'user/unless user=> (unless false (println "This should print.")) This should print. nil user=> (unless true (println "This should not print")) This should not print nil Uh oh! The function unless evaluates <expr> regardless of <test>!
  • 32.
    A Macro Example user=>(defmacro unless [test expr](list 'if test nil expr)) #'user/unless user=> (unless false (println "This should print.")) This should print. nil ;; This now behaves correctly user=> (unless true (println "This should not print")) nil
  • 33.
    Macros (cont.) • SyntaxQuote: ` • Unquote: ~ • Splicing Unquote: ~@ ;; Old Definition user=> (defmacro unless [test expr](list 'if test nil expr)) #'user/unless ;; New Definition user=> (defmacro unless [test expr] `(if ~test nil ~expr)) #'user/unless
  • 34.
    Side Effect Safety •When you define a macro, it is unsafe to evaluate the arguments more than once in the event that they have side effects. • Clojure provides auto-gensym and gensym to allow you to define local variables in safe way and also avoid incorrectly repeated side-effects ;; This is bad user=> (defmacro unless [test expr] `(let [] ~expr (if ~test nil ~expr))) #'user/unless user=> (unless false (println "foo")) foo foo nil ;; This is good user=> (defmacro unless [test expr] `(let [result# ~expr] result# (if ~test nil result#)) #'user/unless user=> (unless false (println "foo")) foo nil
  • 35.
    Datatypes & Protocols •Roughly analogous to classes & interfaces Java but more flexible • Protocols are sets of methods • Protocols are defined with defprotocol • Datatypes are named record types, with a set of named fields, that can implement records and interfaces. • Datatypes are defined with defrecord
  • 36.
    Other Topics • Multimethods •Java Integration • Futures & Promises