Skip to content

Instantly share code, notes, and snippets.

@Integralist
Last active August 29, 2015 14:00
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Integralist/11471364 to your computer and use it in GitHub Desktop.
Save Integralist/11471364 to your computer and use it in GitHub Desktop.
Expanding scratch pad of Clojure code

What we cover:

  • defining functions
  • anonymous functions
  • short hand anonymous functions
  • complex reducing
  • alternatives to the reduce example
  • demonstrate how vectors and lists are different
  • custom method that separates the specified predicate from a sequence
  • destructuring with let bindings
  • utilising do to create side effects
  • closures
  • delays, futures, promises
  • atoms for thread safety
  • macros
  • macro syntax quote
  • refactoring (and unquote slicing)
  • multimethods
  • lazy evaluation
  • data structures
  • channels
  • refs
  • composition
  • partial application
; defining a simple function
(defn foobar "blah" [x]
(+ x 1))
; use the above function
; => 11
(foobar 10)
; anonymous function
; => 6
((fn [a [b c]] (+ a b c)) 1 [2 3])
; short-hand anonymous function
; here is an example of a long-form anonymous function
; => {:baz 3, :foo 1}
((fn [data] (select-keys data [:foo :baz])) {:foo 1 :bar 2 :baz 3})
; here is the short-hand variation using `#()` and passing in `%` as the placeholder value
; use `%` if one arg, `%n` (e.g. `%1`, `%2` etc) when multiple args and `%&` acts like rest
(#(select-keys % [:foo :baz]) {:foo 1 :bar 2 :baz 3})
; reduce two vectors into a single map
; => {:c 3, :b 2, :a 1}
(reduce
(fn
[m [k v]]
(assoc m k v))
{}
(map vector [:a :b :c] [1 2 3]))
; break down the above reduce...
; `vector` constructs a new vector containing each argument
; => [[:a :b :c] [1 2 3]]
(vector [:a :b :c] [1 2 3])
; `map` executes the `vector` fn each loop iteration
; each loop iteration gains the first item from each vector (passed in as arguments)
; and ultimately returns a sequence constructed of vectors
; => ([:a 1] [:b 2] [:c 3])
(map vector [:a :b :c] [1 2 3])
; the result of a sequence ([:a 1] [:b 2] [:c 3]) needs to be passed without being evaluated
; so we quote the sequence (otherwise we'd need to change the sequence into an explicit vector)
; that is then passed (along with {} which acts as an accumulator)
; to an anonymous function which `reduce` then calls recursively
; {:c 3, :b 2, :a 1}
(reduce
(fn
[m [k v]]
(assoc m k v))
{}
'([:a 1] [:b 2] [:c 3]))
; prints x and y so you can see how map passes in the first item from each vector
; note that the function doesn't return anything and so we see `nil` appear as well
; (:a 1:b 2nil :c 3nil nil)
(map (fn [x y] (pr x y)) [:a :b :c] [1 2 3])
; an alternative (expanded for easier reading) version
; where we use `do` to execute side effects
; and the function acts like it should by returning a value
; (a redundant value, but a value nonetheless)
; (:a 1:b 2"foo" :c 3"foo" "foo")
(map
(fn [x y]
(do (pr x y))
"foo")
[:a :b :c]
[1 2 3])
; here is the original reduce code we had:
; (reduce (fn [m [k v]] (assoc m k v)) {} (map vector [:a :b :c] [1 2 3]))
; there are alternatives, each one becoming more focused than the other
; until we realise that Clojure has provided a function that handles exactly our use case
(apply hash-map (interleave [:a :b :c] [1 2 3]))
(into {} (map vector [:a :b :c] [1 2 3]))
(zipmap [:a :b :c] [1 2 3])
; Increment values of a map (remember we don't mutate the original data)
; {:a 2, :c 3, :b 5}
(merge-with + {:a 1, :c 3, :b 2} {:a 1 :b 3})
; vectors are added to the rear
; lists are added to the front
; vector example:
; [1 2 3 99 :bottles]
(into [1 2 3] [99 :bottles])
; list example:
; [:bottles 99 1 2 3]
; you should notice the arguments (`99` and `:bottles`) are reversed.
; this is because `99` is added to the front and then
; `:bottles` is added to the front (pushing `99` out of first position)
(into '(1 2 3) [99 :bottles])
; custom method that separates the predicate from a sequence
; a predicate is a fancy word for a function that returns a boolean (true/false) value
; e.g. from `[1 2 3 4 5]`, if the predicate is `odd?` then we'll get back `[(1 3 5) (2 4)]`
(def separate
(fn [predicate sequence]
[(filter predicate sequence) (remove predicate sequence)]))
; [(1 3 5) (2 4)]
(separate odd? [1 2 3 4 5])
; (1 3 5)
(let [[o e] (separate odd? [1 2 3 4 5])] o)
; (2 4)
(let [[o e] (separate odd? [1 2 3 4 5])] e)
; rest using &
; x: 1 more: (2 3)
(let [[x & more] [1 2 3]]
(println "x:" x "more:" more))
; hold the complete list using :as
; x: 1 more: (2 3) full list: [1 2 3]
(let [[x & more :as full-list] [1 2 3]]
(println "x:" x "more:" more "full list:" full-list))
; x: 5 y: 7
(let [{the-x :x the-y :y} {:x 5 :y 7}]
(println "x:" the-x "y:" the-y))
; The :keys directive allows you to specify keys that you would like as locals with the same name
; x: 5 y: 7
(let [{:keys [x y]} {:x 5 :y 7}]
(println "x:" x "y:" y))
; default values
; x: 0 y: 7
(let [{:keys [x y] :or {x 0 y 0}} {:y 7}]
(println "x:" x "y:" y))
; use `do` to allow side effects...
; (1 3 5)
; (2 4)
; nil
(let [[o e] (separate odd? [1 2 3 4 5])] (do (prn o) (prn e)))
; example of closure via an anonymous function `fn [] ()`
; and use `str` for concatenation
(defn greeting
[x]
(fn [y] (str x y)))
(def exclaim (greeting "hello"))
(exclaim "!") ; => "hello!"
; the `delay` function returns an object that works much like
; an anonymous function in that the inner expression isn't evaluated until called
; the body of the `delay` object will only yield when forced using `force` or `defer/@`
(def later (fn [] (prn "a") (+ 1 1)))
(later) ; => "a" and 2 (always! it never caches the evaluation)
(def later (delay (prn "a") (+ 1 1)))
(deref later) ; => "a" and 2 on the first evaluation, but 2 for all calls after that
@later ; => interpreted as `(deref later)` also known as a 'wormhole' operator
; a `future` is a `delay` that evaluates its body in parallel
; this will evaluate all but the last expression `(+ 1 2)` in parallel
; so when testing from the REPL you'll see `#'user/x` returned as expected and
; "hi", "!", 2 will be displayed somewhere around `#'user/x`
; (either before or after as the evaluation is happening concurrently)
(def x (future (prn "hi") (prn "!") (prn (+ 1 1)) (+ 1 2)))
@x ; => 3 (this is the last expression that was lazily evaluated and is now always returned)
; futures can cause odd side effects because of their concurrent nature
; for example if you run the following code:
(dotimes [i 5] (prn i)) ; => 0 1 2 3 4 nil (each on a separate line)
; but if you use a future then because the code executes in parallel you could see something like:
; nil02
; 4
; 1
;
;
; 3
(dotimes [i 5] (future (prn i)))
; notice how the return value of `nil` and the numbers `0` and `2` all executed at once
; and the line breaks were triggered in odd places.
; `agents` and `locks` apparently resolve these side effects
; delays defer evaluation, and futures parallelize them
; promises let us defer something that might not even exist yet
; we can set-up a promise and then assign something to it using `deliver`
; if we try to `deref` a promise that doesn't have any content yet then we'll wait around indefinitely!
; so make sure to `deliver` your promise before trying to deref it.
; promises also can't be changed once they've been delivered
(def box (promise))
box ; => #<core$promise$reify__6310@746f6266: :pending>
(deliver box :live-scorpions!) ; => #<core$promise$reify__6310@746f6266: :live-scorpions!>
@box ; => :live-scorpions!
(deliver box :puppy) ; => nil
@box ; => :live-scorpions!
; the following code first demonstrates non-thread safe code
; then we use an `atom` and its associated function `swap!` to change the value of the atom
; ultimately the code becomes thread safe
(def xs #{})
(dotimes [i 10] (future (def xs (conj xs i))))
xs ; => #{2 5 6 8 9}
; notice the set doesn't hold values zero to nine like we expect
; this is because running conj on the *current* value over multiple threads means
; we can't guarantee the value of xs (unless it's locked/synchronised/mutexed)
; here is the thread safe version
(def xs (atom #{}))
(dotimes [i 10] (future (swap! xs conj i)))
@xs ; => #{0 1 2 3 4 5 6 7 8 9}
(defmacro postfix-notation
"I'm too indie for prefix notation"
[expression]
(conj (butlast expression) (last expression)))
; The expression/form (1 1 +) would normally cause an error
; But as we're calling a macro it is passed unevaluated
; => 2
(postfix-notation (1 1 +))
; We can see the evaluated result of a macro
; by using the macroexpand function along with a single quote
; to prevent evaluation (as normal functions fully evaluate their arguments)
; => ; => (+ 1 1)
(macroexpand '(postfix-notation (1 1 +)))
; Building macros is usually about constructing a list
; that when called will be evaluated
; Because of this quoting, either '(+ 1 1) or (quote (+ 1 1)), is used a lot
; This is so you can construct an unevaluated list within the macro
; See the following macro uses quotes to prevent `if` and `do` being evaluated
;; This is when's actual source
(defmacro when
"Evaluates test. If logical true, evaluates body in an implicit do."
{:added "1.0"}
[test & body]
(list 'if test (cons 'do body)))
(macroexpand '(when (the-cows-come :home)
(call me :pappy)
(slap me :silly)))
; =>
(if (the-cows-come :home)
(do (call me :pappy)
(slap me :silly)))
; Here is another macro example
(defmacro unless
"Inverted 'if'"
[test & branches]
(conj (reverse branches) test 'if))
(macroexpand '(unless (done-been slapped? me)
(slap me :silly)
(say "I reckon that'll learn me")))
; =>
(if (done-been slapped? me)
(say "I reckon that'll learn me")
(slap me :silly))
; Instead of having to generate lots of lists inside your macro
; all of which need to their function symbols to be quoted
; you could instead use the macro syntax quote ` and ~
; ` will return the fully qualified symbols so that the symbol includes its namespace
; so `+ will return clojure.core/+
; and `(+ 1 2) will return (clojure.core/+ 1 2)
; ~ allows you to temporarily unquote a form expression
; so `(+ 1 ~(inc 1)) returns (clojure.core/+ 1 2)
(defmacro triple [x]
`(+ ~x ~x ~x))
; => 12
(triple 4)
; Building a list with the list function
; => (+ 1 2)
(list '+ 1 (inc 1))
; Building a list from a quoted list - super awkward
; => (+ 1 2)
(concat '(+ 1) (list (inc 1)))
; Building a list with unquoting
; => (clojure.core/+ 1 2)
`(+ 1 ~(inc 1))
; All of this is taken from http://www.braveclojure.com/writing-macros/
; you can define a macro that is quote complex
(defmacro code-critic
"phrases are courtesy Hermes Conrad from Futurama"
[{:keys [good bad]}]
(list 'do
(list 'println
"Great squid of Madrid, this is bad code:"
(list 'quote bad))
(list 'println
"Sweet gorilla of Manila, this is good code:"
(list 'quote good))))
; slight refactor with macro syntax quoting
(defmacro code-critic
"phrases are courtesy Hermes Conrad from Futurama"
[{:keys [good bad]}]
;; Notice the backtick - that's the syntax quote
`(do (println "Great squid of Madrid, this is bad code:"
(quote ~bad))
(println "Sweet gorilla of Manila, this is good code:"
(quote ~good))))
; usage
; => Cursed bacteria of Liberia, this is bad code: (1 + 1)
; => Sweet sacred boa of Western and Eastern Samoa, this is good code: (+ 1 1)
(code-critic {:good (+ 1 1) :bad (1 + 1)})
; and then refactor it so some of the complexity
; is placed inside another function
; that function is then only really useful when
; used from inside the macro
(defn criticize-code
[criticism code]
`(println ~criticism (quote ~code)))
(defmacro code-critic
[{:keys [good bad]}]
`(do ~(criticize-code "Cursed bacteria of Liberia, this is bad code:" bad)
~(criticize-code "Sweet sacred boa of Western and Eastern Samoa, this is good code:" good)))
; more refactoring
(defmacro code-critic
[{:keys [good bad]}]
`(do ~(map #(apply criticize-code %)
[["Great squid of Madrid, this is bad code:" bad]
["Sweet gorilla of Manila, this is good code:" good]])))
; uh-oh that caused an error
; use macroexpand to decipher what's happening
; for those short on time, the fix is unquote splicing ~@
; Unquote splicing is like unwrapping a seq'able data structure (examples below)
; Without unquote splicing
; => (clojure.core/+ (1 2 3))
; which would cause an error
`(+ ~(list 1 2 3))
; With unquote splicing
; => (clojure.core/+ 1 2 3)
`(+ ~@(list 1 2 3))
(defmacro code-critic
[{:keys [good bad]}]
`(do ~@(map #(apply criticize-code %)
[["Sweet lion of Zion, this is bad code:" bad]
["Great cow of Moscow, this is good code:" good]])))
(code-critic {:good (+ 1 1) :bad (1 + 1)})
; final refactoring
(def criticisms {:good "Sweet manatee of Galilee, this is good code:"
:bad "Sweet giant anteater of Santa Anita, this is bad code:"})
(defn criticize-code
[[criticism-key code]]
`(println (~criticism-key criticisms) (quote ~code)))
(defmacro code-critic
[code-evaluations]
`(do ~@(map criticize-code code-evaluations)))
; define our multimethod `greeting`
; and specify that :language will be the variant
(defmulti greeting :language)
; create some methods that are built from the multimethod
; the underscore in the argument list is a convention that indicates we do nothing with the argument
(defmethod greeting "English" [_] "Hi")
(defmethod greeting "French" [_] "Salut")
; test it
(greeting {:language "English"}) ; => "Hi"
(greeting {:language "French"}) ; => "Salut"
; we'll see that the sequence is lazily evaluated
; meaning the entire range isn't created initially
; that only happens when the user requests the relevant data
(def squares
(map
(fn [x] (println x) (* x x))
(range 1 100)))
(first squares) ; prints 1 to 31 (as a side effect) and returns first value: 1

Lists: (1 2 3) are implemented as a Linked List and so they are more efficient for walking/iterating a long collection and adding/removing items from the beginning of the list.

Vectors: [1 2 3] have index access and are a more performant sequence collection for adding/removing items from the end of the collection. It's best to use (peek [1 2 3]) to find the last item of the collection than (last [1 2 3]) as peek determines the best algorithm for the data structure where as last walks the collection looking for the last item.

; lein new async-test
; inside our project.clj
(defproject async-test "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [
[org.clojure/clojure "1.5.1"]
[org.clojure/core.async "0.1.303.0-886421-alpha"]])
(ns async.example
(:require [clojure.core.async :as async :refer :all]))
; or (require '[clojure.core.async :as async :refer :all])
(def hand-off (chan)) ; create a symbol whose value is a new Channel
; (>! channel value) == put
; (<! channel) == take
(go
(dotimes [x 10]
(Thread/sleep 1000)
(>! hand-off x)))
(go
(while true
(println (<! hand-off))))
(def bank (ref 0))
(def bank-props (ref #{"Baltic" "Park Place" "Boardwalk"}))
(def player (ref [{:money 1500
:props #{}}]))
@bank
;=> 0
@bank-props
;=> #{"Baltic" "Boardwalk" "Park Place"}
@player
;=> [{:money 1500, :props #{}}]
(defn buy-property
[prop-name cost player-num]
(dosync
(alter bank + cost)
(alter player update-in [player-num :money] - cost)
(alter bank-props disj prop-name)
(alter player update-in [player-num :props] conj prop-name)))
(buy-property "Baltic" 200 0)
@bank
;=> 200
@bank-props
;=> #{"Boardwalk" "Park Place"}
@player
;=> [{:money 1300, :props #{"Baltic"}}]
(select-keys {:a 1 :b 2 :c 3} [:a :c]) ; {:c 3, :a 1}
(vals {:c 3, :a 1}) ; (3 1)
; the above can be composed manually to:
(vals (select-keys {:a 1 :b 2 :c 3} [:a :c])) ; (3 1)
; but we can use the comp function instead of that:
(def c (comp vals select-keys))
(c {:a 1 :b 2 :c 3} [:a :c]) ; (3 1)
(defn foo [x, y, z] (prn x y z))
(foo "a" "b" "c") ; "a" "b" "c"
(def foo-a (partial foo "a"))
(foo-a "b" "c") ; "a" "b" "c"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment