Agenda

An Abbreviated History of Lisp Dialects

Quick Clojure REPL Demo

Demo goes here.

To follow along: brew install leiningen; lein repl

Clojure: Features

Clojure: Basic Function Definition

(defn indexable? [word]
  "Return true if word should be included in the index"
  (> (count word) 2))

(indexable? "to")               ; => false
(indexable? "clojure")          ; => true
(filter indexable? ["I" "am" "writing" "in" "clojure"])
                                ; => ("writing" "clojure")

Clojure: Maps and keywords

(def m {:title "The Joy of Clojure", :pages 360,
        :authors ["Michael Fogus" "Chris Houser"]})
(get m :pages)                   ; => 360

:my-keyword                      ; => :my-keyword

(identical? :my-keyword :my-keyword)   ; => true
(keyword "a-string")             ; => :a-string
(str :kw)                        ; => ":kw"

Clojure: Working with Maps

More maps and keywords
(def alpha {:a 1, :b 2, :c 3})
;; Maps can be called as functions which produce their values
(alpha :a)                      ; => 1
(alpha :c)                      ; => 3
(alpha :oops)                   ; => nil

;; Keywords can be used as functions that get values from maps
(:a alpha)                      ; => 1
(:nope alpha)                   ; => nil

;; You can specify default values in either case
(:nope alpha "default")         ; => "default"
(alpha :oops 72)                ; => 72

Clojure: Namespaces

(ns demo.core "Optional docstring"
  (:require [compojure.route :as route]
            [clojure.data.json :refer [json-str read-json]]
            [clojure.tools.logging :refer :all))

(route/not-found "Page not found")  ; Explicit namespace

(read-json "{\"abc\": 123}")        ; Direct import

(debug "This is a log statement")   ; From tools.logging

Clojure: More Function Syntax

;; reduce applies the same function to adjoining items in a list
;; fn is just like defn, but returns an anonymous function
(reduce (fn [x y] (str x "-" y))
        [123 456 "abc" 0])      ; => "123-456-abc-0"

;; #() syntax uses %1, %2, %3... values as arguments
(reduce #(str %1 ":" %2)
        [123 456 "abc" 0])      ; => "123:456:abc:0"

;; Single-argument functions can just use % as the argument name
(map #(str %) [1 2 3 4])        ; => ("1" "2" "3" "4")

(map #(* % %) [1 2 3 4 5])      ; => (1 4 9 16 25)

Clojure: Let and Lexical Closures

(defn log-username [json-string]
  (let [parsed-data (json/read-json json-string)
        username (:username parsed-data)]
    (log/debug username)))

(let [num 4]
  (defn addnum [i] (+ i num)))
(addnum 6)                         ; => 10

(defn adder [amount] (fn [x] (+ x amount)))
(def plus5 (adder 5))
(plus5 10)                         ; => 15

Clojure: Destructuring

(defn destr [[one two & tail]]
  (str one "-" two ":" tail))
(destr [1 2 3 4 5 6])           ; => "1-2:(3 4 5 6)"

(defn full [{first :fname, last :lname}]
  (str first " " last))
(full {:fname "Bob", :lname "Dobbs"})   ; => "Bob Dobbs"

(defn coord [{x-pos :x, y-pos :y :or {x-pos 0, y-pos 0}}]
  (str x-pos "," y-pos))
(coord {:x 1, :y 2})            ; => "1,2"
(coord {:y 7})                  ; => "0,7"
(coord {})                      ; => "0,0"

Clojure: Recursion

(defn factorial [n]
  (loop [cnt n acc 1]
    (if (zero? cnt)
      acc
      (recur (dec cnt) (* acc cnt)))))

(factorial 3)                   ; => 6

Clojure: Java Interoperability

(System/getProperty "os.name")  ; => "Mac OS X"

;; Call the "startsWith" method on the object "abcdef" with
;; the argument "abc":  "abcdef".startsWith("abc")
(.startsWith "abcdef" "abc")    ; => true

(def java-map (new java.util.HashMap))
(.put java-map "key" 123)       ; => nil
java-map                        ; => {"key" 123}

Clojure: The Threading Macros: -> and ->>

(-> 3 (+ 3) (/ 2) (- 7))        ; => -4

(macroexpand-all '(-> 3 (+ 3) (/ 2) (- 7)))
; => (- (/ (+ 3 3) 2) 7)

(-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first)

;; Possibly clearer expression of above
(-> "a b c d"
    (.toUpperCase ,,,)
    (.replace ,,, "A" "X")
    (.split ,,, " ")
    (first ,,,))                ; => "X"

Clojure: Atoms

(def a (atom []))
@a                              ; => []

(swap! a (fn [current-value] (conj current-value "hello")))
@a                              ; => ["hello"]

(swap! a (fn [current-value] (conj current-value "hello")))
@a                              ; => ["hello" "hello"]

(reset! a [])
@a                              ; => []

Clojure: Fairly Major Topics I Didn’t Cover

Next up: ClojureScript

ClojureScript: Features

ClojureScript: Differences from Clojure

ClojureScript: Javascript Interop

(js/alert "Hello, world!")      ; => nil

(.log js/console "Log message") ; => nil

(.-href (.-location js/document)) ; => http://localhost/

Tools and Links

Things That Could Be Better

Clojure / ClojureScript Demo

Demo stuff. Possibly named-pool / http-kit.

That’s it

Questions?

Appendices

The following slides have some background on LISP and some more involved topics in Clojure. I’ve decided to skip them in the talk itself unless there’s extra time, but I’m leaving them in here just in case.

Clojure: Laziness

;; (iterate) takes a function and a starting value and produces an infinite sequence
;; (take n seq) lazily takes the first n members of a sequence
(take 3 (iterate inc 1))        ; => (1 2 3)

(take 4 (map #(* 3 %) (iterate inc 1)))
                                ; => (3 6 9 12)

(nth (iterate inc 1) 1000000)   ; => 1000001

Clojure: Concurrency, Parallelism and State

Lisp: Basics

(+ 3 4 5 6)                     ; => 18
(/ (+ 3 4 5 6) 2)               ; => 9
(= 9 (/ (+ 3 4 5 6) 2))         ; => T
(= 7 (/ (+ 3 4 5 6) 2))         ; => NIL
(string-upcase "hello")         ; => "HELLO"
(string-concat (string-upcase "hello,") "there")
                                ; => "HELLO,there"

Lisp: List Processing

'(1 2 3 4)                      ; => (1 2 3 4)
(list 1 (+ 1 1) 3 4)            ; => (1 2 3 4)
'(1 (+ 1 1) 3 4)                ; => (1 (+ 1 1) 3 4)
(car '(1 2 3 4))                ; => 1
;; Equivalent: (first '(1 2 3 4))
(cdr '(1 2 3 4))                ; => (2 3 4)
;; Equivalent: (rest '(1 2 3 4))
(cadadr '(1 (20 30) 4 5 6))     ; => 30

Lisp: Functions

(defun square (x)
  (* x x))
(square 4)                      ; => 16
(mapcar #'square '(1 2 3 4 5))  ; => (1 4 9 16 25)
(mapcar #'(lambda (x) (+ x 5))
        '(10 20 30))            ; => (15 25 35)

Lisp: Recursion

(defun sum-list (input)
  (if (null input)
      0
      (+ (car input)
         (sum-list (cdr input)))))

(sum-list '(3 4 5))          ; => 12
(sum-list '())               ; => 0
Note

This example is not tail-call optimised

Lisp: Macros

(defmacro if-not (condition true-form false-form)
  `(if (not ,condition) ,true-form ,false-form)))

(if-not (= 3 4) "true-value" "false-value")
; => "true-value"
(macroexpand-1 '(if-not (= 3 4) "true-value" "false-value"))
' => (IF (NOT (= 3 4)) "true-value" "false-value")

Next up: Clojure