To follow along: brew install leiningen; lein repl
Demo goes here.
if
, do
, def
.
To follow along: brew install leiningen; lein repl
(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")
[word]
. Recall that square brackets construct a vector.
?
in the function name is a convention indicating a predicate.
indexable?
by itself returns to the function object
(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"
:name
, evaluate to themselves and are interned
(there is only ever a single instance per name)
{key1 value1 key2 value2 ...}
(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
(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
ns
macro is used to define and import namespaces
ns/name
;; 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)
fn
returns an anonymous function (not bound to a namespace)
#( ... )
syntax also returns anonymous functions
(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
(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"
(defn factorial [n]
(loop [cnt n acc 1]
(if (zero? cnt)
acc
(recur (dec cnt) (* acc cnt)))))
(factorial 3) ; => 6
(recur)
special form
(loop)
macro can be used as a recur
target, handy for accumulators
(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}
->
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"
->
inserts results as the second argument to subsequent functions,
->>
inserts results as the last argument
(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 ; => []
swap!
at once).
@
macro, returning the current value
#{:a :b :c}
and regular expressions: #"ab[0-9]+"
Next up: ClojureScript
goog.*
Closure libraries
(js/alert "Hello, world!") ; => nil
(.log js/console "Log message") ; => nil
(.-href (.-location js/document)) ; => http://localhost/
js
namespace
(.method instance args)
(.-fieldname instance)
brew install leiningen
(:use)
namespace macro) are not well-documented
Demo stuff. Possibly named-pool / http-kit.
Questions?
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.
;; (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
Refs
|
Manage access to multiple memory locations in synchronous transactions |
Agents
|
Manage an async queue of updates to a single location |
Atoms
|
Manage atomic access to shared state |
Vars
|
Manage access to dynamically-scoped global vars via thread isolation |
pcalls
, pvalues
, and pmap
core.async
(+ 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"
'(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
'
prevents items from being evaluated
(list x y z)
evaluates to the list (x y z)
car
and cdr
are names derived from from the names of
registers on the IBM 704
(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)
map
, reduce
,
filter
, etc)
#'
) for function quoting
(defun sum-list (input)
(if (null input)
0
(+ (car input)
(sum-list (cdr input)))))
(sum-list '(3 4 5)) ; => 12
(sum-list '()) ; => 0
This example is not tail-call optimised
(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