290
Ben Mabey Plain & Simple Clojure @bmabey Utah Java User’s Group, Aug 15 2013

Clojure, Plain and Simple

Embed Size (px)

DESCRIPTION

This introduction to Clojure was given to the Utah Java Users Group Aug. 15. It's main focus was on Clojure's time model and how the design of Clojure separates (decomplects) many concepts which are all implemented onto of Objects in Java, and other OO languages. This is the abstract for the original talk: Tony Hoare famously said "There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult." Clojure is a functional Lisp that targets, among other platforms, the JVM and strives to enable the former approach to building software. In its pursuit of simplicity Clojure encourages the use of pure functions, sequence abstractions which allow for lazy and parallel processing of data, persistent (immutable) data structures, and a novel way of dealing with state as a succession of values. While these concepts may sound intimidating for those unfamiliar with functional programming, they are actually less complicated than many programming constructs that programmers use everyday. This talk will cover these concepts and the motivation behind them. You will learn the basics of Clojure programming and will be given a taste of what developing an application in Clojure is like.

Citation preview

Page 1: Clojure, Plain and Simple

Ben Mabey

Plain & SimpleClojure

@bmabey

Utah Java User’s Group, Aug 15 2013

Page 2: Clojure, Plain and Simple

redbrainlabs.com

Page 3: Clojure, Plain and Simple

"There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult."

Tony Hoare, 1980 Turing Award Lecture

Page 4: Clojure, Plain and Simple

Clojure is a functional Lisp that targets the JVM and enables simpler software design.

Page 5: Clojure, Plain and Simple

Clojure is a functional Lisp that targets the JVM and enables simpler software design.

Page 6: Clojure, Plain and Simple

Read Eval Print

Loop

Page 7: Clojure, Plain and Simple

Read Eval Print

Loop

((Live))

Page 8: Clojure, Plain and Simple

Macros

Page 9: Clojure, Plain and Simple

(if (= code :RED) (do (launch-missiles!) (sound-the-alarm!)))

Page 10: Clojure, Plain and Simple

‘do’ implies side effects

(if (= code :RED) (do (launch-missiles!) (sound-the-alarm!)))

Page 11: Clojure, Plain and Simple

(if (= code :RED) (do (launch-missiles!) (sound-the-alarm!)))

(when (= code :RED) (launch-missiles!) (sound-the-alarm!))

Page 12: Clojure, Plain and Simple

(defmacro when "Evaluates test. If logical true, evaluates body in an implicit do." [test & body] (list 'if test (cons 'do body)))

Page 13: Clojure, Plain and Simple

(defmacro when "Evaluates test. If logical true, evaluates body in an implicit do." [test & body] (list 'if test (cons 'do body)))

(macroexpand '(when (= code :RED) (launch-missiles!) (sound-the-alarm!)))

Page 14: Clojure, Plain and Simple

(defmacro when "Evaluates test. If logical true, evaluates body in an implicit do." [test & body] (list 'if test (cons 'do body)))

(macroexpand '(when (= code :RED) (launch-missiles!) (sound-the-alarm!)))

=> (if (= code :RED) (do (launch-missiles!) (sound-the-alarm!)))

Page 15: Clojure, Plain and Simple

Paradigms as Libraries

Page 16: Clojure, Plain and Simple

Paradigms as LibrariesDesign by Contract a’la Eiffel - core.contracts

Page 17: Clojure, Plain and Simple

Paradigms as LibrariesDesign by Contract a’la Eiffel - core.contracts

Logic Programming a’la Prolog - core.logic

Page 18: Clojure, Plain and Simple

Paradigms as LibrariesDesign by Contract a’la Eiffel - core.contracts

Logic Programming a’la Prolog - core.logic

Lightweight threads + channels a’la Go - core.async

Page 19: Clojure, Plain and Simple

Paradigms as LibrariesDesign by Contract a’la Eiffel - core.contracts

Logic Programming a’la Prolog - core.logic

Lightweight threads + channels a’la Go - core.async

Optional/Gradual Type system - core.typed

Page 20: Clojure, Plain and Simple

Paradigms as LibrariesDesign by Contract a’la Eiffel - core.contracts

Logic Programming a’la Prolog - core.logic

Lightweight threads + channels a’la Go - core.async

Optional/Gradual Type system - core.typed

Actor model a’la Erlang - pulsar

Page 21: Clojure, Plain and Simple

Paradigms as LibrariesDesign by Contract a’la Eiffel - core.contracts

Logic Programming a’la Prolog - core.logic

Lightweight threads + channels a’la Go - core.async

Optional/Gradual Type system - core.typed

Actor model a’la Erlang - pulsar

And more...

Page 22: Clojure, Plain and Simple

“If you give someone Fortran, he has Fortran. If you give someone Lisp, he has any language he pleases.”

Guy Steele

Page 23: Clojure, Plain and Simple

Clojure is a functional Lisp that targets the JVM and enables simpler software design.

Page 24: Clojure, Plain and Simple

Constructor

new Widget("gizmo");

(Widget. "gizmo")

Page 25: Clojure, Plain and Simple

Static Member

Math.PI

Math/PI

Page 26: Clojure, Plain and Simple

Instance Method

string.trim();

(.trim string)

Page 27: Clojure, Plain and Simple

Chained Access

person.getAddress().getState().getCode();

(.. person getAddress getState getCode)

Page 28: Clojure, Plain and Simple

Chained Access

person.getAddress().getState().getCode();

(.. person getAddress getState getCode)

Count ’em, 3 vs 1 pair!

Page 29: Clojure, Plain and Simple

Chained Access

person.getAddress().getState().getCode();

(.. person getAddress getState getCode)

(macroexpand '(.. person getAddress getState getCode))

(. (. (. person getAddress) getState) getCode)

Page 30: Clojure, Plain and Simple

Chained Access

person.getAddress().getState().getCode();

(.. person getAddress getState getCode)

(macroexpand '(.. person getAddress getState getCode))

(. (. (. person getAddress) getState) getCode)

Clojure has parens so its Java doesn’t need to

Page 31: Clojure, Plain and Simple

Multiple Updatesperson.setFirstName("Ben");person.setLastName("Mabey");person.makePresenter();

(doto person (.setFirstName "Ben") (.setLastName "Mabey") .makePresenter)

Page 32: Clojure, Plain and Simple

Multiple Updatesperson.setFirstName("Ben");person.setLastName("Mabey");person.makePresenter();

(doto person (.setFirstName "Ben") (.setLastName "Mabey") .makePresenter)

‘person’ is implicit to method calls

Page 33: Clojure, Plain and Simple

Multiple Updatesperson.setFirstName("Ben");person.setLastName("Mabey");person.makePresenter();

(doto person (.setFirstName "Ben") (.setLastName "Mabey") .makePresenter)

Again, ‘do’ signifies side effects

Page 34: Clojure, Plain and Simple

Implementing Interfaces

new Runnable() { public void run() { System.out.println("Hello World"); } };

(reify Runnable (run [] (println "Hello")))

Page 35: Clojure, Plain and Simple

Clojure is a functional Lisp that targets the JVM and enables simpler software design.

Page 36: Clojure, Plain and Simple

Functional

Page 37: Clojure, Plain and Simple

FunctionalProgramming with Values

Page 38: Clojure, Plain and Simple

FunctionalProgramming with Values

First Class Functions

Page 39: Clojure, Plain and Simple

FunctionalProgramming with Values

First Class Functions

Laziness

Page 40: Clojure, Plain and Simple

FunctionalProgramming with Values

First Class Functions

Laziness

Page 41: Clojure, Plain and Simple

public static int square(int x) { return x * x; }

Page 42: Clojure, Plain and Simple

public static int square(int x) { return x * x; }

Value

Page 43: Clojure, Plain and Simple

public static int square(int x) { return x * x; }

Value

} Pure Function

Page 44: Clojure, Plain and Simple

public static int square(int x) { return x * x; }

import java.util.Date;public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; }

Page 45: Clojure, Plain and Simple

public static int square(int x) { return x * x; }

import java.util.Date;public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; }

Reference Object

Page 46: Clojure, Plain and Simple

public static int square(int x) { return x * x; }

import java.util.Date;public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; }

Reference Object

Mutable!

Impure Function with side effects

Page 47: Clojure, Plain and Simple

public static int square(int x) { return x * x; }

import java.util.Date;public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; }

import org.joda.time.DateTime;public static DateTime oneYearFrom(DateTime date) { MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime(); }

Page 48: Clojure, Plain and Simple

public static int square(int x) { return x * x; }

import java.util.Date;public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; }

import org.joda.time.DateTime;public static DateTime oneYearFrom(DateTime date) { MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime(); } Value Object

Page 49: Clojure, Plain and Simple

public static int square(int x) { return x * x; }

import java.util.Date;public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; }

import org.joda.time.DateTime;public static DateTime oneYearFrom(DateTime date) { MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime(); } Value Object

Pure Function

Page 50: Clojure, Plain and Simple

public static int square(int x) { return x * x; }

import java.util.Date;public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; }

import org.joda.time.DateTime;public static DateTime oneYearFrom(DateTime date) { //MutableDateTime temp = new MutableDateTime(date); //temp.addYears(1); return date.plusYears(1); }

Pure Function

Nice Immutable API

Page 51: Clojure, Plain and Simple

public static int square(int x) { return x * x; }

import java.util.Date;public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; }

import org.joda.time.DateTime;public static DateTime oneYearFrom(DateTime date) { //MutableDateTime temp = new MutableDateTime(date); //temp.addYears(1); return date.plusYears(1); }

Page 52: Clojure, Plain and Simple

public static int square(int x) { return x * x; }

import java.util.Date;public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; }

import org.joda.time.DateTime;public static DateTime oneYearFrom(DateTime date) { //MutableDateTime temp = new MutableDateTime(date); //temp.addYears(1); return date.plusYears(1); }

Page 53: Clojure, Plain and Simple

ReferencesValues

Page 54: Clojure, Plain and Simple

ReferencesValuesprimitives

java.lang wrappers(Boolean, Byte, Character,

Double, Float, Integer, Long, Short, String)

other wrappers...e.g. UUID, URL, URI

Page 55: Clojure, Plain and Simple

ReferencesValuesprimitives

java.lang wrappers(Boolean, Byte, Character,

Double, Float, Integer, Long, Short, String)

other wrappers...e.g. UUID, URL, URI

DateBigInteger,BigDecimal, etc.

Page 56: Clojure, Plain and Simple

ReferencesValuesprimitives

java.lang wrappers(Boolean, Byte, Character,

Double, Float, Integer, Long, Short, String)

other wrappers...e.g. UUID, URL, URI

DateBigInteger,BigDecimal, etc.

CollectionsArrayList,ArrayDeque,TreeSet,HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc...

Page 57: Clojure, Plain and Simple

ReferencesValuesprimitives

java.lang wrappers(Boolean, Byte, Character,

Double, Float, Integer, Long, Short, String)

other wrappers...e.g. UUID, URL, URI

DateBigInteger,BigDecimal, etc.

CollectionsArrayList,ArrayDeque,TreeSet,HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc...

Default for domain objects

Page 58: Clojure, Plain and Simple

ReferencesValuesprimitives

java.lang wrappers(Boolean, Byte, Character,

Double, Float, Integer, Long, Short, String)

other wrappers...e.g. UUID, URL, URI

DateBigInteger,BigDecimal, etc.

Librariesjoda-time, joda-money

google-guava

CollectionsArrayList,ArrayDeque,TreeSet,HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc...

Default for domain objects

Page 59: Clojure, Plain and Simple

ReferencesValuesprimitives

java.lang wrappers(Boolean, Byte, Character,

Double, Float, Integer, Long, Short, String)

other wrappers...e.g. UUID, URL, URI

DateBigInteger,BigDecimal, etc.

Librariesjoda-time, joda-money

google-guava

CollectionsArrayList,ArrayDeque,TreeSet,HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc...

Immutable Collections!

Default for domain objects

Page 60: Clojure, Plain and Simple

ReferencesValuesprimitives

java.lang wrappers(Boolean, Byte, Character,

Double, Float, Integer, Long, Short, String)

other wrappers...e.g. UUID, URL, URI

DateBigInteger,BigDecimal, etc.

Librariesjoda-time, joda-money

google-guava

CollectionsArrayList,ArrayDeque,TreeSet,HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc...

Immutable Collections!+ They are values!

Default for domain objects

Page 61: Clojure, Plain and Simple

ReferencesValuesprimitives

java.lang wrappers(Boolean, Byte, Character,

Double, Float, Integer, Long, Short, String)

other wrappers...e.g. UUID, URL, URI

DateBigInteger,BigDecimal, etc.

Librariesjoda-time, joda-money

google-guava

CollectionsArrayList,ArrayDeque,TreeSet,HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc...

Immutable Collections!+ They are values!- Copy on write (very good impls though)

Default for domain objects

Page 62: Clojure, Plain and Simple

ReferencesValuesprimitives

java.lang wrappers(Boolean, Byte, Character,

Double, Float, Integer, Long, Short, String)

other wrappers...e.g. UUID, URL, URI

DateBigInteger,BigDecimal, etc.

Librariesjoda-time, joda-money

google-guava

CollectionsArrayList,ArrayDeque,TreeSet,HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc...

Immutable Collections!+ They are values!- Copy on write (very good impls though)

- Can’t help with nesting

Default for domain objects

Page 63: Clojure, Plain and Simple

ReferencesValuesprimitives

java.lang wrappers(Boolean, Byte, Character,

Double, Float, Integer, Long, Short, String)

other wrappers...e.g. UUID, URL, URI

DateBigInteger,BigDecimal, etc.

Librariesjoda-time, joda-money

google-guava

CollectionsArrayList,ArrayDeque,TreeSet,HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc...

Immutable Collections!+ They are values!- Copy on write (very good impls though)

- Can’t help with nesting

Default for domain objects

Page 64: Clojure, Plain and Simple
Page 65: Clojure, Plain and Simple

“Best practice” but not idiomatic...

Page 66: Clojure, Plain and Simple

“Best practice” but not idiomatic...

more difficult than it should be!

Page 67: Clojure, Plain and Simple

“Best practice” but not idiomatic...

more difficult than it should be!

Page 68: Clojure, Plain and Simple

ReferencesValues

Page 69: Clojure, Plain and Simple

ReferencesValuesprimitives

Java’s wrappers & libs

Page 70: Clojure, Plain and Simple

ReferencesValuesprimitives

Java’s wrappers & libs

CollectionsList, Vector, HashMap,

TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords

Page 71: Clojure, Plain and Simple

Explicit Ref Typesatom

refagent

ReferencesValuesprimitives

Java’s wrappers & libs

CollectionsList, Vector, HashMap,

TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords

Page 72: Clojure, Plain and Simple

Explicit Ref Typesatom

refagent

ReferencesValuesprimitives

Java’s wrappers & libs

CollectionsList, Vector, HashMap,

TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords

How Clojure addresses the non-functional aspect

of programs, i.e. state.

Page 73: Clojure, Plain and Simple

Explicit Ref Typesatom

refagent

ReferencesValuesprimitives

Java’s wrappers & libs

CollectionsList, Vector, HashMap,

TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords

deftype

Page 74: Clojure, Plain and Simple

Explicit Ref Typesatom

refagent

ReferencesValuesprimitives

Java’s wrappers & libs

CollectionsList, Vector, HashMap,

TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords Java libs for interop

deftype

Page 75: Clojure, Plain and Simple

Explicit Ref Typesatom

refPersistent Collections

ReferencesValuesprimitives

Java’s wrappers & libs

CollectionsList, Vector, HashMap,

TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords

+ They are values

Page 76: Clojure, Plain and Simple

Explicit Ref Typesatom

refPersistent Collections

ReferencesValuesprimitives

Java’s wrappers & libs

CollectionsList, Vector, HashMap,

TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords

+ They are values

+ Structural Sharing + Memory efficient + Fast

Page 77: Clojure, Plain and Simple

Explicit Ref Typesatom

refPersistent Collections

ReferencesValuesprimitives

Java’s wrappers & libs

CollectionsList, Vector, HashMap,

TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords

+ They are values

+ Structural Sharing + Memory efficient + Fast

+ Everything nests

Page 78: Clojure, Plain and Simple

Explicit Ref Typesatom

refPersistent Collections

ReferencesValuesprimitives

Java’s wrappers & libs

CollectionsList, Vector, HashMap,

TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords

+ They are values

+ Structural Sharing + Memory efficient + Fast

+ Everything nests

The rest of Clojure and its ecosystem is

built on these!

Page 79: Clojure, Plain and Simple

Structural Sharing

Page 80: Clojure, Plain and Simple

Structural Sharing

String brother = "brother";String the = brother.substring(3, 6);

Page 81: Clojure, Plain and Simple

Structural Sharing

String brother = "brother";String the = brother.substring(3, 6);

http://www.slreynolds.net/talks/clojure/collections/index.html

Page 82: Clojure, Plain and Simple
Page 84: Clojure, Plain and Simple

http://www.innoq.com/blog/st/2010/04/clojure_performance_guarantees.html

hash-map

sorted-map

hash-set

sorted-set

vector queue list lazy seq

conj log32(n) log(n) log32(n) log(n) 1 1 1 1assoc log32(n) log(n) log32(n)

dissoc log32(n) log(n)disj log32(n) log(n)nth log32(n) n n nget log32(n) log(n) log32(n) log(n) log32(n)

pop 1 1 1 1peek 1 1 1 1count 1 1 1 1 1 1 1 n

TL;DR, they are fast

Page 85: Clojure, Plain and Simple

Transients

Page 86: Clojure, Plain and Simple

Transients MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime();

import org.joda.time.DateTime;public static DateTime oneYearFrom(DateTime date) { MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime(); }

Page 87: Clojure, Plain and Simple

Transients MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime();

import org.joda.time.DateTime;public static DateTime oneYearFrom(DateTime date) { MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime(); }

(transient data)

(persistent! transient-data)

Page 88: Clojure, Plain and Simple

Nesting

Page 89: Clojure, Plain and Simple

(def data {:nested [0 1 {:double "nested"}]})

Nesting

Page 90: Clojure, Plain and Simple

(def data {:nested [0 1 {:double "nested"}]})

(get-in data [:nested 2 :double])

Nesting

Page 91: Clojure, Plain and Simple

(def data {:nested [0 1 {:double "nested"}]})

(get-in data [:nested 2 :double])

Nesting

First map key Index into vector

Nested map key

Page 92: Clojure, Plain and Simple

(def data {:nested [0 1 {:double "nested"}]})

(get-in data [:nested 2 :double]) > "nested"

Nesting

Page 93: Clojure, Plain and Simple

(update-in data [:nested 2 :double] upper-case)

(get-in data [:nested 2 :double]) > "nested"

(def data {:nested [0 1 {:double "nested"}]})

Nesting

Page 94: Clojure, Plain and Simple

(update-in data [:nested 2 :double] upper-case)

(get-in data [:nested 2 :double]) > "nested"

(def data {:nested [0 1 {:double "nested"}]})

Nesting

Same path

Page 95: Clojure, Plain and Simple

(update-in data [:nested 2 :double] upper-case)

(get-in data [:nested 2 :double]) > "nested"

(def data {:nested [0 1 {:double "nested"}]})

Nesting

Fn applied to nested value

Page 96: Clojure, Plain and Simple

(get-in data [:nested 2 :double]) > "nested"

(def data {:nested [0 1 {:double "nested"}]})

Nesting

(update-in data [:nested 2 :double] upper-case) > {:nested [0 1 {:double "NESTED"}]}

Entire “updated” data is returned as a value

Page 97: Clojure, Plain and Simple

(def data {:nested [0 1 {:double "nested"}]})

(get-in data [:nested 2 :double]) > "nested"

Nesting

(update-in data [:nested 2 :double] upper-case) > {:nested [0 1 {:double "NESTED"}]}

(assoc-in data [:nested 2 :another] "val")

Page 98: Clojure, Plain and Simple

(def data {:nested [0 1 {:double "nested"}]})

(get-in data [:nested 2 :double]) > "nested"

Nesting

(update-in data [:nested 2 :double] upper-case) > {:nested [0 1 {:double "NESTED"}]}

(assoc-in data [:nested 2 :another] "val")

New key

Page 99: Clojure, Plain and Simple

(def data {:nested [0 1 {:double "nested"}]})

(get-in data [:nested 2 :double]) > "nested"

Nesting

(update-in data [:nested 2 :double] upper-case) > {:nested [0 1 {:double "NESTED"}]}

(assoc-in data [:nested 2 :another] "val")

New value

New key

Page 100: Clojure, Plain and Simple

(def data {:nested [0 1 {:double "nested"}]})

(get-in data [:nested 2 :double]) > "nested"

Nesting

(update-in data [:nested 2 :double] upper-case) > {:nested [0 1 {:double "NESTED"}]}

(assoc-in data [:nested 2 :another] "val") > {:nested [0 1 {:double "nested" :another "val"}]}

Page 101: Clojure, Plain and Simple

(def data {:nested [0 1 {:double "nested"}]})

(get-in data [:nested 2 :double]) > "nested"

Nesting

(update-in data [:nested 2 :double] upper-case) > {:nested [0 1 {:double "NESTED"}]}

(assoc-in data [:nested 2 :another] "val") > {:nested [0 1 {:double "nested" :another "val"}]}

Imagine doing this in Java.

Page 102: Clojure, Plain and Simple

Clojure is a functional Lisp that targets the JVM and enables simpler software design.

Page 103: Clojure, Plain and Simple

Simpler?

Page 104: Clojure, Plain and Simple

Simpler?Simple Made Easy Rich Hickeyhttp://www.infoq.com/presentations/Simple-Made-Easy

Page 105: Clojure, Plain and Simple

Simple != Easy

Page 106: Clojure, Plain and Simple

Easy

Page 107: Clojure, Plain and Simple

Easyease < aise < adjacens

lie near

i.e. familiar, convenient,

near to our skill set or current understanding

Page 108: Clojure, Plain and Simple

Easyease < aise < adjacens

lie near

i.e. familiar, convenient,

near to our skill set or current understanding

always relative! opposite of hard

Page 109: Clojure, Plain and Simple

Simple

Page 110: Clojure, Plain and Simple

Simplesim - plex

one fold/braid

opposite of complex

Page 111: Clojure, Plain and Simple

Simplesim - plex

one fold/braid

opposite of complex

Page 112: Clojure, Plain and Simple

Simplesim - plex

one fold/braid

no interleaving!one concept,

one dimensionone role, but maybe multiple operations

opposite of complex

Page 113: Clojure, Plain and Simple

Simple

Page 114: Clojure, Plain and Simple

Simple

Complex

Page 115: Clojure, Plain and Simple

Complect

To interleave, entwine, braid

Page 116: Clojure, Plain and Simple

http://tinyurl.com/candy-land-pdf

Page 117: Clojure, Plain and Simple
Page 118: Clojure, Plain and Simple
Page 119: Clojure, Plain and Simple
Page 120: Clojure, Plain and Simple

How would you model the game

state using objects?

Page 121: Clojure, Plain and Simple
Page 122: Clojure, Plain and Simple

public class GameBoard {!! private List<ColoredSpace> spaces = new ArrayList<ColoredSpace>();! private CardDeck cardDeck = new CardDeck();! private List<Player> players = new ArrayList<Player>();! // Points to player whose turn it is next! private int playerPointer = 0;! // Players position on the board! private Integer[] playerPositions; .... }

Page 123: Clojure, Plain and Simple

public class GameBoard {!! private List<ColoredSpace> spaces = new ArrayList<ColoredSpace>();! private CardDeck cardDeck = new CardDeck();! private List<Player> players = new ArrayList<Player>();! // Points to player whose turn it is next! private int playerPointer = 0;! // Players position on the board! private Integer[] playerPositions; .... }

public class Player {! private String name;! public Player(String name) {! ! this.name = name;! }! public String getName() {! ! return name;! }}

Page 124: Clojure, Plain and Simple
Page 125: Clojure, Plain and Simple

{:players [{:location 32 :name "ben"} {:location 14 :name "maren"}] :board [{:color :purple} ... {:color :orange, :shortcut-to 62} .... {:color :yellow, :picture :candy-heart} ...] :decks {:unplayed (:red [:orange :orange] :peppermint-stick ...) :played (:red :blue ...)}}

Page 126: Clojure, Plain and Simple

{:players [{:location 32 :name "ben"} {:location 14 :name "maren"}] :board [{:color :purple} ... {:color :orange, :shortcut-to 62} .... {:color :yellow, :picture :candy-heart} ...] :decks {:unplayed (:red [:orange :orange] :peppermint-stick ...) :played (:red :blue ...)}}

Commas Optional

Page 127: Clojure, Plain and Simple
Page 128: Clojure, Plain and Simple

distinct filter remove for keep keep-indexed cons concat lazy-cat mapcat cycle interleave

interpose rest next fnext nnext drop drop-while nthnext for take take-nth

take-while butlast drop-last for flatten reverse sort sort-by shuffle split-at split-with partition partition-all partition-by map pmap mapcat for replace reductions map-indexed

seque first ffirst nfirst second nth when-first last rand-nth zipmap into reduce set vec into-array to-array-2d frequencies group-by apply not-empty some reduce seq? every? not-

every? not-any? empty? some filter doseq dorun doall realized? assoc get get-in assoc-in update-in peek pop subvec conj cons into

Page 129: Clojure, Plain and Simple

It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.Alan Perlis

Page 130: Clojure, Plain and Simple
Page 131: Clojure, Plain and Simple

Data

Page 132: Clojure, Plain and Simple

Methods

Data

Page 133: Clojure, Plain and Simple

( )

Page 134: Clojure, Plain and Simple

( )

Values

Page 135: Clojure, Plain and Simple

( )

ValuesFunctions

Page 136: Clojure, Plain and Simple

Create a Game Board

Page 137: Clojure, Plain and Simple

Create a Game Board

Page 138: Clojure, Plain and Simple

Create a Game Board• 129 colored spaces• 6 colors in repeating

sequence:1. Purple2.Yellow3.Blue4.Orange5.Green6.Red

Page 139: Clojure, Plain and Simple

Create a Game Board! private static final int NUMBER_SPACES = 129;! public static String[] COLOR_SEQUENCE = { ! ! "Purple",! ! "Yellow",! ! "Blue",! ! "Orange",! ! "Green",! ! "Red"! };!

Page 140: Clojure, Plain and Simple

Create a Game Board! private static final int NUMBER_SPACES = 129;! public static String[] COLOR_SEQUENCE = { ! ! "Purple",! ! "Yellow",! ! "Blue",! ! "Orange",! ! "Green",! ! "Red"! };!! public GameBoard() {! ! // Create Spaces! ! for (int i = 0; i < NUMBER_SPACES; i++) {! ! ! String color = COLOR_SEQUENCE[i % COLOR_SEQUENCE.length];! ! ! spaces.add(new ColoredSpace(color));! ! }! ! ... }! !

Page 141: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

Page 142: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

user> (find-doc "cycle")-------------------------clojure.core/check-cyclic-dependency([path]) Detects ....-------------------------clojure.core/cycle([coll]) Returns a lazy (infinite!) sequence of repetitions of the items in coll.

ProTip, use find-doc to search for fns

Page 143: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

user> (find-doc "cycle")-------------------------clojure.core/check-cyclic-dependency([path]) Detects ....-------------------------clojure.core/cycle([coll]) Returns a lazy (infinite!) sequence of repetitions of the items in coll.

Page 144: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

Page 145: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

(take 3 (cycle colors)) => (:purple :yellow :blue)

Page 146: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

(take 3 (cycle colors)) => (:purple :yellow :blue)

Laziness Increases Modularity

Page 147: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

(take 3 (cycle colors)) => (:purple :yellow :blue)

Laziness Increases Modularity

for (int i = 0; i < NUMBER_SPACES; i++) {! ! ! String color = COLOR_SEQUENCE[i % COLOR_SEQUENCE.length];! ! ! spaces.add(new ColoredSpace(color));! ! }

Page 148: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

(take 3 (cycle colors)) => (:purple :yellow :blue)

Page 149: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

(take 3 (cycle colors)) => (:purple :yellow :blue)

Inside-Out Code

Page 150: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

(take 3 (cycle colors)) => (:purple :yellow :blue)

Inside-Out Code

(->> (cycle colors) (take 3)) => (:purple :yellow :blue)

Threading Operators

Page 151: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

(take 3 (cycle colors)) => (:purple :yellow :blue)

(->> (cycle colors) (take 3)) => (:purple :yellow :blue)

Page 152: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

(take 3 (cycle colors)) => (:purple :yellow :blue)

(->> (cycle colors) (take 3)) => (:purple :yellow :blue)

(defn make-space [color] {:color color})

Page 153: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

(take 3 (cycle colors)) => (:purple :yellow :blue)

(->> (cycle colors) (take 3)) => (:purple :yellow :blue)

(defn make-space [color] {:color color})

(->> colors (map make-space) cycle (take 3)) => ({:color :purple} {:color :yellow} {:color :blue})

Page 154: Clojure, Plain and Simple

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

(take 3 (cycle colors)) => (:purple :yellow :blue)

(->> (cycle colors) (take 3)) => (:purple :yellow :blue)

(defn make-space [color] {:color color})

(->> colors (map make-space) cycle (take 3)) => ({:color :purple} {:color :yellow} {:color :blue})

Small fns can be inlined

Page 155: Clojure, Plain and Simple

(map make-space)

(defn make-card [color] {:color color})

(->> colors (map make-space) cycle (take 3)) => ({:color :purple} {:color :yellow} {:color :blue})

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

(take 3 (cycle colors)) => (:purple :yellow :blue)

(->> (cycle colors) (take 3)) => (:purple :yellow :blue)

Small fns can be inlined

(->> colors (map #(array-map :color %)) cycle (take 3))

Page 156: Clojure, Plain and Simple

(map make-space)

(defn make-card [color] {:color color})

(->> colors (map make-space) cycle (take 3)) => ({:color :purple} {:color :yellow} {:color :blue})

(def colors [:purple :yellow :blue :orange :green :red])

(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)

(take 3 (cycle colors)) => (:purple :yellow :blue)

(->> (cycle colors) (take 3)) => (:purple :yellow :blue)

Small fns can be inlined

(->> colors (map #(array-map :color %)) cycle (take 3))

% is the anonymous first param

Page 157: Clojure, Plain and Simple

(def game-board (->> [:purple :yellow :blue :orange :green :red] (map #(array-map :color %)) cycle (take 129)))

! public GameBoard() {! ! // Create Spaces! ! for (int i = 0; i < NUMBER_SPACES; i++) {! ! ! String color = COLOR_SEQUENCE[i % COLOR_SEQUENCE.length];! ! ! spaces.add(new ColoredSpace(color));! ! }! ! ... }! !

Create a Game Board

Page 158: Clojure, Plain and Simple

Play the game

Page 159: Clojure, Plain and Simple

Play the gamepublic class GameBoard {

! public void play() {! ! while (nextTurn());! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName());! }

Page 160: Clojure, Plain and Simple

! // Player pulls a card from the top of deck and moves player! private boolean nextTurn() {! ! // Player selects card from top of deck! ! Card currCard = cardDeck.takeTopCard();! ! // If the game has ended, return now! ! if (movePlayerOnBoard(currCard)) return false;! ! // Next players turn! ! playerPointer = (playerPointer + 1) % players.size();! ! // Game has not ended yet! ! return true;! }

Play the gamepublic class GameBoard {

! public void play() {! ! while (nextTurn());! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName());! }

! // Player pulls a card from the top of deck and moves player! private boolean nextTurn() {! ! // Player selects card from top of deck! ! Card currCard = cardDeck.takeTopCard();! ! // If the game has ended, return now! ! if (movePlayerOnBoard(currCard)) return false;! ! // Next players turn! ! playerPointer = (playerPointer + 1) % players.size();! ! // Game has not ended yet! ! return true;! }

}

Page 161: Clojure, Plain and Simple

! // Player pulls a card from the top of deck and moves player! private boolean nextTurn() {! ! // Player selects card from top of deck! ! Card currCard = cardDeck.takeTopCard();! ! // If the game has ended, return now! ! if (movePlayerOnBoard(currCard)) return false;! ! // Next players turn! ! playerPointer = (playerPointer + 1) % players.size();! ! // Game has not ended yet! ! return true;! }

Play the gamepublic class GameBoard {

! public void play() {! ! while (nextTurn());! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName());! }

! // Player pulls a card from the top of deck and moves player! private boolean nextTurn() {! ! // Player selects card from top of deck! ! Card currCard = cardDeck.takeTopCard();! ! // If the game has ended, return now! ! if (movePlayerOnBoard(currCard)) return false;! ! // Next players turn! ! playerPointer = (playerPointer + 1) % players.size();! ! // Game has not ended yet! ! return true;! }

}

Reference Object Land

Page 162: Clojure, Plain and Simple

! // Player pulls a card from the top of deck and moves player! private boolean nextTurn() {! ! // Player selects card from top of deck! ! Card currCard = cardDeck.takeTopCard();! ! // If the game has ended, return now! ! if (movePlayerOnBoard(currCard)) return false;! ! // Next players turn! ! playerPointer = (playerPointer + 1) % players.size();! ! // Game has not ended yet! ! return true;! }

Play the gamepublic class GameBoard {

! public void play() {! ! while (nextTurn());! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName());! }

! // Player pulls a card from the top of deck and moves player! private boolean nextTurn() {! ! // Player selects card from top of deck! ! Card currCard = cardDeck.takeTopCard();! ! // If the game has ended, return now! ! if (movePlayerOnBoard(currCard)) return false;! ! // Next players turn! ! playerPointer = (playerPointer + 1) % players.size();! ! // Game has not ended yet! ! return true;! }

}

Reference Object Land

Page 163: Clojure, Plain and Simple

(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game]

Page 164: Clojure, Plain and Simple

(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game]

Docstring attached as metadata

Page 165: Clojure, Plain and Simple

(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game

Page 166: Clojure, Plain and Simple

(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game

Bind the keys we need with desctructuring

Page 167: Clojure, Plain and Simple

(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index)

Page 168: Clojure, Plain and Simple

(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks)

Page 169: Clojure, Plain and Simple

(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks)

Returns a value, pair of [card updated-decks]

Page 170: Clojure, Plain and Simple

(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks)

We destructure and bind the pair as we like

Page 171: Clojure, Plain and Simple

(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks) players-next-location (next-space card board player)]

Page 172: Clojure, Plain and Simple

(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks) players-next-location (next-space card board player)] (-> game (assoc :decks new-decks :player-index (-> player-index inc (mod (count players))))

Page 173: Clojure, Plain and Simple

(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks) players-next-location (next-space card board player)] (-> game (assoc :decks new-decks :player-index (-> player-index inc (mod (count players)))) (assoc-in [:players player-index :location] players-next-location))))

Page 174: Clojure, Plain and Simple

(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks) players-next-location (next-space card board player)] (-> game (assoc :decks new-decks :player-index (-> player-index inc (mod (count players)))) (assoc-in [:players player-index :location] players-next-location))))

Page 175: Clojure, Plain and Simple

Play the game

(defn play [game] (->> (iterate next-move game) (filter game-over?) first))

Page 176: Clojure, Plain and Simple

(defn play [game] (filter game-over?) first))

Play the game

(->> (iterate next-move game)

Text

Page 177: Clojure, Plain and Simple

(defn play [game] (filter game-over?) first))

Play the game

(->> (iterate next-move game)

Textv1

Page 178: Clojure, Plain and Simple

(defn play [game] (filter game-over?) first))

Play the game

(->> (iterate next-move game)

Textv1

next-move

v2

Page 179: Clojure, Plain and Simple

(defn play [game] (filter game-over?) first))

Play the game

(->> (iterate next-move game)

Textv1

next-move

v2 v3

next-move

Page 180: Clojure, Plain and Simple

(defn play [game] (filter game-over?) first))

Play the game

(->> (iterate next-move game)

Textv1

next-move

v2 v3

next-move next-move

...

Page 181: Clojure, Plain and Simple

(defn play [game] (filter game-over?) first))

Play the game

(->> (iterate next-move game)

Textv1

next-move

v2 v3

next-move next-move

...

(->> 1 (iterate inc) (take 5)) > (1 2 3 4 5)

Page 182: Clojure, Plain and Simple

Play the game

(defn play [game] (->> (iterate next-move game) (filter game-over?) first))

(filter game-over?) first))

Predicate function

Page 183: Clojure, Plain and Simple

Play the game

(defn play [game] (->> (iterate next-move game) (filter game-over?) first))

(filter game-over?) first))

Return the ended game value

Page 184: Clojure, Plain and Simple

Play the game

(defn play [game] (->> (iterate next-move game) (filter game-over?) first))

public void play() {! ! while (nextTurn());! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName());! }

Page 185: Clojure, Plain and Simple

Play the game

(defn play [game] (->> (iterate next-move game) (filter game-over?) first))

(->> (create-game ["Ben" "Maren"]) play winning-player :name (println "The winning player is:"))

public void play() {! ! while (nextTurn());! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName());! }

Page 186: Clojure, Plain and Simple

Play the game

(defn play [game] (->> (iterate next-move game) (filter game-over?) first))

(->> (create-game ["Ben" "Maren"]) play winning-player :name (println "The winning player is:"))

public void play() {! ! while (nextTurn());! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName());! }

Our first side effect!

Page 187: Clojure, Plain and Simple
Page 188: Clojure, Plain and Simple

Data

Methods

Page 189: Clojure, Plain and Simple

Data

Methods

References

Page 190: Clojure, Plain and Simple

( )

ValuesFunctions

Page 191: Clojure, Plain and Simple

Put it on the Web!

Page 192: Clojure, Plain and Simple

{:players [{:location 32, :name "ben"} {:location 14, :name "maren"}] :board [{:color :purple} ... {:color :orange, :shortcut-to 62} .... {:color :yellow, :picture :candy-heart} ...], :decks {:unplayed (:red [:orange :orange] :peppermint-stick ...), :played (:red, :blue, ...)}, :began-at #inst "2013-08-08T07:29:30.134-00:00"}

Page 193: Clojure, Plain and Simple

{"players" : [ {"location" : 32,"name" : "ben"}, {"location" : 14,"name" : "maren"} ], "board" : [{"color" : "purple"}, ... {"color" : "orange","shortcut-to" : 62}, ... {"color" : "yellow","picture" : "candy-heart"} ... ], "decks" : { "unplayed" : [ "red", ["orange", "orange"], "peppermint-stick", ...], "played" : [ "red", "blue" ...] }, "began-at" : "2013-08-08T07:29:30Z"}

Page 194: Clojure, Plain and Simple

JSONJavaScript

EDNClojure

Page 195: Clojure, Plain and Simple

{:players [{:location 32, :name "ben"} {:location 14, :name "maren"}] :board [{:color :purple} ... {:color :orange, :shortcut-to 62} .... {:color :yellow, :picture :candy-heart} ...], :decks {:unplayed (:red [:orange :orange] :peppermint-stick ...), :played (:red, :blue, ...)}, :began-at #inst "2013-08-08T07:29:30.134-00:00"}

Page 196: Clojure, Plain and Simple

{:players [{:location 32, :name "ben"} {:location 14, :name "maren"}] :board [{:color :purple} ... {:color :orange, :shortcut-to 62} .... {:color :yellow, :picture :candy-heart} ...], :decks {:unplayed (:red [:orange :orange] :peppermint-stick ...), :played (:red, :blue, ...)}, :began-at #inst "2013-08-08T07:29:30.134-00:00"}

Custom Tagged Literal

Page 197: Clojure, Plain and Simple

{:id 54321 :players [{:location 32 :name "ben"} {:location 14 :name "maren"}] :board [{:color :purple} ... {:color :orange, :shortcut-to 62} .... {:color :yellow, :picture :candy-heart} ...] :decks {:unplayed (:red [:orange :orange] :peppermint-stick ...) :played (:red :blue ...)} :began-at #inst "2013-08-08T07:29:30.134-00:00"}

Page 198: Clojure, Plain and Simple

( )

ValuesFunctions

Page 199: Clojure, Plain and Simple

( )

ValuesFunctions References

Page 200: Clojure, Plain and Simple

Game Reference

Page 201: Clojure, Plain and Simple

Game Reference(def game-ref (atom (create-game ...)))

Page 202: Clojure, Plain and Simple

Game Reference(def game-ref (atom (create-game ...)))

Reference Constructor

Page 203: Clojure, Plain and Simple

Game Reference(def game-ref (atom (create-game ...)))

Reference Constructor

Initial Value

Page 204: Clojure, Plain and Simple

Game Reference(def game-ref (atom (create-game ...)))

(swap! game-ref take-next-move)

Atomic Succession

Page 205: Clojure, Plain and Simple

Game Reference(def game-ref (atom (create-game ...)))

(swap! game-ref take-next-move)

Atomic Succession

Fn to apply the ref’s current value to

Page 206: Clojure, Plain and Simple

(swap! game-ref add-player "Carter")

Game Reference(def game-ref (atom (create-game ...)))

(swap! game-ref take-next-move)

Additional args to fn

Page 207: Clojure, Plain and Simple

(swap! game-ref add-player "Carter")

Game Reference

(deref game-ref) => current-game-value

(def game-ref (atom (create-game ...)))

(swap! game-ref take-next-move)

Page 208: Clojure, Plain and Simple

(swap! game-ref add-player "Carter")

Game Reference

(deref game-ref) => current-game-value

(def game-ref (atom (create-game ...)))

(swap! game-ref take-next-move)

Observers can deref to get current state

Page 209: Clojure, Plain and Simple

(swap! game-ref add-player "Carter")

Game Reference

(deref game-ref) => current-game-value

(def game-ref (atom (create-game ...)))

(swap! game-ref take-next-move)

@game-ref => current-game-value

Observers can deref to get current state

Page 210: Clojure, Plain and Simple

Clojure’s Time Model

Page 211: Clojure, Plain and Simple

Clojure’s Time ModelState is the current value of an identity.

v1

Page 212: Clojure, Plain and Simple

Clojure’s Time ModelState is the current value of an identity.

An identity is series of values over time.

v1 v2 v3

Page 213: Clojure, Plain and Simple

Clojure’s Time ModelState is the current value of an identity.

An identity is series of values over time.

A reference to an identity allows updates and reads to it.

v1 v2 v3

Page 214: Clojure, Plain and Simple

Clojure’s Time ModelState is the current value of an identity.

An identity is series of values over time.

A reference to an identity allows updates and reads to it.

Values never change, the past never changes.

v1 v2 v3

Page 215: Clojure, Plain and Simple

( )

ValuesFunctions References

Identity

Page 216: Clojure, Plain and Simple

Data

Methods

References

Identity?

Page 217: Clojure, Plain and Simple

Add a new card type

Page 218: Clojure, Plain and Simple

(defn next-location [card board player] (let [spaces-after-player (->> board (drop (:location player))) next-color-id (find-index #(= (:color card) (:color %)))] (or next-color-id (:winning-location board))))

Page 219: Clojure, Plain and Simple

Picture cards

Candy Heart

Peppermint Stick

Ginger Bread

Gum Drop

Peanut Brittle

Lollypop

Ice Cream

Page 220: Clojure, Plain and Simple

(defn next-location [card board player] (let [spaces-after-player (->> board (drop (:location player))) next-color-id (find-index #(= (:color card) (:color %)))] (or next-color-id (:winning-location board))))

Page 221: Clojure, Plain and Simple

(defprotocol Card (next-location [card board player] "Determines the next location of the player"))

Page 222: Clojure, Plain and Simple

(defrecord ColorCard [color] Card (next-location [_ board player] ....)

(defrecord PictureCard [picture] Card (next-location [_ board player] (find-index #(= picture (:picture %)) board)))

....

(defprotocol Card (next-location [card board player] "Determines the next location of the player"))

Page 223: Clojure, Plain and Simple

Protocols are not just another

name for interfaces...

Page 224: Clojure, Plain and Simple

they allow you to add new

abstractions to existing types

Page 225: Clojure, Plain and Simple

(ns abstraction-a)

(defprotocol AbstractionA (foo [obj]))

Page 226: Clojure, Plain and Simple

(extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s] (str "foo-A-" (.toUpperCase s))))

(ns abstraction-a)

(defprotocol AbstractionA (foo [obj]))

Page 227: Clojure, Plain and Simple

(extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s] (str "foo-A-" (.toUpperCase s))))

(ns abstraction-a)

(defprotocol AbstractionA (foo [obj]))

(in-ns 'user)(require '[abstraction-a :as a])

(a/foo "Bar") => "foo-A-BAR"(a/foo nil) => "foo-A!"

Page 228: Clojure, Plain and Simple

(extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s] (str "foo-A-" (.toUpperCase s))))

(ns abstraction-a)

(defprotocol AbstractionA (foo [obj]))

(in-ns 'user)(require '[abstraction-a :as a])

(a/foo "Bar") => "foo-A-BAR"(a/foo nil) => "foo-A!"

(ns abstraction-b)

(defprotocol AbstractionB (foo [obj]))

Page 229: Clojure, Plain and Simple

(extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s] (str "foo-A-" (.toUpperCase s))))

(ns abstraction-a)

(defprotocol AbstractionA (foo [obj]))

(in-ns 'user)(require '[abstraction-a :as a])

(a/foo "Bar") => "foo-A-BAR"(a/foo nil) => "foo-A!"

(extend-protocol AbstractionB nil (foo [s] (str "foo-B!")) String (foo [s] (str "foo-B-" (.toLowerCase s))))

(ns abstraction-b)

(defprotocol AbstractionB (foo [obj]))

Page 230: Clojure, Plain and Simple

(extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s] (str "foo-A-" (.toUpperCase s))))

(ns abstraction-a)

(defprotocol AbstractionA (foo [obj]))

(in-ns 'user)(require '[abstraction-a :as a])

(a/foo "Bar") => "foo-A-BAR"(a/foo nil) => "foo-A!"

(extend-protocol AbstractionB nil (foo [s] (str "foo-B!")) String (foo [s] (str "foo-B-" (.toLowerCase s))))

(ns abstraction-b)

(defprotocol AbstractionB (foo [obj]))

(in-ns 'user)(require '[abstraction-b :as b])

(b/foo "Bar") => "foo-B-bar"(b/foo nil) => "foo-B!"

Page 231: Clojure, Plain and Simple

(extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s] (str "foo-A-" (.toUpperCase s))))

(ns abstraction-a)

(defprotocol AbstractionA (foo [obj]))

(in-ns 'user)(require '[abstraction-a :as a])

(a/foo "Bar") => "foo-A-BAR"(a/foo nil) => "foo-A!"

(extend-protocol AbstractionB nil (foo [s] (str "foo-B!")) String (foo [s] (str "foo-B-" (.toLowerCase s))))

(ns abstraction-b)

(defprotocol AbstractionB (foo [obj]))

(in-ns 'user)(require '[abstraction-b :as b])

(b/foo "Bar") => "foo-B-bar"(b/foo nil) => "foo-B!"

Polymorphic functions live in namespaces, not

complected on Class

Page 232: Clojure, Plain and Simple

( )

ValuesFunctions References

Identity

Page 233: Clojure, Plain and Simple

( )

ValuesFunctions References

Namespaces Identity

Page 234: Clojure, Plain and Simple

( )

ValuesFunctions References

Namespaces IdentityPolymorphism

Page 235: Clojure, Plain and Simple

Data

Methods

References

Identity?

Page 236: Clojure, Plain and Simple

Data

Methods

References

Namespace

Identity?

Page 237: Clojure, Plain and Simple

Data

Methods

References

Polymorphism

Namespace

Identity?

Page 238: Clojure, Plain and Simple

(->> (range 100000) (map inc) (reduce +))

Page 239: Clojure, Plain and Simple

(require '[clojure.core.reducers :as r])

(->> (range 100000) (r/map inc) (r/reduce +))

(->> (range 100000) (map inc) (reduce +))

Page 240: Clojure, Plain and Simple

(require '[clojure.core.reducers :as r])

(->> (range 100000) (r/map inc) (r/reduce +))

(->> (range 100000) (map inc) (reduce +))

Process sequences in parallel with ForkJoin

Page 241: Clojure, Plain and Simple

(require '[clojure.core.reducers :as r])

(->> (range 100000) (r/map inc) (r/reduce +))

(->> (range 100000) (map inc) (reduce +))

Process sequences in parallel with ForkJoin

The same “what”, different “how”

Page 242: Clojure, Plain and Simple

(extend-protocol CollFold nil (coll-fold [coll n combinef reducef] (combinef))

Object (coll-fold [coll n combinef reducef] ;;can't fold, single reduce (reduce reducef (combinef) coll))

clojure.lang.IPersistentVector (coll-fold [v n combinef reducef] (foldvec v n combinef reducef))

clojure.lang.PersistentHashMap (coll-fold [m n combinef reducef] (.fold m n combinef reducef fjinvoke fjtask fjfork fjjoin)))

Page 243: Clojure, Plain and Simple

Parallel Collections

Page 244: Clojure, Plain and Simple

Birthday party fun!

Page 245: Clojure, Plain and Simple

It is bound to happen...

Page 246: Clojure, Plain and Simple
Page 247: Clojure, Plain and Simple
Page 248: Clojure, Plain and Simple

I don’t want to go back to the

gum drops!

Page 249: Clojure, Plain and Simple
Page 250: Clojure, Plain and Simple

I can remember what the game looked like, why

can’t your program?!?

Page 251: Clojure, Plain and Simple

Clojure’s Time Model

A reference to an identity allows updates and reads to it.

Values never change, the past never changes.

v1 v2 v3

State is the current value of an identity.

An identity is series of values over time.

Page 252: Clojure, Plain and Simple

Clojure’s Time Model

A reference to an identity allows updates and reads to it.

Values never change, the past never changes.

v1 v2 v3

State is the current value of an identity.

An identity is series of values over time.

Observers can remember the past

Page 253: Clojure, Plain and Simple

(defn shadow-ref "Returns a ref that contains the time - 1 value of the given ref.In other words, shawdow-ref contains the value of ref before the lastupdate to it (e.g. swap!). " [ref] (let [shadow (atom nil)] (add-watch ref :shawdower (fn [_key _ref old-state _new-state] (reset! shadow old-state))) shadow))

(def old-game-ref (shadow-ref game-ref))

Page 254: Clojure, Plain and Simple

(defn undo-and-skip-card [game-ref old-game-ref] (let [alternate-reality (-> @old-game-ref skip-card take-next-move)] (reset! game-ref alternate-reality)))

(defn shadow-ref "Returns a ref that contains the time - 1 value of the given ref.In other words, shawdow-ref contains the value of ref before the lastupdate to it (e.g. swap!). " [ref] (let [shadow (atom nil)] (add-watch ref :shawdower (fn [_key _ref old-state _new-state] (reset! shadow old-state))) shadow))

(def old-game-ref (shadow-ref game-ref))

Page 255: Clojure, Plain and Simple

Clojure is a functional Lisp that targets the JVM and enables simpler software design.

Page 256: Clojure, Plain and Simple

How do you want to spend your

complexity budget?

Page 257: Clojure, Plain and Simple

Tradeoffs

Page 258: Clojure, Plain and Simple

TradeoffsDifferent way of thinking takes time.

Page 259: Clojure, Plain and Simple

TradeoffsDifferent way of thinking takes time.

Idiomatic Clojure is slower than idiomatic Java in micro benchmarks.

Page 260: Clojure, Plain and Simple

TradeoffsDifferent way of thinking takes time.

Idiomatic Clojure is slower than idiomatic Java in micro benchmarks.

Not as much structure provided (e.g. no familiar class structure), easier to make a mess.

Page 261: Clojure, Plain and Simple

TradeoffsDifferent way of thinking takes time.

Idiomatic Clojure is slower than idiomatic Java in micro benchmarks.

Not as much structure provided (e.g. no familiar class structure), easier to make a mess.

Tool support. Not many great IDE plugins conveniently available.

Page 262: Clojure, Plain and Simple

TradeoffsDifferent way of thinking takes time.

Idiomatic Clojure is slower than idiomatic Java in micro benchmarks.

Not as much structure provided (e.g. no familiar class structure), easier to make a mess.

Tool support. Not many great IDE plugins conveniently available.

Harder to hire for?

Page 263: Clojure, Plain and Simple

TradeoffsDifferent way of thinking takes time.

Idiomatic Clojure is slower than idiomatic Java in micro benchmarks.

Not as much structure provided (e.g. no familiar class structure), easier to make a mess.

Tool support. Not many great IDE plugins conveniently available.

Harder to hire for?

Page 264: Clojure, Plain and Simple

Simplicity

Ease

Real Tradeoffs

Page 265: Clojure, Plain and Simple

Thank you!

BenMabey.com

github.com/bmabey

@bmabey

Page 266: Clojure, Plain and Simple

( )Free

Read Watch Do

Free clojure.org clojure.org/cheatsheet clojure-doc.org Tutorials clojuredocs.org Examples

youtube.c/user/ClojureTV

infoq.com/Clojure/presentations

.com

Free clojure.org clojure.org/cheatsheet clojure-doc.org Tutorials clojuredocs.org Examples

youtube.c/user/ClojureTV

infoq.com/Clojure/presentations

Free clojure.org clojure.org/cheatsheet clojure-doc.org Tutorials clojuredocs.org Examples

youtube.c/user/ClojureTV

infoq.com/Clojure/presentations

$

clojure.org clojure.org/cheatsheet clojure-doc.org Tutorials clojuredocs.org Examples

$Clojure/Conj clojure-conj.org

Trainingclojure.com

$Clojure/Conj clojure-conj.org

Trainingclojure.com

$Clojure/Conj clojure-conj.org

Trainingclojure.com

Page 267: Clojure, Plain and Simple

Extra Slides

Page 268: Clojure, Plain and Simple

C# Asyncasync void Go() { _button.IsEnabled = false; string[] urls = "clojure.org www.albahari.com/nutshell/golang.org".Split(); int totalLength = 0; foreach (string url in urls) { var uri = new Uri ("http://" + url); byte[] data = await new WebClient().DownloadDataTaskAsync (uri); _results.Text += "Length of " + url + " is " + data.Length + totalLength += data.Length; } _results.Text += "Total length: " + totalLength;}

Page 269: Clojure, Plain and Simple

CSP in Go// Run the Web, Image, and Video searches concurrently, // and wait for all results.// No locks. No condition variables. No callbacks.

func Google(query string) (results []Result) { c := make(chan Result) go func() { c <- Web(query) } () go func() { c <- Image(query) } () go func() { c <- Video(query) } ()

for i := 0; i < 3; i++ { result := <-c results = append(results, result) } return}

// http://talks.golang.org/2012/concurrency.slide#46

Page 270: Clojure, Plain and Simple

Go in Clojure(use 'clojure.core.async)

(defn google [query] (let [c (chan)] (go (>! c (<! (web query)))) (go (>! c (<! (image query)))) (go (>! c (<! (video query))))

(go (loop [i 0 ret []] (if (= i 3) ret (recur (inc i) (conj ret (alt! [c t] ([v] v)))))))))

Page 271: Clojure, Plain and Simple

“APL is like a beautiful diamond - flawless, beautifully symmetrical. But you can't add anything to it. If you try to glue on another diamond, you don't get a bigger diamond. Lisp is like a ball of mud. Add more and it's still a ball of mud - it still looks like Lisp.”

Joel Moses, 1970s

Page 272: Clojure, Plain and Simple

Its my ball of mud!

Page 273: Clojure, Plain and Simple

“I remain unenthusiastic about actors.”

Rich Hickey

Page 274: Clojure, Plain and Simple

Erlang Actors in Clojure

;; http://puniverse.github.io/pulsar/

(use 'co.paralleluniverse.pulsar.core)

(let [actor (spawn #(receive :abc "yes!" [:why? answer] answer :else "oy"))] (! actor [:why? "because!"]) (join actor)) ; => "because!"

Page 275: Clojure, Plain and Simple

Color # in Deck

Red 6

Orange 4

Yellow 6

Green 4

Blue 6

Purple 4

Create a Card Deck

Page 276: Clojure, Plain and Simple

Create a Card Deckpublic class CardDeck {

! private Stack<Card> cards;

! private static final Map<String, Integer> CARD_GROUPS;! static {! ! CARD_GROUPS = new HashMap<String, Integer>();! ! CARD_GROUPS.put("Red", 4);! ! CARD_GROUPS.put("Orange", 4);! ! CARD_GROUPS.put("Yellow", 6);! ! CARD_GROUPS.put("Green", 4);! ! CARD_GROUPS.put("Blue", 6);! ! CARD_GROUPS.put("Purple", 4);

! }

Page 277: Clojure, Plain and Simple

Create a Card Deck

! private void addCardsToDeck() {! ! cards = new Stack<Card>();! ! // Add cards to deck based on color and number! ! for (Map.Entry<S,I> cardGroupEntry : CARD_GROUPS.entrySet()) {! ! ! String color = cardGroupEntry.getKey();! ! ! int numCardsInGroup = cardGroupEntry.getValue();! ! ! for (int i = 0; i < numCardsInGroup; i++) {! ! ! ! cards.push(new Card(color));! ! ! }! ! }! }

Page 278: Clojure, Plain and Simple

Create a Card Deck

Page 279: Clojure, Plain and Simple

Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6

Page 280: Clojure, Plain and Simple

Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6 :green 4 :blue 6 :purple 4})

Page 281: Clojure, Plain and Simple

Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6 :green 4 :blue 6 :purple 4})

(repeat 3 :red) => (:red :red :red)

Page 282: Clojure, Plain and Simple

Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6 :green 4 :blue 6 :purple 4})

(repeat 3 :red) => (:red :red :red)

(map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue))

Page 283: Clojure, Plain and Simple

Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6 :green 4 :blue 6 :purple 4})

(repeat 3 :red) => (:red :red :red)

(map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue))

(map (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => ((:red :red) (:blue :blue))

Page 284: Clojure, Plain and Simple

Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6 :green 4 :blue 6 :purple 4})

(repeat 3 :red) => (:red :red :red)

(map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue))

(map (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => ((:red :red) (:blue :blue))

(mapcat (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => (:red :red :blue :blue)

Page 285: Clojure, Plain and Simple

Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6 :green 4 :blue 6 :purple 4})

(repeat 3 :red) => (:red :red :red)

(map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue))

(map (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => ((:red :red) (:blue :blue))

(mapcat (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => (:red :red :blue :blue)

(defn create-deck [face-freqs]

Page 286: Clojure, Plain and Simple

Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6 :green 4 :blue 6 :purple 4})

(repeat 3 :red) => (:red :red :red)

(map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue))

(map (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => ((:red :red) (:blue :blue))

(mapcat (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => (:red :red :blue :blue)

(defn create-deck [face-freqs] (mapcat (fn [[face freq]] (repeat freq face)) face-freqs))

Page 287: Clojure, Plain and Simple

Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6 :green 4 :blue 6 :purple 4})

(repeat 3 :red) => (:red :red :red)

(map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue))

(map (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => ((:red :red) (:blue :blue))

(mapcat (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => (:red :red :blue :blue)

(defn create-deck [face-freqs] (mapcat (fn [[face freq]] (repeat freq face)) face-freqs))

(def deck

Page 288: Clojure, Plain and Simple

Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6 :green 4 :blue 6 :purple 4})

(repeat 3 :red) => (:red :red :red)

(map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue))

(map (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => ((:red :red) (:blue :blue))

(mapcat (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => (:red :red :blue :blue)

(defn create-deck [face-freqs] (mapcat (fn [[face freq]] (repeat freq face)) face-freqs))

(def deck (create-deck card-counts))

Page 289: Clojure, Plain and Simple

Create a Card Deck! private void addCardsToDeck() {! ! cards = new Stack<Card>();! ! // Add cards to deck based on color and number! ! for (Map.Entry<S,I> cardGroupEntry : CARD_GROUPS.entrySet()) {! ! ! String color = cardGroupEntry.getKey();! ! ! int numCardsInGroup = cardGroupEntry.getValue();! ! ! for (int i = 0; i < numCardsInGroup; i++) {! ! ! ! cards.push(new Card(color));! ! ! }! ! }! }

Page 290: Clojure, Plain and Simple

Create a Card Deck! private void addCardsToDeck() {! ! cards = new Stack<Card>();! ! // Add cards to deck based on color and number! ! for (Map.Entry<S,I> cardGroupEntry : CARD_GROUPS.entrySet()) {! ! ! String color = cardGroupEntry.getKey();! ! ! int numCardsInGroup = cardGroupEntry.getValue();! ! ! for (int i = 0; i < numCardsInGroup; i++) {! ! ! ! cards.push(new Card(color));! ! ! }! ! }! }

(defn create-deck [face-freqs] (mapcat (fn [[face freq]] (repeat freq face)) face-freqs))