Your SlideShare is downloading. ×
0
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
deficien...
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...
(defmacro when
"Evaluates test. If logical true,
evaluates body in an implicit do."
[test & body]
(list 'if test (cons 'do...
(defmacro when
"Evaluates test. If logical true,
evaluates body in an implicit do."
[test & body]
(list 'if test (cons 'do...
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
Lightwei...
Paradigms as Libraries
Design by Contract a’la Eiffel - core.contracts
Logic Programming a’la Prolog - core.logic
Lightwei...
Paradigms as Libraries
Design by Contract a’la Eiffel - core.contracts
Logic Programming a’la Prolog - core.logic
Lightwei...
Paradigms as Libraries
Design by Contract a’la Eiffel - core.contracts
Logic Programming a’la Prolog - core.logic
Lightwei...
“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 ...
Chained Access
person.getAddress().getState().getCode();
(.. person getAddress getState getCode)
(macroexpand '(.. person ...
Multiple Updates
person.setFirstName("Ben");
person.setLastName("Mabey");
person.makePresenter();
(doto person
(.setFirstN...
Multiple Updates
person.setFirstName("Ben");
person.setLastName("Mabey");
person.makePresenter();
(doto person
(.setFirstN...
Multiple Updates
person.setFirstName("Ben");
person.setLastName("Mabey");
person.makePresenter();
(doto person
(.setFirstN...
Implementing
Interfaces
new Runnable()
{
public void run()
{
System.out.println("Hello World");
}
};
(reify Runnable
(run ...
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....
public static int square(int x) {
return x * x;
}
import java.util.Date;
public static Date oneYearFrom(Date date)
{
date....
public static int square(int x) {
return x * x;
}
import java.util.Date;
public static Date oneYearFrom(Date date)
{
date....
public static int square(int x) {
return x * x;
}
import java.util.Date;
public static Date oneYearFrom(Date date)
{
date....
public static int square(int x) {
return x * x;
}
import java.util.Date;
public static Date oneYearFrom(Date date)
{
date....
public static int square(int x) {
return x * x;
}
import java.util.Date;
public static Date oneYearFrom(Date date)
{
date....
public static int square(int x) {
return x * x;
}
import java.util.Date;
public static Date oneYearFrom(Date date)
{
date....
public static int square(int x) {
return x * x;
}
import java.util.Date;
public static Date oneYearFrom(Date date)
{
date....
public static int square(int x) {
return x * x;
}
import java.util.Date;
public static Date oneYearFrom(Date date)
{
date....
ReferencesValues
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
oth...
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
oth...
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
oth...
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
oth...
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
oth...
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
oth...
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
oth...
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
oth...
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
oth...
ReferencesValues
primitives
java.lang wrappers
(Boolean, Byte, Character,
Double, Float, Integer, Long,
Short, String)
oth...
“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,...
Explicit Ref Types
atom
ref
agent
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Vector, HashMap,
Tr...
Explicit Ref Types
atom
ref
agent
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Vector, HashMap,
Tr...
Explicit Ref Types
atom
ref
agent
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Vector, HashMap,
Tr...
Explicit Ref Types
atom
ref
agent
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Vector, HashMap,
Tr...
Explicit Ref Types
atom
ref
Persistent Collections
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Ve...
Explicit Ref Types
atom
ref
Persistent Collections
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Ve...
Explicit Ref Types
atom
ref
Persistent Collections
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Ve...
Explicit Ref Types
atom
ref
Persistent Collections
ReferencesValues
primitives
Java’s wrappers & libs
Collections
List, Ve...
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/cloju...
Learn More
http://www.slreynolds.net/talks/clojure/collections/index.html
http://pragprog.com/magazines/2011-07/clojure-co...
http://www.innoq.com/blog/st/2010/04/clojure_performance_guarantees.html
hash-
map
sorted-
map
hash-
set
sorted-
set
vecto...
Transients
Transients
MutableDateTime temp = new MutableDateTime(date);
temp.addYears(1);
return temp.toDateTime();
import org.joda.t...
Transients
MutableDateTime temp = new MutableDateTime(date);
temp.addYears(1);
return temp.toDateTime();
import org.joda.t...
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
Ne...
(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 {:dou...
(update-in data [:nested 2 :double] upper-case)
(get-in data [:nested 2 :double])
> "nested"
(def data {:nested [0 1 {:dou...
(update-in data [:nested 2 :double] upper-case)
(get-in data [:nested 2 :double])
> "nested"
(def data {:nested [0 1 {:dou...
(get-in data [:nested 2 :double])
> "nested"
(def data {:nested [0 1 {:double "nested"}]})
Nesting
(update-in data [:neste...
(def data {:nested [0 1 {:double "nested"}]})
(get-in data [:nested 2 :double])
> "nested"
Nesting
(update-in data [:neste...
(def data {:nested [0 1 {:double "nested"}]})
(get-in data [:nested 2 :double])
> "nested"
Nesting
(update-in data [:neste...
(def data {:nested [0 1 {:double "nested"}]})
(get-in data [:nested 2 :double])
> "nested"
Nesting
(update-in data [:neste...
(def data {:nested [0 1 {:double "nested"}]})
(get-in data [:nested 2 :double])
> "nested"
Nesting
(update-in data [:neste...
(def data {:nested [0 1 {:double "nested"}]})
(get-in data [:nested 2 :double])
> "nested"
Nesting
(update-in data [:neste...
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 relat...
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
opposi...
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 cardDec...
public class GameBoard {
!
! private List<ColoredSpace> spaces = new ArrayList<ColoredSpace>();
! private CardDeck cardDec...
{:players [{:location 32 :name "ben"}
{:location 14 :name "maren"}]
:board
[{:color :purple}
...
{:color :orange, :shortcu...
{:players [{:location 32 :name "ben"}
{:location 14 :name "maren"}]
:board
[{:color :purple}
...
{:color :orange, :shortcu...
distinct filter remove for keep keep-indexed
cons concat lazy-cat mapcat cycle interleave
interpose rest next fnext nnext ...
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 = {
! ! "Purpl...
Create a Game Board
! private static final int NUMBER_SPACES = 129;
! public static String[] COLOR_SEQUENCE = {
! ! "Purpl...
(def colors [:purple :yellow :blue :orange :green :red])
(def colors [:purple :yellow :blue :orange :green :red])
user> (find-doc "cycle")
-------------------------
clojure.core/c...
(def colors [:purple :yellow :blue :orange :green :red])
user> (find-doc "cycle")
-------------------------
clojure.core/c...
(def colors [:purple :yellow :blue :orange :green :red])
(cycle colors)
=> (:purple :yellow :blue :orange :green :red :pur...
(def colors [:purple :yellow :blue :orange :green :red])
(cycle colors)
=> (:purple :yellow :blue :orange :green :red :pur...
(def colors [:purple :yellow :blue :orange :green :red])
(cycle colors)
=> (:purple :yellow :blue :orange :green :red :pur...
(def colors [:purple :yellow :blue :orange :green :red])
(cycle colors)
=> (:purple :yellow :blue :orange :green :red :pur...
(def colors [:purple :yellow :blue :orange :green :red])
(cycle colors)
=> (:purple :yellow :blue :orange :green :red :pur...
(def colors [:purple :yellow :blue :orange :green :red])
(cycle colors)
=> (:purple :yellow :blue :orange :green :red :pur...
(def colors [:purple :yellow :blue :orange :green :red])
(cycle colors)
=> (:purple :yellow :blue :orange :green :red :pur...
(def colors [:purple :yellow :blue :orange :green :red])
(cycle colors)
=> (:purple :yellow :blue :orange :green :red :pur...
(def colors [:purple :yellow :blue :orange :green :red])
(cycle colors)
=> (:purple :yellow :blue :orange :green :red :pur...
(def colors [:purple :yellow :blue :orange :green :red])
(cycle colors)
=> (:purple :yellow :blue :orange :green :red :pur...
(def colors [:purple :yellow :blue :orange :green :red])
(cycle colors)
=> (:purple :yellow :blue :orange :green :red :pur...
(map make-space)
(defn make-card [color]
{:color color})
(->> colors (map make-space) cycle (take 3))
=> ({:color :purple}...
(map make-space)
(defn make-card [color]
{:color color})
(->> colors (map make-space) cycle (take 3))
=> ({:color :purple}...
(def game-board
(->> [:purple :yellow :blue :orange :green :red]
(map #(array-map :color %))
cycle
(take 129)))
! public G...
Play the game
Play the game
public class GameBoard {
! public void play() {
! ! while (nextTurn());
! ! System.out.println(“The winning ...
! // Player pulls a card from the top of deck and moves player
! private boolean nextTurn() {
! ! // Player selects card f...
! // Player pulls a card from the top of deck and moves player
! private boolean nextTurn() {
! ! // Player selects card f...
! // Player pulls a card from the top of deck and moves player
! private boolean nextTurn() {
! ! // Player selects card f...
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[g...
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[g...
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[g...
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[g...
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[g...
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[g...
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[g...
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[g...
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[g...
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[g...
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[g...
(defn next-move
"Takes the game forward one move by drawing a card for the
current player and moving them accordingly."
[g...
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-mov...
(defn play [game]
(filter game-over?)
first))
Play the game
(->> (iterate next-move game)
Text
v1
next-move
v2 v3
next-mov...
Play the game
(defn play [game]
(->> (iterate next-move game)
(filter game-over?)
first))
(filter game-over?)
first))
Pred...
Play the game
(defn play [game]
(->> (iterate next-move game)
(filter game-over?)
first))
(filter game-over?)
first))
Retu...
Play the game
(defn play [game]
(->> (iterate next-move game)
(filter game-over?)
first))
public void play() {
! ! while (...
Play the game
(defn play [game]
(->> (iterate next-move game)
(filter game-over?)
first))
(->> (create-game ["Ben" "Maren"...
Play the game
(defn play [game]
(->> (iterate next-move game)
(filter game-over?)
first))
(->> (create-game ["Ben" "Maren"...
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, :short...
{"players" : [ {"location" : 32,"name" : "ben"},
{"location" : 14,"name" : "maren"} ],
"board" : [{"color" : "purple"},
.....
JSONJavaScript
EDNClojure
{:players [{:location 32, :name "ben"}
{:location 14, :name "maren"}]
:board
[{:color :purple}
...
{:color :orange, :short...
{:players [{:location 32, :name "ben"}
{:location 14, :name "maren"}]
:board
[{:color :purple}
...
{:color :orange, :short...
{:id 54321
:players [{:location 32 :name "ben"}
{:location 14 :name "maren"}]
:board
[{:color :purple}
...
{:color :orange...
( )
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 r...
(swap! game-ref add-player "Carter")
Game Reference
(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-gam...
(swap! game-ref add-player "Carter")
Game Reference
(deref game-ref) => current-game-value
(def game-ref (atom (create-gam...
(swap! game-ref add-player "Carter")
Game Reference
(deref game-ref) => current-game-value
(def game-ref (atom (create-gam...
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 ...
Clojure’s Time Model
State is the current value of an identity.
An identity is series of values over time.
A reference to ...
( )
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 (fin...
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 (fin...
(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-locati...
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 abstracti...
(extend-protocol AbstractionA
nil
(foo [s] (str "foo-A!"))
String
(foo [s] (str "foo-A-" (.toUpperCase s))))
(ns abstracti...
(extend-protocol AbstractionA
nil
(foo [s] (str "foo-A!"))
String
(foo [s] (str "foo-A-" (.toUpperCase s))))
(ns abstracti...
(extend-protocol AbstractionA
nil
(foo [s] (str "foo-A!"))
String
(foo [s] (str "foo-A-" (.toUpperCase s))))
(ns abstracti...
(extend-protocol AbstractionA
nil
(foo [s] (str "foo-A!"))
String
(foo [s] (str "foo-A-" (.toUpperCase s))))
(ns abstracti...
(extend-protocol AbstractionA
nil
(foo [s] (str "foo-A!"))
String
(foo [s] (str "foo-A-" (.toUpperCase s))))
(ns abstracti...
( )
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)
(redu...
(require '[clojure.core.reducers :as r])
(->> (range 100000)
(r/map inc)
(r/reduce +))
(->> (range 100000)
(map inc)
(redu...
(require '[clojure.core.reducers :as r])
(->> (range 100000)
(r/map inc)
(r/reduce +))
(->> (range 100000)
(map inc)
(redu...
(extend-protocol CollFold
nil
(coll-fold
[coll n combinef reducef]
(combinef))
Object
(coll-fold
[coll n combinef reducef]...
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 change...
Clojure’s Time Model
A reference to an identity allows updates and
reads to it.
Values never change, the past never change...
(defn shadow-ref
"Returns a ref that contains the time - 1 value of the given ref.
In other words, shawdow-ref contains th...
(defn undo-and-skip-card [game-ref old-game-ref]
(let [alternate-reality (-> @old-game-ref
skip-card
take-next-move)]
(res...
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 a...
Tradeoffs
Different way of thinking takes time.
Idiomatic Clojure is slower than idiomatic Java
in micro benchmarks.
Not a...
Tradeoffs
Different way of thinking takes time.
Idiomatic Clojure is slower than idiomatic Java
in micro benchmarks.
Not a...
Tradeoffs
Different way of thinking takes time.
Idiomatic Clojure is slower than idiomatic Java
in micro benchmarks.
Not a...
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....
Extra Slides
C# Async
async void Go() {
_button.IsEnabled = false;
string[] urls = "clojure.org www.albahari.com/nutshell/
golang.org"....
CSP in Go// Run the Web, Image, and Video searches concurrently,
// and wait for all results.
// No locks. No condition va...
Go in Clojure
(use 'clojure.core.async)
(defn google [query]
(let [c (chan)]
(go (>! c (<! (web query))))
(go (>! c (<! (i...
“APL is like a beautiful diamond - flawless,
beautifully symmetrical. But you can't add
anything to it. If you try to glue ...
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
#...
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_G...
Create a Card Deck
! private void addCardsToDeck() {
! ! cards = new Stack<Card>();
! ! // Add cards to deck based on colo...
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 ...
Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6
:green 4 :blue 6 :purple 4})
(repeat 3 :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 ...
Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6
:green 4 :blue 6 :purple 4})
(repeat 3 :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 ...
Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6
:green 4 :blue 6 :purple 4})
(repeat 3 :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 ...
Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6
:green 4 :blue 6 :purple 4})
(repeat 3 :red) => (:red :red ...
Create a Card Deck
! private void addCardsToDeck() {
! ! cards = new Stack<Card>();
! ! // Add cards to deck based on colo...
Create a Card Deck
! private void addCardsToDeck() {
! ! cards = new Stack<Card>();
! ! // Add cards to deck based on colo...
Clojure, Plain and Simple
Clojure, Plain and Simple
Clojure, Plain and Simple
Clojure, Plain and Simple
Clojure, Plain and Simple
Clojure, Plain and Simple
Clojure, Plain and Simple
Clojure, Plain and Simple
Clojure, Plain and Simple
Clojure, Plain and Simple
Clojure, Plain and Simple
Clojure, Plain and Simple
Clojure, Plain and Simple
Upcoming SlideShare
Loading in...5
×

Clojure, Plain and Simple

2,415

Published on

This introduction to Clojure was given to the Utah Java Users Group Aug. 15. It's main focus was on Clojure's time model and how the design of Clojure separates (decomplects) many concepts which are all implemented onto of Objects in Java, and other OO languages. This is the abstract for the original talk:

Tony Hoare famously said "There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult." Clojure is a functional Lisp that targets, among other platforms, the JVM and strives to enable the former approach to building software.

In its pursuit of simplicity Clojure encourages the use of pure functions, sequence abstractions which allow for lazy and parallel processing of data, persistent (immutable) data structures, and a novel way of dealing with state as a succession of values. While these concepts may sound intimidating for those unfamiliar with functional programming, they are actually less complicated than many programming constructs that programmers use everyday.

This talk will cover these concepts and the motivation behind them. You will learn the basics of Clojure programming and will be given a taste of what developing an application in Clojure is like.

Published in: Technology
0 Comments
11 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,415
On Slideshare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
52
Comments
0
Likes
11
Embeds 0
No embeds

No notes for slide

Transcript of "Clojure, Plain and Simple"

  1. 1. Ben Mabey Plain & Simple Clojure @bmabey Utah Java User’s Group, Aug 15 2013
  2. 2. redbrainlabs.com
  3. 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. 4. Clojure is a functional Lisp that targets the JVM and enables simpler software design.
  5. 5. Clojure is a functional Lisp that targets the JVM and enables simpler software design.
  6. 6. Read Eval Print Loop
  7. 7. Read Eval Print Loop ((Live))
  8. 8. Macros
  9. 9. (if (= code :RED) (do (launch-missiles!) (sound-the-alarm!)))
  10. 10. ‘do’ implies side effects (if (= code :RED) (do (launch-missiles!) (sound-the-alarm!)))
  11. 11. (if (= code :RED) (do (launch-missiles!) (sound-the-alarm!))) (when (= code :RED) (launch-missiles!) (sound-the-alarm!))
  12. 12. (defmacro when "Evaluates test. If logical true, evaluates body in an implicit do." [test & body] (list 'if test (cons 'do body)))
  13. 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. 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!)))
  15. 15. Paradigms as Libraries
  16. 16. Paradigms as Libraries Design by Contract a’la Eiffel - core.contracts
  17. 17. Paradigms as Libraries Design by Contract a’la Eiffel - core.contracts Logic Programming a’la Prolog - core.logic
  18. 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. 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. 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. 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. 22. “If you give someone Fortran, he has Fortran. If you give someone Lisp, he has any language he pleases.” Guy Steele
  23. 23. Clojure is a functional Lisp that targets the JVM and enables simpler software design.
  24. 24. Constructor new Widget("gizmo"); (Widget. "gizmo")
  25. 25. Static Member Math.PI Math/PI
  26. 26. Instance Method string.trim(); (.trim string)
  27. 27. Chained Access person.getAddress().getState().getCode(); (.. person getAddress getState getCode)
  28. 28. Chained Access person.getAddress().getState().getCode(); (.. person getAddress getState getCode) Count ’em, 3 vs 1 pair!
  29. 29. Chained Access person.getAddress().getState().getCode(); (.. person getAddress getState getCode) (macroexpand '(.. person getAddress getState getCode)) (. (. (. person getAddress) getState) getCode)
  30. 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
  31. 31. Multiple Updates person.setFirstName("Ben"); person.setLastName("Mabey"); person.makePresenter(); (doto person (.setFirstName "Ben") (.setLastName "Mabey") .makePresenter)
  32. 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. 33. Multiple Updates person.setFirstName("Ben"); person.setLastName("Mabey"); person.makePresenter(); (doto person (.setFirstName "Ben") (.setLastName "Mabey") .makePresenter) Again, ‘do’ signifies side effects
  34. 34. Implementing Interfaces new Runnable() { public void run() { System.out.println("Hello World"); } }; (reify Runnable (run [] (println "Hello")))
  35. 35. Clojure is a functional Lisp that targets the JVM and enables simpler software design.
  36. 36. Functional
  37. 37. Functional Programming with Values
  38. 38. Functional Programming with Values First Class Functions
  39. 39. Functional Programming with Values First Class Functions Laziness
  40. 40. Functional Programming with Values First Class Functions Laziness
  41. 41. public static int square(int x) { return x * x; }
  42. 42. public static int square(int x) { return x * x; } Value
  43. 43. public static int square(int x) { return x * x; } Value }Pure Function
  44. 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. 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. 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. 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. 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. 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. 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. 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. 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); }
  53. 53. ReferencesValues
  54. 54. ReferencesValues primitives java.lang wrappers (Boolean, Byte, Character, Double, Float, Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI
  55. 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. 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. 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. 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. 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. 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. 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. 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. 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. 64. “Best practice” but not idiomatic...
  65. 65. “Best practice” but not idiomatic... more difficult than it should be!
  66. 66. “Best practice” but not idiomatic... more difficult than it should be!
  67. 67. ReferencesValues
  68. 68. ReferencesValues primitives Java’s wrappers & libs
  69. 69. ReferencesValues primitives Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords
  70. 70. Explicit Ref Types atom ref agent ReferencesValues primitives Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords
  71. 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 How Clojure addresses the non-functional aspect of programs, i.e. state.
  72. 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 deftype
  73. 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 Java libs for interop deftype
  74. 74. 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
  75. 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 + Structural Sharing + Memory efficient + Fast
  76. 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 + Everything nests
  77. 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 The rest of Clojure and its ecosystem is built on these!
  78. 78. Structural Sharing
  79. 79. Structural Sharing String brother = "brother"; String the = brother.substring(3, 6);
  80. 80. Structural Sharing String brother = "brother"; String the = brother.substring(3, 6); http://www.slreynolds.net/talks/clojure/collections/index.html
  81. 81. Learn More http://www.slreynolds.net/talks/clojure/collections/index.html http://pragprog.com/magazines/2011-07/clojure-collections
  82. 82. 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
  83. 83. Transients
  84. 84. 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(); }
  85. 85. 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)
  86. 86. Nesting
  87. 87. (def data {:nested [0 1 {:double "nested"}]}) Nesting
  88. 88. (def data {:nested [0 1 {:double "nested"}]}) (get-in data [:nested 2 :double]) Nesting
  89. 89. (def data {:nested [0 1 {:double "nested"}]}) (get-in data [:nested 2 :double]) Nesting First map key Index into vector Nested map key
  90. 90. (def data {:nested [0 1 {:double "nested"}]}) (get-in data [:nested 2 :double]) > "nested" Nesting
  91. 91. (update-in data [:nested 2 :double] upper-case) (get-in data [:nested 2 :double]) > "nested" (def data {:nested [0 1 {:double "nested"}]}) Nesting
  92. 92. (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
  93. 93. (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
  94. 94. (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
  95. 95. (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")
  96. 96. (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
  97. 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") New value New key
  98. 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") > {:nested [0 1 {:double "nested" :another "val"}]}
  99. 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") > {:nested [0 1 {:double "nested" :another "val"}]} Imagine doing this in Java.
  100. 100. Clojure is a functional Lisp that targets the JVM and enables simpler software design.
  101. 101. Simpler?
  102. 102. Simpler? Simple Made Easy Rich Hickey http://www.infoq.com/presentations/Simple-Made-Easy
  103. 103. Simple != Easy
  104. 104. Easy
  105. 105. Easyease < aise < adjacens lie near i.e. familiar, convenient, near to our skill set or current understanding
  106. 106. Easyease < aise < adjacens lie near i.e. familiar, convenient, near to our skill set or current understanding always relative! opposite of hard
  107. 107. Simple
  108. 108. Simplesim - plex one fold/braid opposite of complex
  109. 109. Simplesim - plex one fold/braid opposite of complex
  110. 110. Simplesim - plex one fold/braid no interleaving! one concept, one dimension one role, but maybe multiple operations opposite of complex
  111. 111. Simple
  112. 112. Simple Complex
  113. 113. Complect To interleave, entwine, braid
  114. 114. http://tinyurl.com/candy-land-pdf
  115. 115. How would you model the game state using objects?
  116. 116. 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; .... }
  117. 117. 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; ! } }
  118. 118. {: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 ...)}}
  119. 119. {: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
  120. 120. 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
  121. 121. It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures. Alan Perlis
  122. 122. Data
  123. 123. Methods Data
  124. 124. ( )
  125. 125. ( ) Values
  126. 126. ( ) Values Functions
  127. 127. Create a Game Board
  128. 128. Create a Game Board
  129. 129. Create a Game Board • 129 colored spaces • 6 colors in repeating sequence: 1. Purple 2.Yellow 3.Blue 4.Orange 5.Green 6.Red
  130. 130. Create a Game Board ! private static final int NUMBER_SPACES = 129; ! public static String[] COLOR_SEQUENCE = { ! ! "Purple", ! ! "Yellow", ! ! "Blue", ! ! "Orange", ! ! "Green", ! ! "Red" ! }; !
  131. 131. 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)); ! ! } ! ! ... } ! !
  132. 132. (def colors [:purple :yellow :blue :orange :green :red])
  133. 133. (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
  134. 134. (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.
  135. 135. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)
  136. 136. (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)
  137. 137. (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
  138. 138. (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)); ! ! }
  139. 139. (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)
  140. 140. (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
  141. 141. (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
  142. 142. (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)
  143. 143. (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})
  144. 144. (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})
  145. 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) (->> (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
  146. 146. (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))
  147. 147. (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
  148. 148. (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
  149. 149. Play the game
  150. 150. Play the game public class GameBoard { ! public void play() { ! ! while (nextTurn()); ! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName()); ! }
  151. 151. ! // 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; ! }
  152. 152. ! // 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
  153. 153. ! // 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
  154. 154. (defn next-move "Takes the game forward one move by drawing a card for the current player and moving them accordingly." [game]
  155. 155. (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
  156. 156. (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
  157. 157. (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
  158. 158. (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)
  159. 159. (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)
  160. 160. (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]
  161. 161. (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
  162. 162. (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)]
  163. 163. (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))))
  164. 164. (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))))
  165. 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 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))))
  166. 166. Play the game (defn play [game] (->> (iterate next-move game) (filter game-over?) first))
  167. 167. (defn play [game] (filter game-over?) first)) Play the game (->> (iterate next-move game) Text
  168. 168. (defn play [game] (filter game-over?) first)) Play the game (->> (iterate next-move game) Text v1
  169. 169. (defn play [game] (filter game-over?) first)) Play the game (->> (iterate next-move game) Text v1 next-move v2
  170. 170. (defn play [game] (filter game-over?) first)) Play the game (->> (iterate next-move game) Text v1 next-move v2 v3 next-move
  171. 171. (defn play [game] (filter game-over?) first)) Play the game (->> (iterate next-move game) Text v1 next-move v2 v3 next-move next-move ...
  172. 172. (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)
  173. 173. Play the game (defn play [game] (->> (iterate next-move game) (filter game-over?) first)) (filter game-over?) first)) Predicate function
  174. 174. Play the game (defn play [game] (->> (iterate next-move game) (filter game-over?) first)) (filter game-over?) first)) Return the ended game value
  175. 175. 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()); ! }
  176. 176. 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()); ! }
  177. 177. 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!
  178. 178. Data Methods
  179. 179. Data Methods References
  180. 180. ( ) Values Functions
  181. 181. Put it on the Web!
  182. 182. {: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"}
  183. 183. {"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"}
  184. 184. JSONJavaScript EDNClojure
  185. 185. {: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"}
  186. 186. {: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
  187. 187. {: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"}
  188. 188. ( ) Values Functions
  189. 189. ( ) Values Functions References
  190. 190. Game Reference
  191. 191. Game Reference (def game-ref (atom (create-game ...)))
  192. 192. Game Reference (def game-ref (atom (create-game ...))) Reference Constructor
  193. 193. Game Reference (def game-ref (atom (create-game ...))) Reference Constructor Initial Value
  194. 194. Game Reference (def game-ref (atom (create-game ...))) (swap! game-ref take-next-move) Atomic Succession
  195. 195. 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
  196. 196. (swap! game-ref add-player "Carter") Game Reference (def game-ref (atom (create-game ...))) (swap! game-ref take-next-move) Additional args to fn
  197. 197. (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)
  198. 198. (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
  199. 199. (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
  200. 200. Clojure’s Time Model
  201. 201. Clojure’s Time Model State is the current value of an identity. v1
  202. 202. Clojure’s Time Model State is the current value of an identity. An identity is series of values over time. v1 v2 v3
  203. 203. 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
  204. 204. 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
  205. 205. ( ) Values Functions References Identity
  206. 206. Data Methods References Identity?
  207. 207. Add a new card type
  208. 208. (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))))
  209. 209. Picture cards Candy Heart Peppermint Stick Ginger Bread Gum Drop Peanut Brittle Lollypop Ice Cream
  210. 210. (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))))
  211. 211. (defprotocol Card (next-location [card board player] "Determines the next location of the player"))
  212. 212. (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"))
  213. 213. Protocols are not just another name for interfaces...
  214. 214. they allow you to add new abstractions to existing types
  215. 215. (ns abstraction-a) (defprotocol AbstractionA (foo [obj]))
  216. 216. (extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s] (str "foo-A-" (.toUpperCase s)))) (ns abstraction-a) (defprotocol AbstractionA (foo [obj]))
  217. 217. (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!"
  218. 218. (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]))
  219. 219. (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]))
  220. 220. (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!"
  221. 221. (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
  222. 222. ( ) Values Functions References Identity
  223. 223. ( ) Values Functions References Namespaces Identity
  224. 224. ( ) Values Functions References Namespaces IdentityPolymorphism
  225. 225. Data Methods References Identity?
  226. 226. Data Methods References Namespace Identity?
  227. 227. Data Methods References Polymorphism Namespace Identity?
  228. 228. (->> (range 100000) (map inc) (reduce +))
  229. 229. (require '[clojure.core.reducers :as r]) (->> (range 100000) (r/map inc) (r/reduce +)) (->> (range 100000) (map inc) (reduce +))
  230. 230. (require '[clojure.core.reducers :as r]) (->> (range 100000) (r/map inc) (r/reduce +)) (->> (range 100000) (map inc) (reduce +)) Process sequences in parallel with ForkJoin
  231. 231. (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”
  232. 232. (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)))
  233. 233. Parallel Collections
  234. 234. Birthday party fun!
  235. 235. It is bound to happen...
  236. 236. I don’t want to go back to the gum drops!
  237. 237. I can remember what the game looked like, why can’t your program?!?
  238. 238. 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.
  239. 239. 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
  240. 240. (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))
  241. 241. (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))
  242. 242. Clojure is a functional Lisp that targets the JVM and enables simpler software design.
  243. 243. How do you want to spend your complexity budget?
  244. 244. Tradeoffs
  245. 245. Tradeoffs Different way of thinking takes time.
  246. 246. Tradeoffs Different way of thinking takes time. Idiomatic Clojure is slower than idiomatic Java in micro benchmarks.
  247. 247. 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.
  248. 248. 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.
  249. 249. 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?
  250. 250. 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?
  251. 251. Simplicity Ease Real Tradeoffs
  252. 252. Thank you! BenMabey.com github.com/bmabey @bmabey
  253. 253. ( ) 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
  254. 254. Extra Slides
  255. 255. 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; }
  256. 256. 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
  257. 257. 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)))))))))
  258. 258. “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
  259. 259. Its my ball of mud!
  260. 260. “I remain unenthusiastic about actors.” Rich Hickey
  261. 261. 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!"
  262. 262. Color # in Deck Red 6 Orange 4 Yellow 6 Green 4 Blue 6 Purple 4 Create a Card Deck
  263. 263. 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); ! }
  264. 264. 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)); ! ! ! } ! ! } ! }
  265. 265. Create a Card Deck
  266. 266. Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6
  267. 267. Create a Card Deck(def card-counts {:red 6 :orange 4 :yellow 6 :green 4 :blue 6 :purple 4})
  268. 268. 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)
  269. 269. 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))
  270. 270. 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))
  271. 271. 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)
  272. 272. 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]
  273. 273. 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))
  274. 274. 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
  275. 275. 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))
  276. 276. 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)); ! ! ! } ! ! } ! }
  277. 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)); ! ! ! } ! ! } ! } (defn create-deck [face-freqs] (mapcat (fn [[face freq]] (repeat freq face)) face-freqs))
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×