Clojure 7-Languages


Published on

These are my slides from a mini Clojure tutorial presented at the "7 Languages in 7 Months" meetup group. The first part of the presentation faithfully presents material from Bruce Tate book, and the second part covers the more advanced topics of state management and macros

Published in: Technology
1 Like
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Clojure 7-Languages

  1. 1. Seven Languages in Seven MonthsLanguage 6: ClojureRaymond P. de Lacaze04/29/13
  2. 2. Overview• Part 1: Introduction to ClojureChapter 7 of Seven Languages in Seven Weeks• Part 2: Advanced Topics- Macros- State Management- Concurrency- Multimethods
  3. 3. 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
  4. 4. Functional Programming• In the functional language paradigm,programs are collections of functions.• Functions are defined as a composition ofother functions• All functions return values even those that areused purely for their side effects like printstatements• There is no distinction between statementsand function calls.
  5. 5. Higher Order Functions• These are functions that take other functionsas arguments• This is a very powerful data & controlabstraction paradigm• One of the most common uses is to map afunction over a collection of values, essentiallyinvoking the function on each member of thecollection
  6. 6. Homoiconicity• This is basically the idea of treating data and code as thesame• Data structures in the language are used to representprograms as well as data• Lisp(s) arguably provides the most elegant andindistinguishable implementation of this idea• The source code of a Clojure function is actually a datastructure of type list• This means that programs can construct and manipulatecode • We will see this in action when we talk about Macros• The only way to do this in most languages is bymanipulating programs as strings of characters 
  7. 7. Installing Clojure• Official Clojure Site–• Windows: The Clojure Box– Easiest way to get up and running– Downloads, installs & configures Emacs & Clojure–• Mac OS X–• Leiningen– Manage Java/Clojure dependencies– Manage Clojure projects–
  8. 8. Calling Basic Functions• Clojure uses prefix notation(<fn-name><arg1>…<argN>)• Basic Examples:user=> (+ 1 2 3)6user=> (+ 1 (* 2 3) 4)11user=> (+ "a" "b")java.lang.ClassCastException:java.lang.String cannot be cast to java.lang.Number (NO_SOURCE_FILE:0)
  9. 9. Strings and Characters• Strings– Simply enclosed in double-quotes with C-style escapinguser=> (println "This sentencenspans two lines")This sentencespans two linesnil– Use str to convert and concatenate things to strings– If the underlying class of thing is a java class, then this will simply invoke JavatoString method• Characters– These are simply expressed by prefixing the character with a backslashuser=> (str c h a r a c t e r s)"characters"
  10. 10. Boolean & Expressions• Clojure uses the reserved symbol true & falseuser=> (= (str a) "a")trueuser=> (= 1 2)false• In Clojure, primitive data types are as much as possiblealigned with the underlying JVM primitive data typesuser=> (class true)java.lang.Boolean• The values false and nil are both false, every other value is true
  11. 11. Lists• Lists are arguably the most used data structure in Lispwhich is actually an acronym of sorts for “ListProcessing”• Clojure uses parenthesized expressions to denote lists• Clojure allows optional commas in lists to improvereadability and in that context commas=whitespace• The list function is the basic list constructoruser=> (list 1 2 3)(1 2 3)user=> (1 , 2 , 3)(1 2 3)
  12. 12. Constructing and Manipulating Listsuser=> (def my-list (1 2 3))#user/my-list• Use first, last & rest to access components of a listuser=> (first my-list)1user=> (last my-list)3user=> (rest my-list)(2 3)• Use cons and conj to add elements to beginning a listuser=> (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 listsuser=> (concat my-list my-list)(1 2 3 1 2 3)
  13. 13. Vectors & Sets• Use square brackets to denote vectorsuser=> (class [1 2 3])clojure.lang.PersistentVector• Use #curly-brackets to denote setsuser=> (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. 14. Maps• Maps are key-value pairs• Use curly-brackets to denote mapsuser=> (def my-map {1 "one", 2 "two", 3 "three"})#user/my-map• Maps are also functionsuser=> (my-map 2)"two”• Keywords are also functionsuser=> (def my-other-map {:one 1 :two 2 :three 3})#user/my-other-mapuser=> (:two my-other-map)2
  15. 15. Defining Functions• Use defn to define functionsuser=> (defn verbose-sum [x y](let [sum (+ x y)](println (str "The sum of " x " and " y " is " sum))sum)#user/verbose-sumuser=> (verbose-sum 2 3)The sum of 2 and 3 is 55
  16. 16. Destructuring Bindings• Destructuring supported in both function parameters and let statementsuser=> (def board [[:x :o :x][:o :x :o][:o :x :o]])#user/boarduser=> (defn center [[[a1 a2 a3][b1 b2 b3][c1 c2 c3]]] b2)#user/centeruser=> (center board):xuser=> (defn center [[_ [_ c _] _]] c)#user/centeruser=> (center board):x
  17. 17. Anonymous Functions• Use fn or # to specify an anonymous functionuser=> (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 useanonymous functions withuser=> (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)))2user=> (map (fn [x y](+ x y)) (1 2 3 4 5) (5 4 3 2 1))(6 6 6 6 6)
  18. 18. Recursion• Clojure supports direct recursion but the JVMdoes not provide tail-optimization.• Clojure provides loop & recur to essentiallyprovide tail-optimized recursion
  19. 19. Sequences• A sequence Implementation-independentabstraction over collection types.• If your data-type supports first, rest and consyou can wrap it in a sequence.• Lists, Vectors, Sets and Maps are all Sequences
  20. 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))falseuser=> (not-every? odd? (1 2 3 4 5))True• Sequence Manipulation– map, filter, apply, for comprehensions, reduce
  21. 21. Lazy Evaluation• Use range to generate finite Sequencesuser=> (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. 22. Welcome to the middleof the presentationLet’s Take a Break!
  23. 23. 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
  24. 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. 25. References• Use ref to create a ref and deref to dereference ituser=> (def my-ref (ref 5))#user/my-refuser=> (deref my-ref)5user=> @my-ref5• Use ref-set and alter to update a ref within a transaction• Use dosync to specify a transactional atomic operation
  26. 26. Transactional Memory• Bank Accounts in STMuser=> (def account1 (ref 1000))#user/account1user=> (def account2 (ref 1500))#user/account2user=> (defn transfer [a1 a2 amount](dosync(alter a1 - amount)(alter a2 + amount)))#user/transferuser=> (transfer account1 account2 250)1750user=> @account1750user=> @account21750
  27. 27. Atoms• Use atom to create an atom type reference• Use de-ref and @ to deference it• Use swap! and reset! to update ituser=> (def my-atom (atom 5))#user/my-atomuser=> (swap! my-atom + 3)8user=> (reset! my-atom 1)1user=> @my-atom1• Several threads updating a guest list, i.e. independent state
  28. 28. 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 theaction completes– Actions within STM transactions are not called untilthe transaction completes• Concurrency functions: pmap, pvalues & pcalls
  29. 29. State Management Summary• Use refs for coordinated synchronous updates• Use atoms for independent synchronous updates• Use agents for asynchronous updates andconcurrency• Vars maintain state within a thread• Validator functions help with data integrity• Watches trigger events based on identity values
  30. 30. Macros• Macros expand into code and a macro definitionspecifies the code expansion.• Think of macros as a mechanism that allows you to addfeatures to a language rather than within a language.• Macros don’t evaluate their arguments• Macros are extremely powerful and should be usedwith 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. 31. 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/unlessuser=> (unless false (println "This should print."))This should print.niluser=> (unless true (println "This should not print"))This should not printnilUh oh! The function unless evaluates <expr> regardless of <test>!
  32. 32. A Macro Exampleuser=> (defmacro unless [test expr](list if test nil expr))#user/unlessuser=> (unless false (println "This should print."))This should print.nil;; This now behaves correctlyuser=> (unless true (println "This should not print"))nil
  33. 33. Macros (cont.)• Syntax Quote: `• Unquote: ~• Splicing Unquote: ~@;; Old Definitionuser=> (defmacro unless [test expr](list if test nil expr))#user/unless;; New Definitionuser=> (defmacro unless [test expr] `(if ~test nil ~expr))#user/unless
  34. 34. Side Effect Safety• When you define a macro, it is unsafe to evaluate the arguments morethan once in the event that they have side effects.• Clojure provides auto-gensym and gensym to allow you to define localvariables in safe way and also avoid incorrectly repeated side-effects;; This is baduser=> (defmacro unless [test expr]`(let []~expr(if ~test nil ~expr)))#user/unlessuser=> (unless false (println "foo"))foofoonil;; This is gooduser=> (defmacro unless [test expr]`(let [result# ~expr]result#(if ~test nil result#))#user/unlessuser=> (unless false (println "foo"))foonil
  35. 35. Datatypes & Protocols• Roughly analogous to classes & interfaces Javabut more flexible• Protocols are sets of methods• Protocols are defined with defprotocol• Datatypes are named record types, with a setof named fields, that can implement recordsand interfaces.• Datatypes are defined with defrecord
  36. 36. Other Topics• Multimethods• Java Integration• Futures & Promises