Upload
ben-mabey
View
2.325
Download
2
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
Ben Mabey
Plain & SimpleClojure
@bmabey
Utah Java User’s Group, Aug 15 2013
redbrainlabs.com
"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
Clojure is a functional Lisp that targets the JVM and enables simpler software design.
Clojure is a functional Lisp that targets the JVM and enables simpler software design.
Read Eval Print
Loop
Read Eval Print
Loop
((Live))
Macros
(if (= code :RED) (do (launch-missiles!) (sound-the-alarm!)))
‘do’ implies side effects
(if (= code :RED) (do (launch-missiles!) (sound-the-alarm!)))
(if (= code :RED) (do (launch-missiles!) (sound-the-alarm!)))
(when (= code :RED) (launch-missiles!) (sound-the-alarm!))
(defmacro when "Evaluates test. If logical true, evaluates body in an implicit do." [test & body] (list 'if test (cons 'do body)))
(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!)))
(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!)))
Paradigms as Libraries
Paradigms as LibrariesDesign by Contract a’la Eiffel - core.contracts
Paradigms as LibrariesDesign by Contract a’la Eiffel - core.contracts
Logic Programming a’la Prolog - core.logic
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
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
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
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...
“If you give someone Fortran, he has Fortran. If you give someone Lisp, he has any language he pleases.”
Guy Steele
Clojure is a functional Lisp that targets the JVM and enables simpler software design.
Constructor
new Widget("gizmo");
(Widget. "gizmo")
Static Member
Math.PI
Math/PI
Instance Method
string.trim();
(.trim string)
Chained Access
person.getAddress().getState().getCode();
(.. person getAddress getState getCode)
Chained Access
person.getAddress().getState().getCode();
(.. person getAddress getState getCode)
Count ’em, 3 vs 1 pair!
Chained Access
person.getAddress().getState().getCode();
(.. person getAddress getState getCode)
(macroexpand '(.. person getAddress getState getCode))
(. (. (. person getAddress) getState) getCode)
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
Multiple Updatesperson.setFirstName("Ben");person.setLastName("Mabey");person.makePresenter();
(doto person (.setFirstName "Ben") (.setLastName "Mabey") .makePresenter)
Multiple Updatesperson.setFirstName("Ben");person.setLastName("Mabey");person.makePresenter();
(doto person (.setFirstName "Ben") (.setLastName "Mabey") .makePresenter)
‘person’ is implicit to method calls
Multiple Updatesperson.setFirstName("Ben");person.setLastName("Mabey");person.makePresenter();
(doto person (.setFirstName "Ben") (.setLastName "Mabey") .makePresenter)
Again, ‘do’ signifies side effects
Implementing Interfaces
new Runnable() { public void run() { System.out.println("Hello World"); } };
(reify Runnable (run [] (println "Hello")))
Clojure is a functional Lisp that targets the JVM and enables simpler software design.
Functional
FunctionalProgramming with Values
FunctionalProgramming with Values
First Class Functions
FunctionalProgramming with Values
First Class Functions
Laziness
FunctionalProgramming with Values
First Class Functions
Laziness
public static int square(int x) { return x * x; }
public static int square(int x) { return x * x; }
Value
public static int square(int x) { return x * x; }
Value
} Pure Function
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; }
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
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
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(); }
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
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
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
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); }
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); }
ReferencesValues
ReferencesValuesprimitives
java.lang wrappers(Boolean, Byte, Character,
Double, Float, Integer, Long, Short, String)
other wrappers...e.g. UUID, URL, URI
ReferencesValuesprimitives
java.lang wrappers(Boolean, Byte, Character,
Double, Float, Integer, Long, Short, String)
other wrappers...e.g. UUID, URL, URI
DateBigInteger,BigDecimal, etc.
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...
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
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
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
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
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
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
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
“Best practice” but not idiomatic...
“Best practice” but not idiomatic...
more difficult than it should be!
“Best practice” but not idiomatic...
more difficult than it should be!
ReferencesValues
ReferencesValuesprimitives
Java’s wrappers & libs
ReferencesValuesprimitives
Java’s wrappers & libs
CollectionsList, Vector, HashMap,
TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords
Explicit Ref Typesatom
refagent
ReferencesValuesprimitives
Java’s wrappers & libs
CollectionsList, Vector, HashMap,
TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords
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.
Explicit Ref Typesatom
refagent
ReferencesValuesprimitives
Java’s wrappers & libs
CollectionsList, Vector, HashMap,
TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords
deftype
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
Explicit Ref Typesatom
refPersistent Collections
ReferencesValuesprimitives
Java’s wrappers & libs
CollectionsList, Vector, HashMap,
TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords
+ They are values
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
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
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!
Structural Sharing
Structural Sharing
String brother = "brother";String the = brother.substring(3, 6);
Structural Sharing
String brother = "brother";String the = brother.substring(3, 6);
http://www.slreynolds.net/talks/clojure/collections/index.html
Learn Morehttp://www.slreynolds.net/talks/clojure/collections/index.html
http://pragprog.com/magazines/2011-07/clojure-collections
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
Transients
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(); }
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)
Nesting
(def data {:nested [0 1 {:double "nested"}]})
Nesting
(def data {:nested [0 1 {:double "nested"}]})
(get-in data [:nested 2 :double])
Nesting
(def data {:nested [0 1 {:double "nested"}]})
(get-in data [:nested 2 :double])
Nesting
First map key Index into vector
Nested map key
(def data {:nested [0 1 {:double "nested"}]})
(get-in data [:nested 2 :double]) > "nested"
Nesting
(update-in data [:nested 2 :double] upper-case)
(get-in data [:nested 2 :double]) > "nested"
(def data {:nested [0 1 {:double "nested"}]})
Nesting
(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
(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
(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
(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")
(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
(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
(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"}]}
(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.
Clojure is a functional Lisp that targets the JVM and enables simpler software design.
Simpler?
Simpler?Simple Made Easy Rich Hickeyhttp://www.infoq.com/presentations/Simple-Made-Easy
Simple != Easy
Easy
Easyease < aise < adjacens
lie near
i.e. familiar, convenient,
near to our skill set or current understanding
Easyease < aise < adjacens
lie near
i.e. familiar, convenient,
near to our skill set or current understanding
always relative! opposite of hard
Simple
Simplesim - plex
one fold/braid
opposite of complex
Simplesim - plex
one fold/braid
opposite of complex
Simplesim - plex
one fold/braid
no interleaving!one concept,
one dimensionone role, but maybe multiple operations
opposite of complex
Simple
Simple
Complex
Complect
To interleave, entwine, braid
http://tinyurl.com/candy-land-pdf
How would you model the game
state using objects?
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 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;! }}
{: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 ...)}}
{: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
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
It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.Alan Perlis
Data
Methods
Data
( )
( )
Values
( )
ValuesFunctions
Create a Game Board
Create a Game Board
Create a Game Board• 129 colored spaces• 6 colors in repeating
sequence:1. Purple2.Yellow3.Blue4.Orange5.Green6.Red
Create a Game Board! private static final int NUMBER_SPACES = 129;! public static String[] COLOR_SEQUENCE = { ! ! "Purple",! ! "Yellow",! ! "Blue",! ! "Orange",! ! "Green",! ! "Red"! };!
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));! ! }! ! ... }! !
(def colors [:purple :yellow :blue :orange :green :red])
(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
(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.
(def colors [:purple :yellow :blue :orange :green :red])
(cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)
(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)
(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
(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));! ! }
(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)
(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
(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
(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)
(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})
(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})
(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
(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))
(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
(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
Play the game
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;! }
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;! }
}
! // 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
! // 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
(defn next-move "Takes the game forward one move by drawing a card for thecurrent player and moving them accordingly." [game]
(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
(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
(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
(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)
(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)
(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]
(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
(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)]
(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))))
(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))))
(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))))
Play the game
(defn play [game] (->> (iterate next-move game) (filter game-over?) first))
(defn play [game] (filter game-over?) first))
Play the game
(->> (iterate next-move game)
Text
(defn play [game] (filter game-over?) first))
Play the game
(->> (iterate next-move game)
Textv1
(defn play [game] (filter game-over?) first))
Play the game
(->> (iterate next-move game)
Textv1
next-move
v2
(defn play [game] (filter game-over?) first))
Play the game
(->> (iterate next-move game)
Textv1
next-move
v2 v3
next-move
(defn play [game] (filter game-over?) first))
Play the game
(->> (iterate next-move game)
Textv1
next-move
v2 v3
next-move next-move
...
(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)
Play the game
(defn play [game] (->> (iterate next-move game) (filter game-over?) first))
(filter game-over?) first))
Predicate function
Play the game
(defn play [game] (->> (iterate next-move game) (filter game-over?) first))
(filter game-over?) first))
Return the ended game value
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());! }
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());! }
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!
Data
Methods
Data
Methods
References
( )
ValuesFunctions
Put it on the Web!
{: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"}
{"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"}
JSONJavaScript
EDNClojure
{: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"}
{: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
{: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"}
( )
ValuesFunctions
( )
ValuesFunctions References
Game Reference
Game Reference(def game-ref (atom (create-game ...)))
Game Reference(def game-ref (atom (create-game ...)))
Reference Constructor
Game Reference(def game-ref (atom (create-game ...)))
Reference Constructor
Initial Value
Game Reference(def game-ref (atom (create-game ...)))
(swap! game-ref take-next-move)
Atomic Succession
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
(swap! game-ref add-player "Carter")
Game Reference(def game-ref (atom (create-game ...)))
(swap! game-ref take-next-move)
Additional args to fn
(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)
(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
(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
Clojure’s Time Model
Clojure’s Time ModelState is the current value of an identity.
v1
Clojure’s Time ModelState is the current value of an identity.
An identity is series of values over time.
v1 v2 v3
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
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
( )
ValuesFunctions References
Identity
Data
Methods
References
Identity?
Add a new card type
(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))))
Picture cards
Candy Heart
Peppermint Stick
Ginger Bread
Gum Drop
Peanut Brittle
Lollypop
Ice Cream
(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))))
(defprotocol Card (next-location [card board player] "Determines the next location of the player"))
(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"))
Protocols are not just another
name for interfaces...
they allow you to add new
abstractions to existing types
(ns abstraction-a)
(defprotocol AbstractionA (foo [obj]))
(extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s] (str "foo-A-" (.toUpperCase s))))
(ns abstraction-a)
(defprotocol AbstractionA (foo [obj]))
(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 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]))
(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]))
(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!"
(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
( )
ValuesFunctions References
Identity
( )
ValuesFunctions References
Namespaces Identity
( )
ValuesFunctions References
Namespaces IdentityPolymorphism
Data
Methods
References
Identity?
Data
Methods
References
Namespace
Identity?
Data
Methods
References
Polymorphism
Namespace
Identity?
(->> (range 100000) (map inc) (reduce +))
(require '[clojure.core.reducers :as r])
(->> (range 100000) (r/map inc) (r/reduce +))
(->> (range 100000) (map inc) (reduce +))
(require '[clojure.core.reducers :as r])
(->> (range 100000) (r/map inc) (r/reduce +))
(->> (range 100000) (map inc) (reduce +))
Process sequences in parallel with ForkJoin
(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”
(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)))
Parallel Collections
Birthday party fun!
It is bound to happen...
I don’t want to go back to the
gum drops!
I can remember what the game looked like, why
can’t your program?!?
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.
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
(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))
(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))
Clojure is a functional Lisp that targets the JVM and enables simpler software design.
How do you want to spend your
complexity budget?
Tradeoffs
TradeoffsDifferent way of thinking takes time.
TradeoffsDifferent way of thinking takes time.
Idiomatic Clojure is slower than idiomatic Java in micro benchmarks.
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.
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.
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?
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?
Simplicity
Ease
Real Tradeoffs
Thank you!
BenMabey.com
github.com/bmabey
@bmabey
( )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
Extra Slides
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;}
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
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)))))))))
“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
Its my ball of mud!
“I remain unenthusiastic about actors.”
Rich Hickey
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!"
Color # in Deck
Red 6
Orange 4
Yellow 6
Green 4
Blue 6
Purple 4
Create a Card Deck
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);
! }
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));! ! ! }! ! }! }
Create a Card Deck
Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6
Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6 :green 4 :blue 6 :purple 4})
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)
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))
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))
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)
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]
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))
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 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))
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));! ! ! }! ! }! }
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))