SlideShare a Scribd company logo
1 of 290
Download to read offline
Ben Mabey
Plain & Simple
Clojure
@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 Libraries
Design by Contract a’la Eiffel - core.contracts
Paradigms as Libraries
Design by Contract a’la Eiffel - core.contracts
Logic Programming a’la Prolog - core.logic
Paradigms as Libraries
Design by Contract a’la Eiffel - core.contracts
Logic Programming a’la Prolog - core.logic
Lightweight threads + channels a’la Go - core.async
Paradigms as Libraries
Design 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 Libraries
Design 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 Libraries
Design 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 Updates
person.setFirstName("Ben");
person.setLastName("Mabey");
person.makePresenter();
(doto person
(.setFirstName "Ben")
(.setLastName "Mabey")
.makePresenter)
Multiple Updates
person.setFirstName("Ben");
person.setLastName("Mabey");
person.makePresenter();
(doto person
(.setFirstName "Ben")
(.setLastName "Mabey")
.makePresenter)
‘person’ is implicit to method calls
Multiple Updates
person.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
Functional
Programming with Values
Functional
Programming with Values
First Class Functions
Functional
Programming with Values
First Class Functions
Laziness
Functional
Programming 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
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
other wrappers...
e.g. UUID, URL, URI
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
other wrappers...
e.g. UUID, URL, URI
Date
BigInteger,BigDecimal, etc.
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
other wrappers...
e.g. UUID, URL, URI
Date
BigInteger,BigDecimal, etc.
Collections
ArrayList,ArrayDeque,TreeSet,
HashSet, TreeMap, HashMap,
LinkedList,PriorityQueue,etc...
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
other wrappers...
e.g. UUID, URL, URI
Date
BigInteger,BigDecimal, etc.
Collections
ArrayList,ArrayDeque,TreeSet,
HashSet, TreeMap, HashMap,
LinkedList,PriorityQueue,etc...
Default for domain objects
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
other wrappers...
e.g. UUID, URL, URI
Date
BigInteger,BigDecimal, etc.
Libraries
joda-time, joda-money
google-guava
Collections
ArrayList,ArrayDeque,TreeSet,
HashSet, TreeMap, HashMap,
LinkedList,PriorityQueue,etc...
Default for domain objects
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
other wrappers...
e.g. UUID, URL, URI
Date
BigInteger,BigDecimal, etc.
Libraries
joda-time, joda-money
google-guava
Collections
ArrayList,ArrayDeque,TreeSet,
HashSet, TreeMap, HashMap,
LinkedList,PriorityQueue,etc...
Immutable Collections!
Default for domain objects
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
other wrappers...
e.g. UUID, URL, URI
Date
BigInteger,BigDecimal, etc.
Libraries
joda-time, joda-money
google-guava
Collections
ArrayList,ArrayDeque,TreeSet,
HashSet, TreeMap, HashMap,
LinkedList,PriorityQueue,etc...
Immutable Collections!
+ They are values!
Default for domain objects
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
other wrappers...
e.g. UUID, URL, URI
Date
BigInteger,BigDecimal, etc.
Libraries
joda-time, joda-money
google-guava
Collections
ArrayList,ArrayDeque,TreeSet,
HashSet, TreeMap, HashMap,
LinkedList,PriorityQueue,etc...
Immutable Collections!
+ They are values!
- Copy on write (very good impls though)
Default for domain objects
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
other wrappers...
e.g. UUID, URL, URI
Date
BigInteger,BigDecimal, etc.
Libraries
joda-time, joda-money
google-guava
Collections
ArrayList,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
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
other wrappers...
e.g. UUID, URL, URI
Date
BigInteger,BigDecimal, etc.
Libraries
joda-time, joda-money
google-guava
Collections
ArrayList,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
ReferencesValues
primitives
Java’s wrappers & libs
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Vector, HashMap,
TreeMap, ArrayMap,
StructMap, Queue, HashSet,
TreeSet, LazySeq, defrecords
Explicit Ref Types
atom
ref
agent
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Vector, HashMap,
TreeMap, ArrayMap,
StructMap, Queue, HashSet,
TreeSet, LazySeq, defrecords
Explicit Ref Types
atom
ref
agent
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Vector, HashMap,
TreeMap, ArrayMap,
StructMap, Queue, HashSet,
TreeSet, LazySeq, defrecords
How Clojure addresses
the non-functional aspect
of programs, i.e. state.
Explicit Ref Types
atom
ref
agent
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Vector, HashMap,
TreeMap, ArrayMap,
StructMap, Queue, HashSet,
TreeSet, LazySeq, defrecords
deftype
Explicit Ref Types
atom
ref
agent
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Vector, HashMap,
TreeMap, ArrayMap,
StructMap, Queue, HashSet,
TreeSet, LazySeq, defrecords Java libs for interop
deftype
Explicit Ref Types
atom
ref
Persistent Collections
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Vector, HashMap,
TreeMap, ArrayMap,
StructMap, Queue, HashSet,
TreeSet, LazySeq, defrecords
+ They are values
Explicit Ref Types
atom
ref
Persistent Collections
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Vector, HashMap,
TreeMap, ArrayMap,
StructMap, Queue, HashSet,
TreeSet, LazySeq, defrecords
+ They are values
+ Structural Sharing
+ Memory efficient
+ Fast
Explicit Ref Types
atom
ref
Persistent Collections
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Vector, HashMap,
TreeMap, ArrayMap,
StructMap, Queue, HashSet,
TreeSet, LazySeq, defrecords
+ They are values
+ Structural Sharing
+ Memory efficient
+ Fast
+ Everything nests
Explicit Ref Types
atom
ref
Persistent Collections
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, 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 More
http://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 1
assoc log32(n) log(n) log32(n)
dissoc log32(n) log(n)
disj log32(n) log(n)
nth log32(n)
n n n
get log32(n) log(n) log32(n) log(n) log32(n)
pop
1 1 1 1
peek
1 1 1 1
count
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 Hickey
http://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 dimension
one 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
( )
Values
Functions
Create a Game Board
Create a Game Board
Create a Game Board
• 129 colored spaces
• 6 colors in repeating
sequence:
1. Purple
2.Yellow
3.Blue
4.Orange
5.Green
6.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 game
public 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 game
public 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 game
public 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 game
public 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 the
current player and moving them accordingly."
[game]
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[game]
Docstring attached as metadata
(defn next-move
"Takes the game forward one move by drawing a card for the
current 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 the
current 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 the
current 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 the
current 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 the
current 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 the
current 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 the
current 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 the
current 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 the
current 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 the
current 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)
Text
v1
(defn play [game]
(filter game-over?)
first))
Play the game
(->> (iterate next-move game)
Text
v1
next-move
v2
(defn play [game]
(filter game-over?)
first))
Play the game
(->> (iterate next-move game)
Text
v1
next-move
v2 v3
next-move
(defn play [game]
(filter game-over?)
first))
Play the game
(->> (iterate next-move game)
Text
v1
next-move
v2 v3
next-move next-move
...
(defn play [game]
(filter game-over?)
first))
Play the game
(->> (iterate next-move game)
Text
v1
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
( )
Values
Functions
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"}
( )
Values
Functions
( )
Values
Functions 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 Model
State is the current value of an identity.
v1
Clojure’s Time Model
State is the current value of an identity.
An identity is series of values over time.
v1 v2 v3
Clojure’s Time Model
State 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 Model
State 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
( )
Values
Functions 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
( )
Values
Functions References
Identity
( )
Values
Functions References
Namespaces Identity
( )
Values
Functions 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 las
update 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 las
update 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
Tradeoffs
Different way of thinking takes time.
Tradeoffs
Different way of thinking takes time.
Idiomatic Clojure is slower than idiomatic Java
in micro benchmarks.
Tradeoffs
Different 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.
Tradeoffs
Different 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.
Tradeoffs
Different 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?
Tradeoffs
Different 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
Training
clojure.com
$
Clojure/Conj
clojure-conj.org
Training
clojure.com
$
Clojure/Conj
clojure-conj.org
Training
clojure.com
Extra Slides
C# Async
async 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 Deck
public 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))

More Related Content

What's hot

Alternatives of JPA/Hibernate
Alternatives of JPA/HibernateAlternatives of JPA/Hibernate
Alternatives of JPA/HibernateSunghyouk Bae
 
Exploring Clojurescript
Exploring ClojurescriptExploring Clojurescript
Exploring ClojurescriptLuke Donnet
 
Clojure for Java developers - Stockholm
Clojure for Java developers - StockholmClojure for Java developers - Stockholm
Clojure for Java developers - StockholmJan Kronquist
 
Kotlin Developer Starter in Android projects
Kotlin Developer Starter in Android projectsKotlin Developer Starter in Android projects
Kotlin Developer Starter in Android projectsBartosz Kosarzycki
 
Python 3.6 Features 20161207
Python 3.6 Features 20161207Python 3.6 Features 20161207
Python 3.6 Features 20161207Jay Coskey
 
Java7 New Features and Code Examples
Java7 New Features and Code ExamplesJava7 New Features and Code Examples
Java7 New Features and Code ExamplesNaresh Chintalcheru
 
The Kotlin Programming Language
The Kotlin Programming LanguageThe Kotlin Programming Language
The Kotlin Programming Languageintelliyole
 
Why GC is eating all my CPU?
Why GC is eating all my CPU?Why GC is eating all my CPU?
Why GC is eating all my CPU?Roman Elizarov
 
Introduction to clojure
Introduction to clojureIntroduction to clojure
Introduction to clojureAbbas Raza
 
What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)Pavlo Baron
 
A brief tour of modern Java
A brief tour of modern JavaA brief tour of modern Java
A brief tour of modern JavaSina Madani
 
New Features Of JDK 7
New Features Of JDK 7New Features Of JDK 7
New Features Of JDK 7Deniz Oguz
 
Introduction to kotlin + spring boot demo
Introduction to kotlin + spring boot demoIntroduction to kotlin + spring boot demo
Introduction to kotlin + spring boot demoMuhammad Abdullah
 
Generics Past, Present and Future (Latest)
Generics Past, Present and Future (Latest)Generics Past, Present and Future (Latest)
Generics Past, Present and Future (Latest)RichardWarburton
 
06 Java Language And OOP Part VI
06 Java Language And OOP Part VI06 Java Language And OOP Part VI
06 Java Language And OOP Part VIHari Christian
 
Kotlin Slides from Devoxx 2011
Kotlin Slides from Devoxx 2011Kotlin Slides from Devoxx 2011
Kotlin Slides from Devoxx 2011Andrey Breslav
 
SQL Server Select Topics
SQL Server Select TopicsSQL Server Select Topics
SQL Server Select TopicsJay Coskey
 

What's hot (20)

Alternatives of JPA/Hibernate
Alternatives of JPA/HibernateAlternatives of JPA/Hibernate
Alternatives of JPA/Hibernate
 
Seeking Clojure
Seeking ClojureSeeking Clojure
Seeking Clojure
 
Exploring Clojurescript
Exploring ClojurescriptExploring Clojurescript
Exploring Clojurescript
 
Clojure for Java developers - Stockholm
Clojure for Java developers - StockholmClojure for Java developers - Stockholm
Clojure for Java developers - Stockholm
 
Kotlin Developer Starter in Android projects
Kotlin Developer Starter in Android projectsKotlin Developer Starter in Android projects
Kotlin Developer Starter in Android projects
 
Java 7 New Features
Java 7 New FeaturesJava 7 New Features
Java 7 New Features
 
Python 3.6 Features 20161207
Python 3.6 Features 20161207Python 3.6 Features 20161207
Python 3.6 Features 20161207
 
Java7 New Features and Code Examples
Java7 New Features and Code ExamplesJava7 New Features and Code Examples
Java7 New Features and Code Examples
 
The Kotlin Programming Language
The Kotlin Programming LanguageThe Kotlin Programming Language
The Kotlin Programming Language
 
Why GC is eating all my CPU?
Why GC is eating all my CPU?Why GC is eating all my CPU?
Why GC is eating all my CPU?
 
Introduction to clojure
Introduction to clojureIntroduction to clojure
Introduction to clojure
 
What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)
 
Requery overview
Requery overviewRequery overview
Requery overview
 
A brief tour of modern Java
A brief tour of modern JavaA brief tour of modern Java
A brief tour of modern Java
 
New Features Of JDK 7
New Features Of JDK 7New Features Of JDK 7
New Features Of JDK 7
 
Introduction to kotlin + spring boot demo
Introduction to kotlin + spring boot demoIntroduction to kotlin + spring boot demo
Introduction to kotlin + spring boot demo
 
Generics Past, Present and Future (Latest)
Generics Past, Present and Future (Latest)Generics Past, Present and Future (Latest)
Generics Past, Present and Future (Latest)
 
06 Java Language And OOP Part VI
06 Java Language And OOP Part VI06 Java Language And OOP Part VI
06 Java Language And OOP Part VI
 
Kotlin Slides from Devoxx 2011
Kotlin Slides from Devoxx 2011Kotlin Slides from Devoxx 2011
Kotlin Slides from Devoxx 2011
 
SQL Server Select Topics
SQL Server Select TopicsSQL Server Select Topics
SQL Server Select Topics
 

Similar to Clojure, Plain and Simple

jimmy hacking (at) Microsoft
jimmy hacking (at) Microsoftjimmy hacking (at) Microsoft
jimmy hacking (at) MicrosoftJimmy Schementi
 
Iron Languages - NYC CodeCamp 2/19/2011
Iron Languages - NYC CodeCamp 2/19/2011Iron Languages - NYC CodeCamp 2/19/2011
Iron Languages - NYC CodeCamp 2/19/2011Jimmy Schementi
 
Scala clojure techday_2011
Scala clojure techday_2011Scala clojure techday_2011
Scala clojure techday_2011Thadeu Russo
 
Jug trojmiasto 2014.04.24 tricky stuff in java grammar and javac
Jug trojmiasto 2014.04.24  tricky stuff in java grammar and javacJug trojmiasto 2014.04.24  tricky stuff in java grammar and javac
Jug trojmiasto 2014.04.24 tricky stuff in java grammar and javacAnna Brzezińska
 
Productive Programming in Java 8 - with Lambdas and Streams
Productive Programming in Java 8 - with Lambdas and Streams Productive Programming in Java 8 - with Lambdas and Streams
Productive Programming in Java 8 - with Lambdas and Streams Ganesh Samarthyam
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing UpDavid Padbury
 
Eric Lafortune - The Jack and Jill build system
Eric Lafortune - The Jack and Jill build systemEric Lafortune - The Jack and Jill build system
Eric Lafortune - The Jack and Jill build systemGuardSquare
 
Carlo Sciolla - Above and beyond type systems with clojure.spec - Codemotion ...
Carlo Sciolla - Above and beyond type systems with clojure.spec - Codemotion ...Carlo Sciolla - Above and beyond type systems with clojure.spec - Codemotion ...
Carlo Sciolla - Above and beyond type systems with clojure.spec - Codemotion ...Codemotion
 
UniRx - Reactive Extensions for Unity(EN)
UniRx - Reactive Extensions for Unity(EN)UniRx - Reactive Extensions for Unity(EN)
UniRx - Reactive Extensions for Unity(EN)Yoshifumi Kawai
 
purely_functional_play_framework_application
purely_functional_play_framework_applicationpurely_functional_play_framework_application
purely_functional_play_framework_applicationNaoki Aoyama
 
Exploring SharePoint with F#
Exploring SharePoint with F#Exploring SharePoint with F#
Exploring SharePoint with F#Talbott Crowell
 
Clojure - A new Lisp
Clojure - A new LispClojure - A new Lisp
Clojure - A new Lispelliando dias
 

Similar to Clojure, Plain and Simple (20)

jimmy hacking (at) Microsoft
jimmy hacking (at) Microsoftjimmy hacking (at) Microsoft
jimmy hacking (at) Microsoft
 
Iron Languages - NYC CodeCamp 2/19/2011
Iron Languages - NYC CodeCamp 2/19/2011Iron Languages - NYC CodeCamp 2/19/2011
Iron Languages - NYC CodeCamp 2/19/2011
 
Java
JavaJava
Java
 
Scala clojure techday_2011
Scala clojure techday_2011Scala clojure techday_2011
Scala clojure techday_2011
 
Jug trojmiasto 2014.04.24 tricky stuff in java grammar and javac
Jug trojmiasto 2014.04.24  tricky stuff in java grammar and javacJug trojmiasto 2014.04.24  tricky stuff in java grammar and javac
Jug trojmiasto 2014.04.24 tricky stuff in java grammar and javac
 
Productive Programming in Java 8 - with Lambdas and Streams
Productive Programming in Java 8 - with Lambdas and Streams Productive Programming in Java 8 - with Lambdas and Streams
Productive Programming in Java 8 - with Lambdas and Streams
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
 
Core java
Core javaCore java
Core java
 
Eric Lafortune - The Jack and Jill build system
Eric Lafortune - The Jack and Jill build systemEric Lafortune - The Jack and Jill build system
Eric Lafortune - The Jack and Jill build system
 
Basics of JavaScript
Basics of JavaScriptBasics of JavaScript
Basics of JavaScript
 
Java 5 and 6 New Features
Java 5 and 6 New FeaturesJava 5 and 6 New Features
Java 5 and 6 New Features
 
Carlo Sciolla - Above and beyond type systems with clojure.spec - Codemotion ...
Carlo Sciolla - Above and beyond type systems with clojure.spec - Codemotion ...Carlo Sciolla - Above and beyond type systems with clojure.spec - Codemotion ...
Carlo Sciolla - Above and beyond type systems with clojure.spec - Codemotion ...
 
UniRx - Reactive Extensions for Unity(EN)
UniRx - Reactive Extensions for Unity(EN)UniRx - Reactive Extensions for Unity(EN)
UniRx - Reactive Extensions for Unity(EN)
 
Twins: OOP and FP
Twins: OOP and FPTwins: OOP and FP
Twins: OOP and FP
 
Introduction to Qt
Introduction to QtIntroduction to Qt
Introduction to Qt
 
Clojure intro
Clojure introClojure intro
Clojure intro
 
basic_java.ppt
basic_java.pptbasic_java.ppt
basic_java.ppt
 
purely_functional_play_framework_application
purely_functional_play_framework_applicationpurely_functional_play_framework_application
purely_functional_play_framework_application
 
Exploring SharePoint with F#
Exploring SharePoint with F#Exploring SharePoint with F#
Exploring SharePoint with F#
 
Clojure - A new Lisp
Clojure - A new LispClojure - A new Lisp
Clojure - A new Lisp
 

More from Ben Mabey

PCA for the uninitiated
PCA for the uninitiatedPCA for the uninitiated
PCA for the uninitiatedBen Mabey
 
Cucumber: Automating the Requirements Language You Already Speak
Cucumber: Automating the Requirements Language You Already SpeakCucumber: Automating the Requirements Language You Already Speak
Cucumber: Automating the Requirements Language You Already SpeakBen Mabey
 
Writing Software not Code with Cucumber
Writing Software not Code with CucumberWriting Software not Code with Cucumber
Writing Software not Code with CucumberBen Mabey
 
Outside-In Development With Cucumber
Outside-In Development With CucumberOutside-In Development With Cucumber
Outside-In Development With CucumberBen Mabey
 
Disconnecting the Database with ActiveRecord
Disconnecting the Database with ActiveRecordDisconnecting the Database with ActiveRecord
Disconnecting the Database with ActiveRecordBen Mabey
 
SVD and the Netflix Dataset
SVD and the Netflix DatasetSVD and the Netflix Dataset
SVD and the Netflix DatasetBen Mabey
 
The WHY behind TDD/BDD and the HOW with RSpec
The WHY behind TDD/BDD and the HOW with RSpecThe WHY behind TDD/BDD and the HOW with RSpec
The WHY behind TDD/BDD and the HOW with RSpecBen Mabey
 

More from Ben Mabey (8)

PCA for the uninitiated
PCA for the uninitiatedPCA for the uninitiated
PCA for the uninitiated
 
Github flow
Github flowGithub flow
Github flow
 
Cucumber: Automating the Requirements Language You Already Speak
Cucumber: Automating the Requirements Language You Already SpeakCucumber: Automating the Requirements Language You Already Speak
Cucumber: Automating the Requirements Language You Already Speak
 
Writing Software not Code with Cucumber
Writing Software not Code with CucumberWriting Software not Code with Cucumber
Writing Software not Code with Cucumber
 
Outside-In Development With Cucumber
Outside-In Development With CucumberOutside-In Development With Cucumber
Outside-In Development With Cucumber
 
Disconnecting the Database with ActiveRecord
Disconnecting the Database with ActiveRecordDisconnecting the Database with ActiveRecord
Disconnecting the Database with ActiveRecord
 
SVD and the Netflix Dataset
SVD and the Netflix DatasetSVD and the Netflix Dataset
SVD and the Netflix Dataset
 
The WHY behind TDD/BDD and the HOW with RSpec
The WHY behind TDD/BDD and the HOW with RSpecThe WHY behind TDD/BDD and the HOW with RSpec
The WHY behind TDD/BDD and the HOW with RSpec
 

Recently uploaded

Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilV3cube
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 

Recently uploaded (20)

Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 

Clojure, Plain and Simple

  • 1. Ben Mabey Plain & Simple Clojure @bmabey Utah Java User’s Group, Aug 15 2013
  • 3. "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
  • 4. Clojure is a functional Lisp that targets the JVM and enables simpler software design.
  • 5. Clojure is a functional Lisp that targets the JVM and enables simpler software design.
  • 9. (if (= code :RED) (do (launch-missiles!) (sound-the-alarm!)))
  • 10. ‘do’ implies side effects (if (= code :RED) (do (launch-missiles!) (sound-the-alarm!)))
  • 11. (if (= code :RED) (do (launch-missiles!) (sound-the-alarm!))) (when (= code :RED) (launch-missiles!) (sound-the-alarm!))
  • 12. (defmacro when "Evaluates test. If logical true, evaluates body in an implicit do." [test & body] (list 'if test (cons 'do body)))
  • 13. (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!)))
  • 14. (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!)))
  • 16. Paradigms as Libraries Design by Contract a’la Eiffel - core.contracts
  • 17. Paradigms as Libraries Design by Contract a’la Eiffel - core.contracts Logic Programming a’la Prolog - core.logic
  • 18. Paradigms as Libraries Design by Contract a’la Eiffel - core.contracts Logic Programming a’la Prolog - core.logic Lightweight threads + channels a’la Go - core.async
  • 19. Paradigms as Libraries Design 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
  • 20. Paradigms as Libraries Design 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
  • 21. Paradigms as Libraries Design 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...
  • 22. “If you give someone Fortran, he has Fortran. If you give someone Lisp, he has any language he pleases.” Guy Steele
  • 23. Clojure is a functional Lisp that targets the JVM and enables simpler software design.
  • 28. Chained Access person.getAddress().getState().getCode(); (.. person getAddress getState getCode) Count ’em, 3 vs 1 pair!
  • 29. Chained Access person.getAddress().getState().getCode(); (.. person getAddress getState getCode) (macroexpand '(.. person getAddress getState getCode)) (. (. (. person getAddress) getState) getCode)
  • 30. 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
  • 32. Multiple Updates person.setFirstName("Ben"); person.setLastName("Mabey"); person.makePresenter(); (doto person (.setFirstName "Ben") (.setLastName "Mabey") .makePresenter) ‘person’ is implicit to method calls
  • 33. Multiple Updates person.setFirstName("Ben"); person.setLastName("Mabey"); person.makePresenter(); (doto person (.setFirstName "Ben") (.setLastName "Mabey") .makePresenter) Again, ‘do’ signifies side effects
  • 34. Implementing Interfaces new Runnable() { public void run() { System.out.println("Hello World"); } }; (reify Runnable (run [] (println "Hello")))
  • 35. Clojure is a functional Lisp that targets the JVM and enables simpler software design.
  • 39. Functional Programming with Values First Class Functions Laziness
  • 40. Functional Programming with Values First Class Functions Laziness
  • 41. public static int square(int x) { return x * x; }
  • 42. public static int square(int x) { return x * x; } Value
  • 43. public static int square(int x) { return x * x; } Value }Pure Function
  • 44. 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; }
  • 45. 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
  • 46. 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
  • 47. 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(); }
  • 48. 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
  • 49. 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
  • 50. 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
  • 51. 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); }
  • 52. 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); }
  • 54. ReferencesValues primitives java.lang wrappers (Boolean, Byte, Character, Double, Float, Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI
  • 55. ReferencesValues primitives java.lang wrappers (Boolean, Byte, Character, Double, Float, Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc.
  • 56. ReferencesValues primitives java.lang wrappers (Boolean, Byte, Character, Double, Float, Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc...
  • 57. ReferencesValues primitives java.lang wrappers (Boolean, Byte, Character, Double, Float, Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc... Default for domain objects
  • 58. ReferencesValues primitives java.lang wrappers (Boolean, Byte, Character, Double, Float, Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Libraries joda-time, joda-money google-guava Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc... Default for domain objects
  • 59. ReferencesValues primitives java.lang wrappers (Boolean, Byte, Character, Double, Float, Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Libraries joda-time, joda-money google-guava Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc... Immutable Collections! Default for domain objects
  • 60. ReferencesValues primitives java.lang wrappers (Boolean, Byte, Character, Double, Float, Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Libraries joda-time, joda-money google-guava Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc... Immutable Collections! + They are values! Default for domain objects
  • 61. ReferencesValues primitives java.lang wrappers (Boolean, Byte, Character, Double, Float, Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Libraries joda-time, joda-money google-guava Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc... Immutable Collections! + They are values! - Copy on write (very good impls though) Default for domain objects
  • 62. ReferencesValues primitives java.lang wrappers (Boolean, Byte, Character, Double, Float, Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Libraries joda-time, joda-money google-guava Collections ArrayList,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
  • 63. ReferencesValues primitives java.lang wrappers (Boolean, Byte, Character, Double, Float, Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Libraries joda-time, joda-money google-guava Collections ArrayList,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
  • 64.
  • 65. “Best practice” but not idiomatic...
  • 66. “Best practice” but not idiomatic... more difficult than it should be!
  • 67. “Best practice” but not idiomatic... more difficult than it should be!
  • 70. ReferencesValues primitives Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords
  • 71. Explicit Ref Types atom ref agent ReferencesValues primitives Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords
  • 72. Explicit Ref Types atom ref agent ReferencesValues primitives Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords How Clojure addresses the non-functional aspect of programs, i.e. state.
  • 73. Explicit Ref Types atom ref agent ReferencesValues primitives Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords deftype
  • 74. Explicit Ref Types atom ref agent ReferencesValues primitives Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords Java libs for interop deftype
  • 75. Explicit Ref Types atom ref Persistent Collections ReferencesValues primitives Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords + They are values
  • 76. Explicit Ref Types atom ref Persistent Collections ReferencesValues primitives Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords + They are values + Structural Sharing + Memory efficient + Fast
  • 77. Explicit Ref Types atom ref Persistent Collections ReferencesValues primitives Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords + They are values + Structural Sharing + Memory efficient + Fast + Everything nests
  • 78. Explicit Ref Types atom ref Persistent Collections ReferencesValues primitives Java’s wrappers & libs Collections List, 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!
  • 80. Structural Sharing String brother = "brother"; String the = brother.substring(3, 6);
  • 81. Structural Sharing String brother = "brother"; String the = brother.substring(3, 6); http://www.slreynolds.net/talks/clojure/collections/index.html
  • 82.
  • 84. 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 1 assoc log32(n) log(n) log32(n) dissoc log32(n) log(n) disj log32(n) log(n) nth log32(n) n n n get log32(n) log(n) log32(n) log(n) log32(n) pop 1 1 1 1 peek 1 1 1 1 count 1 1 1 1 1 1 1 n TL;DR, they are fast
  • 86. 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(); }
  • 87. 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)
  • 89. (def data {:nested [0 1 {:double "nested"}]}) Nesting
  • 90. (def data {:nested [0 1 {:double "nested"}]}) (get-in data [:nested 2 :double]) Nesting
  • 91. (def data {:nested [0 1 {:double "nested"}]}) (get-in data [:nested 2 :double]) Nesting First map key Index into vector Nested map key
  • 92. (def data {:nested [0 1 {:double "nested"}]}) (get-in data [:nested 2 :double]) > "nested" Nesting
  • 93. (update-in data [:nested 2 :double] upper-case) (get-in data [:nested 2 :double]) > "nested" (def data {:nested [0 1 {:double "nested"}]}) Nesting
  • 94. (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
  • 95. (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
  • 96. (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
  • 97. (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")
  • 98. (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
  • 99. (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
  • 100. (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"}]}
  • 101. (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.
  • 102. Clojure is a functional Lisp that targets the JVM and enables simpler software design.
  • 104. Simpler? Simple Made Easy Rich Hickey http://www.infoq.com/presentations/Simple-Made-Easy
  • 106. Easy
  • 107. Easyease < aise < adjacens lie near i.e. familiar, convenient, near to our skill set or current understanding
  • 108. Easyease < aise < adjacens lie near i.e. familiar, convenient, near to our skill set or current understanding always relative! opposite of hard
  • 109. Simple
  • 110. Simplesim - plex one fold/braid opposite of complex
  • 111. Simplesim - plex one fold/braid opposite of complex
  • 112. Simplesim - plex one fold/braid no interleaving! one concept, one dimension one role, but maybe multiple operations opposite of complex
  • 113. Simple
  • 117.
  • 118.
  • 119.
  • 120. How would you model the game state using objects?
  • 121.
  • 122. 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; .... }
  • 123. 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; ! } }
  • 124.
  • 125. {: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 ...)}}
  • 126. {: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
  • 127.
  • 128. 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
  • 129. It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures. Alan Perlis
  • 130.
  • 131. Data
  • 133. ( )
  • 136. Create a Game Board
  • 137. Create a Game Board
  • 138. Create a Game Board • 129 colored spaces • 6 colors in repeating sequence: 1. Purple 2.Yellow 3.Blue 4.Orange 5.Green 6.Red
  • 139. Create a Game Board ! private static final int NUMBER_SPACES = 129; ! public static String[] COLOR_SEQUENCE = { ! ! "Purple", ! ! "Yellow", ! ! "Blue", ! ! "Orange", ! ! "Green", ! ! "Red" ! }; !
  • 140. 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)); ! ! } ! ! ... } ! !
  • 141. (def colors [:purple :yellow :blue :orange :green :red])
  • 142. (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
  • 143. (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.
  • 144. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)
  • 145. (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)
  • 146. (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
  • 147. (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)); ! ! }
  • 148. (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)
  • 149. (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
  • 150. (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
  • 151. (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)
  • 152. (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})
  • 153. (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})
  • 154. (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
  • 155. (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))
  • 156. (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
  • 157. (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
  • 159. Play the game public class GameBoard { ! public void play() { ! ! while (nextTurn()); ! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName()); ! }
  • 160. ! // 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 game public 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; ! }
  • 161. ! // 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 game public 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
  • 162. ! // 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 game public 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
  • 163. (defn next-move "Takes the game forward one move by drawing a card for the current player and moving them accordingly." [game]
  • 164. (defn next-move "Takes the game forward one move by drawing a card for the current player and moving them accordingly." [game] Docstring attached as metadata
  • 165. (defn next-move "Takes the game forward one move by drawing a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game
  • 166. (defn next-move "Takes the game forward one move by drawing a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game Bind the keys we need with desctructuring
  • 167. (defn next-move "Takes the game forward one move by drawing a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index)
  • 168. (defn next-move "Takes the game forward one move by drawing a card for the current 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)
  • 169. (defn next-move "Takes the game forward one move by drawing a card for the current 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]
  • 170. (defn next-move "Takes the game forward one move by drawing a card for the current 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
  • 171. (defn next-move "Takes the game forward one move by drawing a card for the current 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)]
  • 172. (defn next-move "Takes the game forward one move by drawing a card for the current 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))))
  • 173. (defn next-move "Takes the game forward one move by drawing a card for the current 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))))
  • 174. (defn next-move "Takes the game forward one move by drawing a card for the current 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))))
  • 175. Play the game (defn play [game] (->> (iterate next-move game) (filter game-over?) first))
  • 176. (defn play [game] (filter game-over?) first)) Play the game (->> (iterate next-move game) Text
  • 177. (defn play [game] (filter game-over?) first)) Play the game (->> (iterate next-move game) Text v1
  • 178. (defn play [game] (filter game-over?) first)) Play the game (->> (iterate next-move game) Text v1 next-move v2
  • 179. (defn play [game] (filter game-over?) first)) Play the game (->> (iterate next-move game) Text v1 next-move v2 v3 next-move
  • 180. (defn play [game] (filter game-over?) first)) Play the game (->> (iterate next-move game) Text v1 next-move v2 v3 next-move next-move ...
  • 181. (defn play [game] (filter game-over?) first)) Play the game (->> (iterate next-move game) Text v1 next-move v2 v3 next-move next-move ... (->> 1 (iterate inc) (take 5)) > (1 2 3 4 5)
  • 182. Play the game (defn play [game] (->> (iterate next-move game) (filter game-over?) first)) (filter game-over?) first)) Predicate function
  • 183. Play the game (defn play [game] (->> (iterate next-move game) (filter game-over?) first)) (filter game-over?) first)) Return the ended game value
  • 184. 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()); ! }
  • 185. 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()); ! }
  • 186. 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!
  • 187.
  • 191. Put it on the Web!
  • 192. {: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"}
  • 193. {"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"}
  • 195. {: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"}
  • 196. {: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
  • 197. {: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"}
  • 201. Game Reference (def game-ref (atom (create-game ...)))
  • 202. Game Reference (def game-ref (atom (create-game ...))) Reference Constructor
  • 203. Game Reference (def game-ref (atom (create-game ...))) Reference Constructor Initial Value
  • 204. Game Reference (def game-ref (atom (create-game ...))) (swap! game-ref take-next-move) Atomic Succession
  • 205. 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
  • 206. (swap! game-ref add-player "Carter") Game Reference (def game-ref (atom (create-game ...))) (swap! game-ref take-next-move) Additional args to fn
  • 207. (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)
  • 208. (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
  • 209. (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
  • 211. Clojure’s Time Model State is the current value of an identity. v1
  • 212. Clojure’s Time Model State is the current value of an identity. An identity is series of values over time. v1 v2 v3
  • 213. Clojure’s Time Model State 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
  • 214. Clojure’s Time Model State 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
  • 217. Add a new card type
  • 218. (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))))
  • 219. Picture cards Candy Heart Peppermint Stick Ginger Bread Gum Drop Peanut Brittle Lollypop Ice Cream
  • 220. (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))))
  • 221. (defprotocol Card (next-location [card board player] "Determines the next location of the player"))
  • 222. (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"))
  • 223. Protocols are not just another name for interfaces...
  • 224. they allow you to add new abstractions to existing types
  • 226. (extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s] (str "foo-A-" (.toUpperCase s)))) (ns abstraction-a) (defprotocol AbstractionA (foo [obj]))
  • 227. (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!"
  • 228. (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]))
  • 229. (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]))
  • 230. (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!"
  • 231. (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
  • 238. (->> (range 100000) (map inc) (reduce +))
  • 239. (require '[clojure.core.reducers :as r]) (->> (range 100000) (r/map inc) (r/reduce +)) (->> (range 100000) (map inc) (reduce +))
  • 240. (require '[clojure.core.reducers :as r]) (->> (range 100000) (r/map inc) (r/reduce +)) (->> (range 100000) (map inc) (reduce +)) Process sequences in parallel with ForkJoin
  • 241. (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”
  • 242. (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)))
  • 245. It is bound to happen...
  • 246.
  • 247.
  • 248. I don’t want to go back to the gum drops!
  • 249.
  • 250. I can remember what the game looked like, why can’t your program?!?
  • 251. 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.
  • 252. 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
  • 253. (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 las update 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))
  • 254. (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 las update 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))
  • 255. Clojure is a functional Lisp that targets the JVM and enables simpler software design.
  • 256. How do you want to spend your complexity budget?
  • 258. Tradeoffs Different way of thinking takes time.
  • 259. Tradeoffs Different way of thinking takes time. Idiomatic Clojure is slower than idiomatic Java in micro benchmarks.
  • 260. Tradeoffs Different 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.
  • 261. Tradeoffs Different 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.
  • 262. Tradeoffs Different 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?
  • 263. Tradeoffs Different 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?
  • 266. ( ) 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 Training clojure.com $ Clojure/Conj clojure-conj.org Training clojure.com $ Clojure/Conj clojure-conj.org Training clojure.com
  • 268. C# Async async 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; }
  • 269. 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
  • 270. 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)))))))))
  • 271. “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
  • 272. Its my ball of mud!
  • 273. “I remain unenthusiastic about actors.” Rich Hickey
  • 274. 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!"
  • 275. Color # in Deck Red 6 Orange 4 Yellow 6 Green 4 Blue 6 Purple 4 Create a Card Deck
  • 276. Create a Card Deck public 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); ! }
  • 277. 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)); ! ! ! } ! ! } ! }
  • 278. Create a Card Deck
  • 279. Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6
  • 280. Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6 :green 4 :blue 6 :purple 4})
  • 281. 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)
  • 282. 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))
  • 283. 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))
  • 284. 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)
  • 285. 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]
  • 286. 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))
  • 287. 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
  • 288. 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))
  • 289. 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)); ! ! ! } ! ! } ! }
  • 290. 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))