Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clojure, Plain and Simple

Ben Mabey
August 16, 2013

Clojure, Plain and Simple

Ben Mabey
Clojure, Plain and Simple
by Ben Mabey on Aug 16, 2013 Edit

1 views

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 on top 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.

Ben Mabey

August 16, 2013
Tweet

More Decks by Ben Mabey

Other Decks in Technology

Transcript

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

    View Slide

  2. redbrainlabs.com

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  6. Read Eval Print
    Loop

    View Slide

  7. Read Eval Print
    Loop
    ((Live))

    View Slide

  8. Macros

    View Slide

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

    View Slide

  10. ‘do’ implies side effects
    (if (= code :RED)
    (do
    (launch-missiles!)
    (sound-the-alarm!)))

    View Slide

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

    View Slide

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

    View Slide

  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!)))

    View Slide

  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!)))

    View Slide

  15. Paradigms as Libraries

    View Slide

  16. Paradigms as Libraries
    Design by Contract a’la Eiffel - core.contracts

    View Slide

  17. Paradigms as Libraries
    Design by Contract a’la Eiffel - core.contracts
    Logic Programming a’la Prolog - core.logic

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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...

    View Slide

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

    View Slide

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

    View Slide

  24. Constructor
    new Widget("gizmo");
    (Widget. "gizmo")

    View Slide

  25. Static Member
    Math.PI
    Math/PI

    View Slide

  26. Instance Method
    string.trim();
    (.trim string)

    View Slide

  27. Chained Access
    person.getAddress().getState().getCode();
    (.. person getAddress getState getCode)

    View Slide

  28. Chained Access
    person.getAddress().getState().getCode();
    (.. person getAddress getState getCode)
    Count ’em, 3 vs 1 pair!

    View Slide

  29. Chained Access
    person.getAddress().getState().getCode();
    (.. person getAddress getState getCode)
    (macroexpand '(.. person getAddress getState getCode))
    (. (. (. person getAddress) getState) getCode)

    View Slide

  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

    View Slide

  31. Multiple Updates
    person.setFirstName("Ben");
    person.setLastName("Mabey");
    person.makePresenter();
    (doto person
    (.setFirstName "Ben")
    (.setLastName "Mabey")
    .makePresenter)

    View Slide

  32. Multiple Updates
    person.setFirstName("Ben");
    person.setLastName("Mabey");
    person.makePresenter();
    (doto person
    (.setFirstName "Ben")
    (.setLastName "Mabey")
    .makePresenter)
    ‘person’ is implicit to method calls

    View Slide

  33. Multiple Updates
    person.setFirstName("Ben");
    person.setLastName("Mabey");
    person.makePresenter();
    (doto person
    (.setFirstName "Ben")
    (.setLastName "Mabey")
    .makePresenter)
    Again, ‘do’ signifies side effects

    View Slide

  34. Implementing
    Interfaces
    new Runnable()
    {
    public void run()
    {
    System.out.println("Hello World");
    }
    };
    (reify Runnable
    (run [] (println "Hello")))

    View Slide

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

    View Slide

  36. Functional

    View Slide

  37. Functional
    Programming with Values

    View Slide

  38. Functional
    Programming with Values
    First Class Functions

    View Slide

  39. Functional
    Programming with Values
    First Class Functions
    Laziness

    View Slide

  40. Functional
    Programming with Values
    First Class Functions
    Laziness

    View Slide

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

    View Slide

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

    View Slide

  43. public static int square(int x) {
    return x * x;
    }
    Value
    }Pure Function

    View Slide

  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;
    }

    View Slide

  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

    View Slide

  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

    View Slide

  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();
    }

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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);
    }

    View Slide

  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);
    }

    View Slide

  53. References
    Values

    View Slide

  54. References
    Values
    primitives
    java.lang wrappers
    (Boolean, Byte, Character,
    Double, Float, Integer, Long,
    Short, String)
    other wrappers...
    e.g. UUID, URL, URI

    View Slide

  55. References
    Values
    primitives
    java.lang wrappers
    (Boolean, Byte, Character,
    Double, Float, Integer, Long,
    Short, String)
    other wrappers...
    e.g. UUID, URL, URI
    Date
    BigInteger,BigDecimal, etc.

    View Slide

  56. References
    Values
    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...

    View Slide

  57. References
    Values
    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

    View Slide

  58. References
    Values
    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

    View Slide

  59. References
    Values
    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

    View Slide

  60. References
    Values
    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

    View Slide

  61. References
    Values
    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

    View Slide

  62. References
    Values
    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

    View Slide

  63. References
    Values
    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

    View Slide

  64. View Slide

  65. “Best practice” but not
    idiomatic...

    View Slide

  66. “Best practice” but not
    idiomatic...
    more difficult than it
    should be!

    View Slide

  67. “Best practice” but not
    idiomatic...
    more difficult than it
    should be!

    View Slide

  68. References
    Values

    View Slide

  69. References
    Values
    primitives
    Java’s wrappers & libs

    View Slide

  70. References
    Values
    primitives
    Java’s wrappers & libs
    Collections
    List, Vector, HashMap,
    TreeMap, ArrayMap,
    StructMap, Queue, HashSet,
    TreeSet, LazySeq, defrecords

    View Slide

  71. Explicit Ref Types
    atom
    ref
    agent
    References
    Values
    primitives
    Java’s wrappers & libs
    Collections
    List, Vector, HashMap,
    TreeMap, ArrayMap,
    StructMap, Queue, HashSet,
    TreeSet, LazySeq, defrecords

    View Slide

  72. Explicit Ref Types
    atom
    ref
    agent
    References
    Values
    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.

    View Slide

  73. Explicit Ref Types
    atom
    ref
    agent
    References
    Values
    primitives
    Java’s wrappers & libs
    Collections
    List, Vector, HashMap,
    TreeMap, ArrayMap,
    StructMap, Queue, HashSet,
    TreeSet, LazySeq, defrecords
    deftype

    View Slide

  74. Explicit Ref Types
    atom
    ref
    agent
    References
    Values
    primitives
    Java’s wrappers & libs
    Collections
    List, Vector, HashMap,
    TreeMap, ArrayMap,
    StructMap, Queue, HashSet,
    TreeSet, LazySeq, defrecords Java libs for interop
    deftype

    View Slide

  75. Explicit Ref Types
    atom
    ref
    Persistent Collections
    References
    Values
    primitives
    Java’s wrappers & libs
    Collections
    List, Vector, HashMap,
    TreeMap, ArrayMap,
    StructMap, Queue, HashSet,
    TreeSet, LazySeq, defrecords
    + They are values

    View Slide

  76. Explicit Ref Types
    atom
    ref
    Persistent Collections
    References
    Values
    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

    View Slide

  77. Explicit Ref Types
    atom
    ref
    Persistent Collections
    References
    Values
    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

    View Slide

  78. Explicit Ref Types
    atom
    ref
    Persistent Collections
    References
    Values
    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!

    View Slide

  79. Structural
    Sharing

    View Slide

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

    View Slide

  81. Structural
    Sharing
    String brother = "brother";
    String the = brother.substring(3, 6);
    http://www.slreynolds.net/talks/clojure/collections/index.html

    View Slide

  82. View Slide

  83. Learn More
    http://www.slreynolds.net/talks/clojure/collections/index.html
    http://pragprog.com/magazines/2011-07/clojure-collections

    View Slide

  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

    View Slide

  85. Transients

    View Slide

  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();
    }

    View Slide

  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)

    View Slide

  88. Nesting

    View Slide

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

    View Slide

  90. (def data {:nested [0 1 {:double "nested"}]})
    (get-in data [:nested 2 :double])
    Nesting

    View Slide

  91. (def data {:nested [0 1 {:double "nested"}]})
    (get-in data [:nested 2 :double])
    Nesting
    First map key Index into vector
    Nested map key

    View Slide

  92. (def data {:nested [0 1 {:double "nested"}]})
    (get-in data [:nested 2 :double])
    > "nested"
    Nesting

    View Slide

  93. (update-in data [:nested 2 :double] upper-case)
    (get-in data [:nested 2 :double])
    > "nested"
    (def data {:nested [0 1 {:double "nested"}]})
    Nesting

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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")

    View Slide

  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

    View Slide

  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

    View Slide

  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"}]}

    View Slide

  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.

    View Slide

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

    View Slide

  103. Simpler?

    View Slide

  104. Simpler?
    Simple Made Easy
    Rich Hickey
    http://www.infoq.com/presentations/Simple-Made-Easy

    View Slide

  105. Simple != Easy

    View Slide

  106. Easy

    View Slide

  107. Easy
    ease < aise < adjacens
    lie near
    i.e. familiar,
    convenient,
    near to our skill set or
    current understanding

    View Slide

  108. Easy
    ease < aise < adjacens
    lie near
    i.e. familiar,
    convenient,
    near to our skill set or
    current understanding
    always relative! opposite of hard

    View Slide

  109. Simple

    View Slide

  110. Simple
    sim - plex
    one fold/braid
    opposite of complex

    View Slide

  111. Simple
    sim - plex
    one fold/braid
    opposite of complex

    View Slide

  112. Simple
    sim - plex
    one fold/braid
    no interleaving!
    one concept,
    one dimension
    one role, but maybe multiple operations
    opposite of complex

    View Slide

  113. Simple

    View Slide

  114. Simple
    Complex

    View Slide

  115. Complect
    To interleave, entwine, braid

    View Slide

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

    View Slide

  117. View Slide

  118. View Slide

  119. View Slide

  120. How would you
    model the game
    state using
    objects?

    View Slide

  121. View Slide

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

    View Slide

  123. public class GameBoard {
    !
    ! private List spaces = new ArrayList();
    ! private CardDeck cardDeck = new CardDeck();
    ! private List players = new ArrayList();
    ! // 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;
    ! }
    }

    View Slide

  124. View Slide

  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 ...)}}

    View Slide

  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

    View Slide

  127. View Slide

  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

    View Slide

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

    View Slide

  130. View Slide

  131. Data

    View Slide

  132. Methods
    Data

    View Slide

  133. ( )

    View Slide

  134. ( )
    Values

    View Slide

  135. ( )
    Values
    Functions

    View Slide

  136. Create a Game Board

    View Slide

  137. Create a Game Board

    View Slide

  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

    View Slide

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

    View Slide

  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));
    ! ! }
    ! ! ...
    }
    ! !

    View Slide

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

    View Slide

  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

    View Slide

  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.

    View Slide

  144. (def colors [:purple :yellow :blue :orange :green :red])
    (cycle colors)
    => (:purple :yellow :blue :orange :green :red :purple
    :yellow :blue :orange ...)

    View Slide

  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)

    View Slide

  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

    View Slide

  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));
    ! ! }

    View Slide

  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)

    View Slide

  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

    View Slide

  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

    View Slide

  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)

    View Slide

  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})

    View Slide

  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})

    View Slide

  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

    View Slide

  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))

    View Slide

  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

    View Slide

  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

    View Slide

  158. Play the game

    View Slide

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

    View Slide

  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;
    ! }

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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)

    View Slide

  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)

    View Slide

  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]

    View Slide

  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

    View Slide

  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)]

    View Slide

  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))))

    View Slide

  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))))

    View Slide

  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))))

    View Slide

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

    View Slide

  176. (defn play [game]
    (filter game-over?)
    first))
    Play the game
    (->> (iterate next-move game)
    Text

    View Slide

  177. (defn play [game]
    (filter game-over?)
    first))
    Play the game
    (->> (iterate next-move game)
    Text
    v1

    View Slide

  178. (defn play [game]
    (filter game-over?)
    first))
    Play the game
    (->> (iterate next-move game)
    Text
    v1
    next-move
    v2

    View Slide

  179. (defn play [game]
    (filter game-over?)
    first))
    Play the game
    (->> (iterate next-move game)
    Text
    v1
    next-move
    v2 v3
    next-move

    View Slide

  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
    ...

    View Slide

  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)

    View Slide

  182. Play the game
    (defn play [game]
    (->> (iterate next-move game)
    (filter game-over?)
    first))
    (filter game-over?)
    first))
    Predicate function

    View Slide

  183. Play the game
    (defn play [game]
    (->> (iterate next-move game)
    (filter game-over?)
    first))
    (filter game-over?)
    first))
    Return the ended game value

    View Slide

  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());
    ! }

    View Slide

  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());
    ! }

    View Slide

  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!

    View Slide

  187. View Slide

  188. Data
    Methods

    View Slide

  189. Data
    Methods
    References

    View Slide

  190. ( )
    Values
    Functions

    View Slide

  191. Put it on the Web!

    View Slide

  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"}

    View Slide

  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"}

    View Slide

  194. JSON
    JavaScript
    EDN
    Clojure

    View Slide

  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"}

    View Slide

  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

    View Slide

  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"}

    View Slide

  198. ( )
    Values
    Functions

    View Slide

  199. ( )
    Values
    Functions References

    View Slide

  200. Game Reference

    View Slide

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

    View Slide

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

    View Slide

  203. Game Reference
    (def game-ref (atom (create-game ...)))
    Reference Constructor
    Initial Value

    View Slide

  204. Game Reference
    (def game-ref (atom (create-game ...)))
    (swap! game-ref take-next-move)
    Atomic Succession

    View Slide

  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

    View Slide

  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

    View Slide

  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)

    View Slide

  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

    View Slide

  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

    View Slide

  210. Clojure’s Time Model

    View Slide

  211. Clojure’s Time Model
    State is the current value of an identity.
    v1

    View Slide

  212. Clojure’s Time Model
    State is the current value of an identity.
    An identity is series of values over time.
    v1 v2 v3

    View Slide

  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

    View Slide

  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

    View Slide

  215. ( )
    Values
    Functions References
    Identity

    View Slide

  216. Data
    Methods
    References
    Identity?

    View Slide

  217. Add a new card type

    View Slide

  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))))

    View Slide

  219. Picture cards
    Candy Heart
    Peppermint Stick
    Ginger Bread
    Gum Drop
    Peanut Brittle
    Lollypop
    Ice Cream

    View Slide

  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))))

    View Slide

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

    View Slide

  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"))

    View Slide

  223. Protocols are
    not just another
    name for
    interfaces...

    View Slide

  224. they allow you to
    add new
    abstractions to
    existing types

    View Slide

  225. (ns abstraction-a)
    (defprotocol AbstractionA
    (foo [obj]))

    View Slide

  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]))

    View Slide

  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!"

    View Slide

  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]))

    View Slide

  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]))

    View Slide

  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!"

    View Slide

  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

    View Slide

  232. ( )
    Values
    Functions References
    Identity

    View Slide

  233. ( )
    Values
    Functions References
    Namespaces Identity

    View Slide

  234. ( )
    Values
    Functions References
    Namespaces Identity
    Polymorphism

    View Slide

  235. Data
    Methods
    References
    Identity?

    View Slide

  236. Data
    Methods
    References
    Namespace
    Identity?

    View Slide

  237. Data
    Methods
    References
    Polymorphism
    Namespace
    Identity?

    View Slide

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

    View Slide

  239. (require '[clojure.core.reducers :as r])
    (->> (range 100000)
    (r/map inc)
    (r/reduce +))
    (->> (range 100000)
    (map inc)
    (reduce +))

    View Slide

  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

    View Slide

  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”

    View Slide

  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)))

    View Slide

  243. Parallel Collections

    View Slide

  244. Birthday party fun!

    View Slide

  245. It is bound to
    happen...

    View Slide

  246. View Slide

  247. View Slide

  248. I don’t want to
    go back to the
    gum drops!

    View Slide

  249. View Slide

  250. I can remember
    what the game
    looked like, why
    can’t your
    program?!?

    View Slide

  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.

    View Slide

  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

    View Slide

  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))

    View Slide

  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))

    View Slide

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

    View Slide

  256. How do you want
    to spend your
    complexity
    budget?

    View Slide

  257. Tradeoffs

    View Slide

  258. Tradeoffs
    Different way of thinking takes time.

    View Slide

  259. Tradeoffs
    Different way of thinking takes time.
    Idiomatic Clojure is slower than idiomatic Java
    in micro benchmarks.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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?

    View Slide

  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?

    View Slide

  264. Simplicity
    Ease
    Real Tradeoffs

    View Slide

  265. Thank you!
    BenMabey.com
    github.com/bmabey
    @bmabey

    View Slide

  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

    View Slide

  267. Extra Slides

    View Slide

  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;
    }

    View Slide

  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

    View Slide

  270. Go in Clojure
    (use 'clojure.core.async)
    (defn google [query]
    (let [c (chan)]
    (go (>! c ((go (>! c ((go (>! c ((go (loop [i 0 ret []]
    (if (= i 3)
    ret
    (recur (inc i) (conj ret (alt! [c t] ([v] v)))))))))

    View Slide

  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

    View Slide

  272. Its my ball
    of mud!

    View Slide

  273. “I remain unenthusiastic about actors.”
    Rich Hickey

    View Slide

  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!"

    View Slide

  275. Color # in Deck
    Red 6
    Orange 4
    Yellow 6
    Green 4
    Blue 6
    Purple 4
    Create a Card Deck

    View Slide

  276. Create a Card Deck
    public class CardDeck {
    ! private Stack cards;
    ! private static final Map CARD_GROUPS;
    ! static {
    ! ! CARD_GROUPS = new HashMap();
    ! ! 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);
    ! }

    View Slide

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

    View Slide

  278. Create a Card Deck

    View Slide

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

    View Slide

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

    View Slide

  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)

    View Slide

  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))

    View Slide

  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))

    View Slide

  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)

    View Slide

  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]

    View Slide

  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))

    View Slide

  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

    View Slide

  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))

    View Slide

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

    View Slide

  290. Create a Card Deck
    ! private void addCardsToDeck() {
    ! ! cards = new Stack();
    ! ! // Add cards to deck based on color and number
    ! ! for (Map.Entry 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))

    View Slide