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

Clojure: Your New Favorite Language

Clojure: Your New Favorite Language

An overview of what Clojure is, and what makes it special.

Updated as presented March 18th at Portland JUG.

Howard M. Lewis Ship

March 18, 2014
Tweet

More Decks by Howard M. Lewis Ship

Other Decks in Programming

Transcript

  1. “A language that doesn't affect the way you think about

    programming is not worth knowing.” ! – Alan Perlis
 First recipient of the Turing Award
  2. “In mathematics, a function is a relation between a set

    of inputs and a set of permissible outputs with the property that each input is related to exactly one output.”
  3. public class TaxPayer { private String firstName, lastName, SSN; !

    private boolean donate = false; ! public String getFirstName() { return firstName; } ! public void setFirstName(String firstName) { this.firstName = firstName; } ! public String getLastName() { return lastName; } ! public void setLastName(String lastName) { this.lastName = lastName; … } Noun Verb Verb Verb Verb
  4. (def tax-payer {
 :first-name "Howard" :last-name "Lewis Ship" :ssn "…"

    :donate true }) (assoc tax-payer :tax-schedule "C")
 ➠ {:first-name "Howard"
 :last-name "Lewis Ship"
 :ssn "…"
 :donate true
 :tax-schedule "C"
 } Generic Verb
  5. “It is better to have 100 functions operate on one

    data structure than 10 functions on 10 data structures.”
 
 — Alan Perlis
  6. Pop Quiz Can any of these parenthesis be removed? if

    ((user != null && ((user.perms & WRITE_PERM) != 0)) || (record.version == snapshot.version)) { … } Don't Know / Don't Care
  7. if ((user != null && ((user.perms & WRITE_PERM) != 0))

    || (record.version == snapshot.version)) { … } (if (or (and user (-> user :perms (bit-and WRITE_PERM) pos?) (= (:version record) (:version snapshot))) … )
  8. “Has anybody noticed that as languages grow in complexity, the

    best programmers switch to simpler languages?”
 —“Uncle” Bob Martin
  9. Call a function Define a function Define and pass a

    function How do I do ______ in Clojure?
  10. (defn largest [p1 p2] (if (> p1 p2) p1 p2))

    symbol vector of parameters
  11. “A long double run for ten” “A run of four”

    “A different run of four” “A pair for two” “Fifteen for two”
  12. Five or Four Card Flush Fifteens Pairs Runs of three,

    four, five Right Jack — “His Knees”
  13. public enum Rank { ACE(1), TWO(2), THREE(3),
 … JACK(10), QUEEN(10),

    KING(10); ! public final int points; ! Rank(int points) { this.points = points; } }
  14. public class Card { public final Suit suit; ! public

    final Rank rank; ! public Card(Suit suit, Rank rank) { this.suit = suit; this.rank = rank; } ! @Override public String toString() { return String.format("%s of %s", StringUtils.capitalize(rank.name().toLowerCase()), StringUtils.capitalize(suit.name().toLowerCase())); } }
  15. public interface Scorer { void score(List<Card> hand, List<Score> scores); }

    ! public class Score { public final int points; ! public final String description; ! public final Set<Card> cards = new HashSet<>(); ! public Score(int points, String description) { this.points = points; this.description = description; } ! public Score(int points, String description, Collection<Card> cards) { this(points, description); ! this.cards.addAll(cards); } }
  16. public class FlushScorer implements Scorer { @Override public void score(List<Card>

    hand, List<Score> scores) { Suit flipSuit = hand.get(0).suit; ! boolean fiveFlush = true; ! for (int i = 1; i < 5; i++) { if (hand.get(i).suit != flipSuit) { fiveFlush = false; break; } } ! if (fiveFlush) { scores.add(new Score(5, "five card flush", hand)); ! return; } ! Suit firstSuit = hand.get(1).suit; ! for (int i = 2; i < 5; i++) { if (hand.get(i).suit != firstSuit) { return; } } ! scores.add(new Score(4, "four card flush", hand.subList(1, 5))); } }
  17. (defn flush? "Returns true if the set of cards all

    have the same suit." [hand] (let [suits (map :suit hand) first-suit (first suits)] (every? #(= first-suit %) (rest suits)))) !
  18. (defn flush? "Returns true if the set of cards all

    have the same suit." [hand] (let [suits (map :suit hand) first-suit (first suits)] (every? #(= first-suit %) (rest suits)))) !
  19. (defn flush? "Returns true if the set of cards all

    have the same suit." [hand] (let [suits (map :suit hand) first-suit (first suits)] (every? #(= first-suit %) (rest suits)))) !
  20. (map inc [3 4 5]) (4 5 6) ➠ (list

    (inc 3) (inc 4) (inc 5))
  21. [{:suit :spades :rank 3} {:suit :hearts :rank 4} {:suit :hearts

    :rank 9} {:suit :hearts :rank 10} {:suit :hearts :rank :queen}] (:spades :hearts :hearts :hearts :hearts) (map :suit hand)
  22. (defn flush? "Returns true if the set of cards all

    have the same suit." [hand] (let [suits (map :suit hand) first-suit (first suits)] (every? #(= first-suit %) (rest suits)))) !
  23. Clojure seqs first - first element in collection rest -

    remaining elements in collection next - remaining elements or nil • java.util.Collection • String • Java primitive array • java.util.Map
  24. (defn flush? "Returns true if the set of cards all

    have the same suit." [hand] (let [suits (map :suit hand) first-suit (first suits)] (every? #(= first-suit %) (rest suits)))) !
  25. (defn flush? "Returns true if the set of cards all

    have the same suit." [hand] (let [suits (map :suit hand) first-suit (first suits)] (every? #(= first-suit %) (rest suits)))) !
  26. (defn score-flush "Returns a score map or nil depending on

    finding a four or five card flush." [hand] (cond (flush? hand) {:points 5 :text "five card flush" :cards hand} (flush? (rest hand)) {:points 4 :text "four card flush" :cards (rest hand)}))
  27. (defn score "Constructs a score map." [points text cards] {:points

    points :text text :cards cards}) ! (defn score-flush "Returns a score map…" [hand] (cond (flush? hand) (score 5 "five card flush" hand) (flush? (rest hand)) (score 4 "four card flush" (rest hand))))
  28. public class PairsScorer implements Scorer { @Override public void score(List<Card>

    hand, List<Score> scores) { for (int i = 0; i < 4; i++) { Card left = hand.get(i); ! for (int j = i + 1; j < 5; j++) { Card right = hand.get(j); ! if (left.rank == right.rank) { Score score = new Score(2, "pair"); score.cards.add(left); score.cards.add(right); ! scores.add(score); } } } } } Match Cards Identify Pairs Score
  29. Pop Quiz (rest [1 2 3])➠ ____ (rest '(2 3))➠

    ____ (rest '(3))➠ ____ (rest ())➠ ____ (2 3) (3) () ()
  30. (defn score-pairs "Finds all pairs in the hand, returning a

    scoring map for each one." [hand] (for [sub-hand (take (-> hand count dec) (iterate rest hand)) :let [left (first sub-hand)] right (rest sub-hand) :when (= (:rank left) (:rank right))] (score 2 "a pair" [left right])))
  31. (defn score-pairs "Finds all pairs in the hand, returning a

    scoring map for each one." [hand] (for [sub-hand (take (-> hand count dec) (iterate rest hand)) :let [left (first sub-hand)] right (rest sub-hand) :when (= (:rank left) (:rank right))] (score 2 "a pair" [left right])))
  32. (for [suit [:hearts :diamonds :spades :clubs] rank [:ace 2 3

    4 5 6 7 8 9 10 :jack :queen :king]] {:suit suit :rank rank}) ({:suit :hearts, :rank :ace} {:suit :hearts, :rank 2} … {:suit :clubs, :rank :queen} {:suit :clubs, :rank :king}) ➠
  33. (defn score-pairs "Finds all pairs in the hand, returning a

    scoring map for each one." [hand] (for [sub-hand (take (-> hand count dec) (iterate rest hand)) :let [left (first sub-hand)] right (rest sub-hand) :when (= (:rank left) (:rank right))] (score 2 "a pair" [left right])))
  34. (defn score-pairs "Finds all pairs in the hand, returning a

    scoring map for each one." [hand] (for [sub-hand (take (-> hand count dec) (iterate rest hand)) :let [left (first sub-hand)] right (rest sub-hand) :when (= (:rank left) (:rank right))] (score 2 "a pair" [left right])))
  35. (defn score-pairs "Finds all pairs in the hand, returning a

    scoring map for each one." [hand] (for [sub-hand (take (-> hand count dec) (iterate rest hand)) :let [left (first sub-hand)] right (rest sub-hand) :when (= (:rank left) (:rank right))] (score 2 "a pair" [left right])))
  36. (defn score-pairs "Finds all pairs in the hand, returning a

    scoring map for each one." [hand] (for [sub-hand (take (-> hand count dec) (iterate rest hand)) :let [left (first sub-hand)] right (rest sub-hand) :when (= (:rank left) (:rank right))] (score 2 "a pair" [left right])))
  37. (defn score-pairs "Finds all pairs in the hand, returning a

    scoring map for each one." [hand] (for [sub-hand (take (-> hand count dec) (iterate rest hand)) :let [left (first sub-hand)] right (rest sub-hand) :when (= (:rank left) (:rank right))] (score 2 "a pair" [left right])))
  38. (defn score-pairs "Finds all pairs in the hand, returning a

    scoring map for each one." [hand] (for [sub-hand (take (-> hand count dec) (iterate rest hand)) :let [left (first sub-hand)] right (rest sub-hand) :when (= (:rank left) (:rank right))] (score 2 "a pair" [left right]))) Match Cards Identify Pairs Score
  39. private final Lock lock = new ReentrantReadWriteLock(); ! public String

    getCachedValue() { try { lock.readLock().lock(); ! … } finally { lock.readLock().unlock(); } }
  40. private final Lock lock = new ReentrantReadWriteLock(); ! public String

    getCachedValue() { withReadLock(lock) { … } }
  41. Literals
 
 "Hello" 2.5 nil Vectors
 
 [ … ]

    Lists 
 '(1 2 3) Maps
 ! { … } Function Calls, Special Forms, Macros (a b c) Sets
 ! #{ … } Forms
  42. (defmacro with-read-lock [lock & body] `(let [read-lock# (.readLock ~lock)] (try

    (.lock read-lock#) ~@body (finally (.unlock read-lock#)))))
  43. (defmacro with-read-lock [lock & body] `(let [read-lock# (.readLock ~lock)] (try

    (.lock read-lock#) ~@body (finally (.unlock read-lock#)))))
  44. (defmacro with-read-lock [lock & body] `(let [read-lock# (.readLock ~lock)] (try

    (.lock read-lock#) ~@body (finally (.unlock read-lock#))))) Syntax Quote
  45. (defmacro with-read-lock [lock & body] `(let [read-lock# (.readLock ~lock)] (try

    (.lock read-lock#) ~@body (finally (.unlock read-lock#))))) read-lock__1234__auto__
  46. (defmacro with-read-lock [lock & body] `(let [read-lock# (.readLock ~lock)] (try

    (.lock read-lock#) ~@body (finally (.unlock read-lock#)))))
  47. (defmacro with-read-lock [lock & body] `(let [read-lock# (.readLock ~lock)] (try

    (.lock read-lock#) ~@body (finally (.unlock read-lock#)))))
  48. (defmacro with-read-lock [lock & body] `(let [read-lock# (.readLock ~lock)] (try

    (.lock read-lock#) ~@body (finally (.unlock read-lock#)))))
  49. (defn with-read-lock [lock f] (let [read-lock (.readLock lock)] (try (.lock

    read-lock) (f) (finally (.unlock read-lock))))) (with-read-lock my-lock #(println my-lock))
  50. Not Covered Here Concurrency — Vars, Atoms, and Refs ClojureScript

    — Clojure in the Browser Datomic — The Database as a Value Ring, Compojure — Web and API Servers Pedestal, Reagent, Om, Hoplon — Client Frameworks core.test, midje, speclj — testing frameworks 100's of built-in functions and macros
  51. Howard's Workflow 1. Terrific Idea! 2. Implement 3. Test 4.

    Gloat/Tweet 5. Discover it's already in clojure.core 6. Repeat
  52. Power Focus Simplicity ⊙ Macros ⊙ Java Interop ⊙ Syntax

    ⊙ Immutability ⊙ REPL ⊙ clojure.core
  53. “Simplicity and elegance are unpopular because they require hard work

    and discipline to achieve and education to be appreciated.” ! —Edsger Dijkstra
  54. Links http://clojure.org http://clojure.org/cheatsheet http://kapeli.com/dash https://github.com/ technomancy/leiningen http://clojars.org http://www.lighttable.com/ https://nightcode.info/
 http://cursiveclojure.com/

    https://code.google.com/p/ counterclockwise/ https://www.4clojure.com/ http://clojurekoans.com/ http://www.cognitect.com/ podcast http://howardlewisship.com