Clojure class

Preview:

DESCRIPTION

 

Citation preview

Better Living Through Clojure

MIT IAPJanuary 14-15, 2014

Bhaskar (Buro) Mookerji (‘09 VIII , ‘11 VI/MEng)

David Greenberg (‘11 XVIII-C)

Aysylu Greenberg (‘12 VI-2)

We are ….

Today’s agenda includes...● Clojure Overview● Syntax: Everything is Data● Functions● Flow Control● Collections● Sequences

... and tomorrow: ● Testing● Concurrency● Polymorphism● JVM Interop● Performance● Tooling● Interesting Problems Solved by Clojure● Our Projects

Clojure Overview

Rationale and Philosophy

● First released by Rich Hickey in 2007○ Expressive functional programming with Lisp○ Designed for concurrency use by default○ Embedded in an existing platform

● Robust, practical, high-performance ● Simple abstractions backed by powerful

ideas

● Complacency breeds incidental complexity○ Large programs are difficult to understand○ Scope of effects are difficult to reason about○ Concurrency: holy crap.

● “Object-oriented programming is overrated”

The familiar solutions are complex

Clojure is a Lisp

About the use of language: it is impossible to sharpen a pencil with a blunt axe. It is equally vain to try to do it with ten blunt axes instead.

- Edsger Dijkstra How do we tell truths that might hurt?, EWD498

(Actually John McCarthy.)

Clojure is a Lisp

● LISP: code=data, few primitives, abstraction○ Local maximum in the hierarchy of expression

● Dynamically typed and compiled, interpreted● Syntactic abstraction through macros● Clojure:

○ Extends code-as-data to new data structures○ Modernizes LISP with new ideas from existing

functional languages

Clojure is Functional and Dynamic

● Functions are first-class● All data structures are immutable, persistent,

recursive, and support heterogeneous types● Well-defined concurrent behavior● Strict typing (i.e., Haskell, OCaml) is not

everyone nor needed for every application

Clojure Runs on the JVM (and Javascript!)

● OS independent● JVM: Well-supported, existing platform, lots

of libraries/platforms● Static types and safety● Garbage collection and a great compiler● Clojurescript:

○ Javascript as the platform: runs everywhere

Syntax: Everything is Data

Atomic Literals22 ;; Long

12345678901 ;; BigInt

1.34 ;; Double

1.34M ;; BigDecimal

22/3 ;; Ratio

true ;; boolean

“derp” ;; String

\d \e \r \p ;; Character

#”[0-9]+” ;; Regex

:derp, ::derp ;; Keyword

nil ;; null

Atomic Literals22 ;; Long

12345678901 ;; BigInt

1.34 ;; Double

1.34M ;; BigDecimal

22/2 ;; Ratio

true ;; boolean

Evaluating literals in the REPL:

“derp” ;; String

\d \e \r \p ;; Character

#”[0-9]+” ;; Regex

:derp, ::derp ;; Keyword

nil ;; null

user=> 22 ;; Read ;; Eval22 ;; Printuser=> ;; Loop

Atomic Literals22 ;; Long

12345678901 ;; BigInt

1.34 ;; Double

1.34M ;; BigDecimal

22/2 ;; Ratio

true ;; boolean

Evaluating expressions:

“derp” ;; String

\d \e \r \p ;; Character

#”[0-9]+” ;; Regex

:derp, ::derp ;; Keyword

nil ;; null

user=> (+ 2 2) ;; Read ;; Eval4 ;; Printuser=> ;; Loop

Atomic Literals22 ;; Long

12345678901 ;; BigInt

1.34 ;; Double

1.34M ;; BigDecimal

22/2 ;; Ratio

true ;; boolean

Evaluating expressions:

“derp” ;; String

\d \e \r \p ;; Character

#”[0-9]+” ;; Regex

:derp, ::derp ;; Keyword

nil ;; null

user=> ::derp ;; Read ;; Eval:user/derp ;; Printuser=> ;; Loop

Atomic Literals22 ;; Long

12345678901 ;; BigInt

1.34 ;; Double

1.34M ;; BigDecimal

22/2 ;; Ratio

true ;; boolean

Evaluating expressions:

“derp” ;; String

\d \e \r \p ;; Character

#”[0-9]+” ;; Regex

:derp, ::derp ;; Keyword

nil ;; null

user=> (class ::derp) clojure.lang.Keyword user=>

def binds namesuser=> pi ;; NOOOOO!! ⇒ Undefined.CompilerException java.lang.RuntimeException: ...

user=> (def pi 3.1415926)#'user/pi

user=> pi3.1415926

user=> (println pi) 3.1415926nil

Data Structures

● Lists - singly-linked, front-append○ (1 2 3 4)

● Vectors - indexed, back-append○ [:a 2 3 “4”]

● Maps - key/value store○ {:a 2 :b 4}

● Sets○ #{:a :b :c :d}

Expressions are Data

From: http://alandipert.github.io/oscon2012-clojure/

Expressions are Data

vs…

From: http://alandipert.github.io/oscon2012-clojure/

Expressions are Data

(op ...)● op can be a ….

○ Function: +, mod, println○ Macro: when, cond, and○ Special Operator: do, if, ref○ Higher-order function○ … anything callable (clojure.lang.IFn)

Function calls use parens...user=> (defn hello [] "Hi!!!") ;; define hello functionuser=> hello ;; return it#<user$hello user$hello@be1a6e>user=> (hello) ;; invoke it"Hi!!!"

… but values never douser=> (def my-name "Kelly Q. Programmer") #'user/my-name user=> my-name"Kelly Q. Programmer"user=>(my-name)ClassCastException java.lang.String cannot be cast to clojure.lang.IFn

Quote makes “variables” symbolicuser=> (def x 3)#'user/x

user=> (+ x 2)5

user=> '(+ x 2) ;; quoted list(+ x 2) ;; returns list, unevaluated

user=> (quote (+ x 2)) ;; same as above(+ x 2)

Introspect your Environmentsuser=> (use 'clojure.repl) ;; Quoted symbol user=> (doc if) ;; Print docstring------------------------- if (if test then else?)Special Form Evaluates test. If not the singular values nil or false, evaluates and yields then, otherwise, evaluates and yields else. If else is not supplied it defaults to nil.

Please see http://clojure.org/special_forms#ifnil

Introspect your Environmentsuser=> (source when) (defmacro when "Evaluates test. If logical true, evaluates body in an implicit do." {:added "1.0"} [test & body] (list 'if test (cons 'do body)))nil

For More:● find-doc - Print docstring for var whose doc or name matches a pattern

● apropos - returns a seq of definitions matching a regex

● pst - print stack trace for a given exception or *e by default

● See: http://clojure.org/cheatsheet

Namespaces are all around you

● Namespaces disambiguate names○ vars -> symbols, functions, macros, Java class etc.

are all defined in namespaces● Namespace management is a big part of

Clojure development(ns com.your.great.app (:require clojure.string [clojure.set :as set] [clojure.java.io :refer (file reader)]))

Functions

Functional Abstractions are Useful

● Clojure is functional● Functions and higher-order functions are

generic in a obviously fundamental ways● clojure.core (mostly) free of side-effects● TBDL: Immutability and persistence

guarantee behavior and performance

defn binds functions(defn messenger ;; multi-arity definition ([] ;; no args (messenger "Hi!")) ;; call self with default ([msg] ;; one arg (println msg)) ;; print it ([msg & args] ;; variadic definition (println msg args))) ;; apply print to all argsuser=> (messenger)Hi!user=> (messenger "Hi class!")Hi class!user=> (messenger "Hi class!" "Who?" "What?")Hello class! (Who? What?)

fn creates anonymous functions● fn creates lambdas (fn [message] (println message) )

( (fn [message] (println message) ) “Hi!”)Hi!nil

apply applies functions

● Invokes argument on arguments (last is a sequence).

;; & puts rest of args into sequence;; apply pulls args out of sequence(defn messenger [greeting & who] (apply println greeting who))

user=> (messenger "Hi, everyone!" "Who?" "What?")Hi, everyone! Who? What?

let binds symbols to values

● Values are either literal or expressions● Bound symbols have “lexical scope”(defn messenger [msg] (let [a “Who?” b “What?” c (capitalize msg)] (println a b c)))

user=> (messenger “Why?”)

Who? What? Why?

Closures

● Allows bindings to persist beyond the lexical scope

(defn messenger-builder [greeting] (fn [who] (println greeting who))) ;; defines closure

;; greeting provided here, then goes out of scope(def hello-er (messenger-builder "Hello"))

;; greeting still available because hello-er is closureuser=> (hello-er "world!")Hello world!

Flow Control

Expressions in Clojure

● Java, etc. : Expressions return values, but statements do not.

String s;

if (s == “foo”) {

s = “bar”;

}

return s;

● Clojure: Everything is an expression (-> value or nil)

(if (= s “foo”) “bar”)

(do (stuff) ;; Do stuff.

(= s “foo”) ;; => true

Flow Control Operators

● if, when, if-not, when-not, do, cond, case, etc. are all flow control operators.

● composable and general

“Truthiness”(if true :truthy :falsey) ;;=> :truthy, and so are:(if (Object.) :truthy :falsey) ;; objects(if [] :truthy :falsey) ;; empty collections(if 0 :truthy :falsey) ;; zero

(if false :truthy :falsey) ;;=> :falsey(if nil :truthy :falsey) ;; nil(if (seq []) :truthy :falsey) ;; seq on empty collection

(and true 0 22) ;; returns last expression

(or true false 22)

if, do, and whenuser => (if (even? 5) ;; if is multibranch (do (println "Even!") ;; do evaluates block and true) ;; returns last value (do (println "Odd!") false))odd ;; printedfalse ;; return value

user => (when (even? 5) ;; when is single-branch (println "Even!") ;; sugar for (if <>(do..)) true)

cond and case(let [x 22] ;; Compares predicates (cond (<= x 22) "Dismissed as coincidence." (> x 222) "x is greater than 222." :else "No conditions met"))

(defn bazbar [x] ;; Matches arguments (case x ;; in O(1) time 22 "x is 22" ;; Must be literals 222 "x is 222" "x is neither 22 or 222"))

Iterations (and side effects)(dotimes [i 5] ;; Evals and returns nil. (println i))

(doseq [letter [:a :b :c] ;; Imperative cartesian product. number (range 5)] (println [letter number]))

(for [letter [:a :b :c] ;; Sequence cartesian product. number (range 5)] [letter number]) ;; ([:a 0] [:a 1] [:a 2] [:b 0] ;; [:b 1] [:b 2] …)

Threading Macros Remove Nesting ;; Deeply-nested parens ...

(first (.split (.replace (.toUpperCase "a b c d") "A" "X")

" "))

;; ... are unwinded with thread-first macro

(-> "a b c d"

.toUpperCase

(.replace "A" "X")

(.split " ")

first)

;; Also ->>, as->, cond->, cond->>, etc.

Recursion (with loop/recur)

● loop binds, recur re-loops ● Strong prefer iteration and higher-order

functions over recursion

(loop [i 0] (if (< i 22) (recur (inc i)) i))

22

Today ….● Intro to Clojure● Clojure Overview & REPL● Functions● Flow Control● Collections● Sequences

Collections

● Extensive facilities for representing and manipulating data

● Small number of data structures● Seq abstraction common across data

structures● Large library of functions across all of them

Collections: Immutability

● Simple (numbers, strings) and compound values are immutable

● Key to Clojure's concurrency model● Cannot change immutable values

○ Generate new ones instead○ Persistent data structures for efficiency

Collections: Persistent Data Structures● New values = old values + modifications● New values are not full copies● New value and old value are both available

after 'changes'● Performance guarantees for most operations● All Clojure data structures are persistent

Collections: Persistent Data Structures

http://eclipsesource.com/blogs/2009/12/13/persistent-trees-in-git-clojure-and-couchdb-data-structure-convergence/

Collections: Data Structures

● Sequential: list

Collections: Data Structures

● Sequential: list○ Singly-linked lists○ Prepend: O(1)○ Lookup: O(1) at head, O(n) anywhere else○ Grow at the head (front)

Collections: Data Structures

● Sequential: list() ;=> the empty list

(:a :b :c) ; error because :a not function

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

(list :a :b :c) ;=> (:a :b :c)

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

Collections: Data Structures

● Sequential: list, vector

Collections: Data Structures

● Sequential: list, vector○ Indexed, random-access, array-like○ Append: O(1) *○ Lookup: O(1) *○ Grow at the tail (end)

O(1) * = O(log 32 n), really close to O(1),is O(1) for n < 1 billion

Collections: Data Structures

● Sequential: list, vector[] ;=> the empty vector[:a :b :c] ;=> [:a :b :c](vector :a :b :c) ;=> [:a :b :c](vec '(:a :b :c)) ;=> [:a :b :c]

(nth [:a :b :c] 0) ;=> :a

(conj [:a :b] :c) ;=> [:a :b :c]

Collections: Data Structures

● Sequential: list, vector● Associative: map

Collections: Data Structures

● Sequential: list, vector● Associative: map

○ Key → value, hash table, dictionary○ Insert and lookup: O(1) *○ Unordered

Collections: Data Structures

● Sequential: list, vector● Associative: map

{} ;;=> the empty map{:a "a" :b "b"} ;;=> {:a "a" :b "b"}

(get {:a "a"} :a) ;;=> "a"(get {:a "a"} :z) ;;=> nil ; not found(get {:a "a"} :z 22) ;;=> 22 ; default

(assoc {:a "a"} :b "b") ;;=> {:a "a" :b "b"}(dissoc {:a "a"} :a) ;;=> {}(conj {} [:a "a"]) ;;=> {:a "a"}

Collections: Data Structures

● Sequential: list, vector● Associative: map

○ Nested access: get-in, assoc-in, update-in(def our-class {:class "Better Living Through Clojure" :location {:room "4-231"})

Collections: Data Structures

● Sequential: list, vector● Associative: map

○ Nested access: get-in, assoc-in, update-in(def our-class {:class "Better Living Through Clojure" :location {:room "4-231"})(get (get our-class :location) :room) ;;=> "4-231"

Collections: Data Structures

● Sequential: list, vector● Associative: map

○ Nested access: get-in, assoc-in, update-in(def our-class {:class "Better Living Through Clojure" :location {:room "4-231"})(get (get our-class :location) :room) ;;=> "4-231"(-> our-class (get :location) (get :room)) ;;=> "4-231"

Collections: Data Structures

● Sequential: list, vector● Associative: map

○ Nested access: get-in, assoc-in, update-in(def our-class {:class "Better Living Through Clojure" :location {:room "4-231"})(get (get our-class :location) :room) ;;=> "4-231"(-> our-class (get :location) (get :room)) ;;=> "4-231"(get-in our-class [:location :room]) ;;=> "4-231"

Collections: Data Structures

● Sequential: list, vector● Associative: map, set

Collections: Data Structures

● Sequential: list, vector● Associative: map, set

○ Set of distinct values○ Insert: O(1) *○ Member?: O(1) *○ Unordered

Collections: Data Structures

● Sequential: list, vector● Associative: map, set

#{} ;;=> the empty set#{"a" "b"} ;;=> #{"a" "b"}

(set ["a" "b"]) ;;=> #{"a" "b"}

(conj #{} "a") ;;=> #{"a"}

(contains? #{"a"} "a") ;;=> true

Collections: Data Structures are Functions● Maps are functions of their keys

(def dict {:a "a" :b "b"})(dict :b) ;;=> "b"

Collections: Data Structures are Functions● Maps are functions of their keys

(def dict {:a "a" :b "b"})(dict :b) ;;=> "b"

● Keywords are functions of maps(:b dict) ;;=> "b"

Collections: Data Structures are Functions● Maps are functions of their keys

(def dict {:a "a" :b "b"})(dict :b) ;;=> "b"

● Keywords are functions of maps(:b dict) ;;=> "b"

● Sets are functions of their elements(def s #{3 7 9})(s 7) ;;=> 7

Collections: Data Structures are Functions● Maps are functions of their keys

(def dict {:a "a" :b "b"})(dict :b) ;;=> "b"

● Keywords are functions of maps(:b dict) ;;=> "b"

● Sets are functions of their elements(def s #{3 7 9})(s 7) ;;=> 7

● Vectors are functions of their indices(def v [:a :b :c])(v 1) ;;=> :b

Collections: Destructuring● Declarative way to pull apart compound data

○ vs. explicit, verbose access● Sequential & associative data structures● Nests for deep, arbitrary access● Works in fn and defn params, let bindings

Collections: Destructuring;; Without destructuring:(defn next-fib-pair [pair] [(second pair) (+ (first pair) (second pair))])

Collections: Destructuring;; Without destructuring:(defn next-fib-pair [pair] [(second pair) (+ (first pair) (second pair))])

;; With destructuring:(defn next-fib-pair [[x y]] [y (+ x y)])

Today ….● Intro to Clojure● Clojure Overview & REPL● Functions● Flow Control● Names & Namespaces● Collections● Sequences

Sequences

● Abstraction for representing iteration● Backed by a data structure or a function

○ Can be lazy and/or "infinite"● Foundation for large library of functions

Sequences: API

● (seq coll)○ If collection is not empty, return seq object on it○ If collection is empty, return nil

Sequences: API

● (seq coll)○ If collection is not empty, return seq object on it○ If collection is empty, return nil

● (first coll) returns the first element

Sequences: API

● (seq coll)○ If collection is not empty, return seq object on it○ If collection is empty, return nil

● (first coll) returns the first element● (rest coll) returns a sequence of the rest of

the elements

Sequences: API

● (seq coll)○ If collection is not empty, return seq object on it○ If collection is empty, return nil

● (first coll) returns the first element● (rest coll) returns a sequence of the rest of

the elements● (cons x coll) returns a new sequence: first is

x, rest is coll

Sequences: Example(seq [1 2 3]) ;=> (1 2 3) ; not a list

(seq "Clojure") ;=> (\C \l \o \j \u \r \e)

(seq {:a 1 :b 2}) ;=> ([:a 1] [:b 2]) ; seq of map entries

(seq a-java-array) ;=> (...)

(seq []) ;=> nil(seq "") ;=> nil(seq {}) ;=> nil

Sequences: Over Structures

● We can treat any data structure as a seq

1 2 3

s

(def s '(1 2 3)) ; s is a list

Sequences: Over Structures

● We can treat any data structure as a seq(def s '(1 2 3)) ; s is a list

1 2 3

s

a

(def a (first s)) ; a is 1

Sequences: Over Structures

● We can treat any data structure as a seq(def s '(1 2 3)) ; s is a list(def r (rest s)) ; r is a seq

1 2 3

s r

Sequences: Over Structures

● We can treat any data structure as a seq(def s '(1 2 3)) ; s is a list(def b (first (rest s)) ; b is 2(def b (second s)) ; same thing

1 2 3

s

b

Sequences: Over Structures

● We can treat any data structure as a seq○ Lists are seqs○ Others are wrapped○ Associative structures become sequence of pairs

Sequences: Over Functions

● Can map a generator function to a seq● Seq is lazy, can be infinite

● Can process more than fits in memory(def r (range 1 100)) ; r is a lazy seq(def a (first r)) ; a is 1(def s (rest r)) ; s is a lazy seq(def b (first (rest r)) ; b is 2(def b (second r)) ; same thing(def c (first (rest (rest r)))) ; c is 3(def c (nth r 2)) ; same thing

Sequences: Generation(range) ;=> (0 1 2 ... infinite

(range 5) ;=> (0 1 2 3 4)

(repeat :b) ;=> (:b :b :b ... infinite

(repeatedly #(rand-int 100)) ;=> (89 58 73 ... infinite

Sequences: Into Collections(into #{} "hello") ;=> #{\e \h \l \o}

(into {} [[:x 1] [:y 2]]) ;=> {:x 1, :y 2}

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

Sequences: Shorten(take 3 (range)) ;=> (0 1 2)

(drop 3 (range)) ;=> (3 4 5 ... infinite

(filter even? (range)) ;=> (0 2 4 6 ... infinite

(remove even? (range)) ;=> (1 3 5 7 ... infinite

Sequences: Lengthen(concat [:a :b] (range 2 5)) ;=> (:a :b 2 3 4)

(cycle [:a :b :c]) ;=> (:a :b :c :a :b ... infinite

(interpose \, (range 3)) ;=> (0 \, 1 \, 2)

Sequences: map(map even? (range 1 5)) ;=> (false true false true)

(map + [1 2 3] [4 5 6]) ;=> (5 7 9)

(map * [1 2 3] (range 1000)) ;=> (0 2 6)

Sequences: reduce(reduce function init seq)

● function takes 2 arguments: accumulator and the current argument● reduce calls (function init (first seq))● Return value becomes init for next step● Repeat until the end of the seq, returns the last init

(reduce (fn [total item] (+ total (* 10 item))) 0 ; init [1 2 3 4]) ;=> 100

Sequences: some(some even? [1 2 3 4]) ;=> true

(some #{:foo} [:baz :bar :foo]) ;=> :foo

;; Sum of the first 50 odd integers(reduce + (take 50 (filter odd? (range)))) ;=> 2500

Combine sequence functions: Power

;; Top 5 most frequently used words in the docstrings of the current namespace(->> (ns-publics *ns*) (map (comp :doc meta val)) (remove nil?) (mapcat (fn [s] (re-seq #"\w+" s))) (frequencies) (sort-by val) (reverse) (take 5) (map first))

In this Class● Testing● Concurrency● Polymorphism● JVM Interop● Performance● Tooling● Interesting Problems Solved by Clojure● Our Projects

clojure.test(deftest math-basics (is (= 3 (+ 1 2))) (is (= 10 (* 2 5))) (is (thrown? java.lang.ArithmeticException (/ 1 0))))

midje(fact (+ 1 2) => 3 (* 2 5) => 10 (/ 1 0) => (throws java.lang.ArithmeticException))

expectations(expect 3 (+ 1 2))

(expect 10 (* 2 5))

(expect java.lang.ArithmeticException (/ 1 0))

In this Class● Testing● Concurrency● Polymorphism● JVM Interop● Performance● Tooling● Interesting Problems Solved by Clojure● Our Projects

Benefits of Immutability

● What is a race condition?

● How does immutability help?

Atoms;; Create atom with initial value(def a (atom 0))

;; Atomically transition from old to new value(swap! a (fn [old-value] (+ 3 old-value)))

;; Set to a new value, regardless of old value(reset! a 22)

Refs and Agents

● Refs allow you do many “swap!”s transactionally○ aka Software Transactional Memory

● Agents allow you do do “swap!”s asynchronously on a thread pool○ a dual of actors: send functions to the value, rather

than values (messages) to the function (actor)

core.async

● Go-style communicating sequential processes

● “select” operator● Scalable user-space scheduling● Works in ClojureScript● See the talk from Clojure Conj here

In this Class● Testing● Concurrency● Polymorphism● JVM Interop● Performance● Tooling● Interesting Problems Solved by Clojure● Our Projects

Two kinds of polymorphism

Protocols Multimethods

Dispatch The first argument’s type Arbitrary function of all arguments

Performance Fast; uses native virtual dispatch Slow; requires a hash table lookup

Popularity Extremely popular, good reputation Used only when arbitrary dispatch is necessary

Protocols

● Solves the expression problem○ “How can we extend existing functions to new

datatypes and new functions to existing datatypes without recompiling all code?”

● Uses perfect hashing and Java interfaces for dispatching

Protocols - Example(defprotocol ITaskRunner (run [this task] "run the task, which is a fn"))(defrecord ThreadRunner [name] ITaskRunner (run [this task] (future (task)))(defrecord ImmediateRunner [nickname] ITaskRunner (run [this task] (task)))

(run (->ThreadRunner "Bob") (fn [] (println "hello world from thread")))(run (->ImmediateRunner "Lily") (fn [] (println "hello world from stack")))

Protocols - Example(defprotocol INamed (named [this] "Return the name of this"))

(extend-protocol INamed TaskRunner (named [this] (:name this)) ImmediateRunner (named [this] (:nickname this)))

(named (->ThreadRunner "Bob"));;=> "Bob"(named (->ImmediateRunner "Lily"));;=> "Lily"

Multimethods - Example;; We define a multimethod with its dispatch function(defmulti battle "Engage 2 spacecraft in epic battle" (fn [x y] [(:type x) (:type y)]))

;; We can even create hierarchies of keywords.;; Java types are permissible as leaves(derive :x-wing :ship)

Multimethods - Example;; We can define arbitrary numbers of implementations(defmethod battle [:death-star :planet] [death-star planet] (str planet " has been obliterated by " death-star))(defmethod battle [:ship :star-destroyer] [ship destroyer] (str ship " has been eliminated by " destroyer "defenses"))

(defmethod battle [:x-wing :death-star] [x-wing death-star] (str x-wing " perfectly shot its proton torpedoes into " death-star))

In this Class● Testing● Concurrency● Polymorphism● JVM Interop● Performance● Tooling● Interesting Problems Solved by Clojure● Our Projects

Exceptions(try (do-something-that-could-fail) (catch RuntimeException e (print-stack-trace e)) (finally (dispose-of-resources)))

Making your own Exceptions(try (throw (ex-info "A data-carrying exception" {:x 22 :y 42})) (catch clojure.lang.ExceptionInfo e (println ":x is" (:x (ex-data e)))))

Java InteropTask Java Clojure

Instantiation new MyClass(“foo”) (MyClass. “foo”)

Instance method dog.bark() (.bark dog)

Instance field object.field (.-field object)

Static method Math.sin(22) (Math/sin 22)

Static field Math.PI Math/PI

Java Methods vs. Clojure Functions;; Works(map str (range 10));; Doesn’t work(map .toString (range 10));;Works(map (fn [x] (.toString x)) (range 10))

Clojure Types are Java TypesClojure Type Java Type

Long java.lang.Long

Double java.lang.Double

Boolean java.lang.Boolean

String java.lang.String

Regex java.util.regex.Pattern

BigDecimal java.lang.BigDecimal

BigInt clojure.lang.BigInt (wraps java.lang.BigInteger or long)

Clojure Types are Java TypesClojure Type Java Type

Function java.lang.Runnable, java.util.concurrent.Callable

List java.util.List *

Vector java.util.List *

Map java.util.Map *

Set java.util.Set *

* Does not include mutation operators, like add() or put()

In this Class● Testing● Concurrency● Polymorphism● JVM Interop● Performance● Tooling● Interesting Problems Solved by Clojure● Our Projects

How long did it take?;;Built-in (simple)(time (dotimes [i 1000] (+ 1 1)))

;;Criterium (statistically robust)(criterium.core/bench (+ 1 1))

Clojurians care about Speed

● Transient collections● Protocols● Unboxed numeric support in the compiler● no.dissassemble (inspect bytecode of fn)● Performance-oriented libraries

○ core.async - user space threading○ core.matrix - matrix math○ narrator - time series analysis

What’s in your toolkit?Complex Simple

State, Objects, Methods Values, Functions/Namespaces

Variables Managed references and concurrency

Inheritance, switch, matching Polymorphism

Syntax Data

Imperative loops, folds Sequence Functions

Conditionals Rules

Inconsistency Consistency and Design

“Paradigms” Syntactic Abstraction

In this Class● Testing● Concurrency● Polymorphism● JVM Interop● Performance● Tooling● Interesting Problems Solved by Clojure● Our Projects

Leiningen● “for automating Clojure projects without setting your hair on fire”● Functional project management● Fetches dependencies and constructs classpath● Packages and deploys project● Easily extensible with plugins

Leingen - usage

lein new my-cool-project

lein repl

lein uberjar

lein deploy clojars(defproject my-cool-project "0.1.0-SNAPSHOT" ... :dependencies [[org.clojure/clojure "1.4.0"] [ring "1.2.1"]])

Vim

● fireplace○ Simple evaluation○ Prefix intellisense○ Documentation lookup○ Go to source

● redl○ Advanced REPL with simple debugger○ Fuzzy intellisense

Emacs

● cider○ Advanced REPL○ Documentation lookup○ Symbol completion○ Source lookup

● ritz○ Stepping debugger via JVM’s debugging API○ All features of cider

Light Table

● Easiest to get started● REPL● Inline documentation● Written in Clojure(Script)!

Paredit + Rainbow Parens

● It’s really hard to keep parens, braces, and brackets matching

● It’s really hard to see which ones are pairs● Let your editor handle it!

In this Class● Testing● Concurrency● Polymorphism● JVM Interop● Performance● Tooling● Interesting Problems Solved by Clojure● Our Projects

Clojure Toolbox

● clojure-toolbox.com● Great starting point for projects!● Database clients, web frameworks, data

structures, parsers, etc.

● Purely functional database

● Horizontal read scalability

Storm

● Hadoop for real-time data

● Used by Twitter to process all tweets in real-time (and by many others!)

Riemann

● Event processor and monitoring tool for distributed systems

● Makes it easy to monitor clusters, calculate statistics, and send alerts

Instaparse

What if context-free grammars were as easy to use as regular expressions?

(insta/parser "S = AB* AB = A B A = 'a'+ B = 'b'+")

In this Class● Testing● Concurrency● Polymorphism● JVM Interop● Performance● Tooling● Interesting Problems Solved by Clojure● Our Projects

Sources

● http://clojure.org● Talks by Rich Hickey: Clojure, Are We There

Yet, and Simple Made Easy● Beating the Averages, Paul Graham