1. CLOJURE BASICS
Kyle Oba
koba@pasdechocolat.com
@mudphone
Tuesday, March 19, 13
2. Clojure 1.5 was released on March 1st, 2013.
Read this to see changes:
https://github.com/clojure/clojure/blob/master/changes.md
My examples use 1.4.0.
Tuesday, March 19, 13
3. “Startups tend to be an all or nothing
proposition. You either get rich, or you get
nothing. In a startup, if you bet on the
wrong technology, your competitors will
crush you.”
Paul Graham
Tuesday, March 19, 13
4. “When you choose technology, you have to
ignore what other people are doing, and
consider only what will work the best.”
Paul Graham
Tuesday, March 19, 13
5. “In a big company, you can do what all the
other big companies are doing. But a
startup can't do what all the other startups
do. I don't think a lot of people realize this,
even in startups.”
Paul Graham
Tuesday, March 19, 13
6. “So if you're running a startup, you had
better be doing something odd. If not,
you're in trouble.”
Paul Graham
Tuesday, March 19, 13
7. “Lisp is so great not because of some
magic quality visible only to devotees, but
because it is simply the most powerful
language available. And the reason
everyone doesn't use it is that
programming languages are not merely
technologies, but habits of mind as well,
and nothing changes slower.”
Paul Graham
Tuesday, March 19, 13
8. “I'll begin with a shockingly controversial
statement: programming languages vary in
power.”
Paul Graham
Tuesday, March 19, 13
11. Rich Hickey is a genius. And, I’m just here to piss you off.
Clojure Concurrency
http://blip.tv/clojure/clojure-concurrency-819147
22:00 - 23:18
Simple Made Easy
http://www.infoq.com/presentations/Simple-Made-Easy
15:30 - 17:15
Tuesday, March 19, 13
12. Why you should take a look...
functional langs can more easily use multiple cores
purely functional is awkward when dealing with state
Clojure has refs, agents, atoms, dynamic binding
dynamic typing
Java invocation (not functional)
refs (STM)
JVM, calling Java is idiomatic
Tuesday, March 19, 13
13. CLOJURE BASICS
Why is Clojure so cool necessary?
Basic Setup x2 or 3
Language Basics
Working with State (or without it)
Working with government forms Java
Macros
Webby Stuff
Testing
Clojure + Processing + Kinect/OpenNI
Tuesday, March 19, 13
16. Why use the JVM?
The JVM is advanced technology.
Java libraries... lots of them.
Widely deployed
Tuesday, March 19, 13
17. Using the JVM
For example: https://github.com/mmcgrana/clj-redis
(require '[clj-redis.client :as redis])
(def db (redis/init))
(redis/ping db)
=> "PONG"
(redis/set db "foo" "BAR")
=> "OK"
(redis/get db "foo")
=> "BAR"
Tuesday, March 19, 13
21. Why is Clojure so cool?
Homoiconic
Modern LISP
Functional (but not purely functional)
Tuesday, March 19, 13
22. Homoiconic
Lisp code is just lisp data
Revenge of the Nerds - Paul Graham
Macros == black magic
Reprogramming the language with the language
Tuesday, March 19, 13
23. Modern
Fewer parentheses
Sequences
Reader macros for data structures
regex, map, set, vector, metadata
First-class data structures: [], {}, ‘()
Less Lipsy than other Lisps (ex: fns use [ ])
Commas are whitespace
Tuesday, March 19, 13
27. Clojure’s approach to Functional Programming
Not purely functional
Has system for working with refs, agents, atoms,
and dynamic binding
Dynamic typing
Java invocation is NOT functional
Tuesday, March 19, 13
28. “Mutable objects are the new spaghetti code.”
- Rich Hickey
Tuesday, March 19, 13
30. You can’t stop the world
http://clojure.org/concurrent_programming
http://blip.tv/clojure/clojure-concurrency-819147
http://www.infoq.com/presentations/Value-Values
Tuesday, March 19, 13
31. Concurrency is difficult
Persistent data structures
Programming with values
Immutable by default
Syntax for state:
Refs (STM), Agents, Atoms, Dynamic Vars
We’ll a bit about state later.
Tuesday, March 19, 13
33. Let’s get started
there are a few options
Emacs, Leiningen, nrepl
Emacs Live
LightTable
Tuesday, March 19, 13
34. Leiningen
Clojure automation...
https://github.com/technomancy/leiningen
1) Download the script:
https://raw.github.com/technomancy/leiningen/stable/bin/lein
2) Place it on your $PATH. (I like to use ~/bin)
3) Set it to be executable. (chmod 755 ~/bin/lein)
Start a new project:
$ lein new <project name>
$ lein repl
Tuesday, March 19, 13
35. Emacs
Get version >= 24.X
Pick one (or more):
1) http://emacsformacosx.com
2) $ brew install emacs
3) https://github.com/overtone/emacs-live
Tuesday, March 19, 13
36. Emacs https://gist.github.com/mudphone/4698169
= install lein 2
- Do this: https://github.com/technomancy/leiningen
- download lein script, it will boot strap itself
- install on path, rename to lein, chmod 755
- run lein --version
Leiningen 2.0.0 on Java 1.6.0_37 Java HotSpot(TM) 64-Bit Server VM
= Emacs >= 24
- Download latest version: http://emacsformacosx.com, if you want the GUI version
- Or, for CLI, use homebrew
- Or, do both
= Emacs Starter Kit
- look at my init.el
- install latest: https://github.com/technomancy/emacs-starter-kit
- install clojure-mode
package-install <ret> clojure-mode <ret>
- install nrepl
" package-install <ret> nrepl <ret>
= nrepl (already installed)
- docs here: https://github.com/kingtim/nrepl.el
- at a minimum, check out the keybindings
= nrepl auto-complete
- https://github.com/purcell/ac-nrepl
(popup docs keybinding didn't work for me, so I'm not using it right now)
= REFS:
- this isn't required reading, but it works:
http://www.kedrovsky.com/blog/clojure-emacs-nrepl-and-leiningen
Tuesday, March 19, 13
37. Emacs
nREPL
M-x nrepl-jack-in
C-c M-n (to switch repl to this ns)
C-x C-c (eval buffer in repl)
M-C-x (eval form under point in repl)
C-c C-z (switch to repl buffer)
Tuesday, March 19, 13
39. Getting Started
Symbols
Vars
Namespaces
use == require + refer
Help w/ doc, find-doc, source
Tuesday, March 19, 13
40. Forms
Prefix notation
false and nil evaluate to false
maps are functions of keys, and keys are functions of maps
defrecord
Tuesday, March 19, 13
41. defrecord
user> (defrecord Person [fname lname age favorites])
user.Person
user> (defrecord Favorites [movie band])
user.Favorites
user> (def me (Person. "Kyle" "Oba" 37
(Favorites. "Lost in Translation" "Pantera")))
#'user/me
user> (:fname me)
"Kyle"
user> (-> me :favorites :movie)
"Lost in Translation"
user> (assoc me :fname "That guy")
#user.Person{:fname "That guy", :lname "Oba", :age 37,
:favorites #user.Favorites{:movie "Lost in Translation", :band "Pantera"}}
user> (update-in me [:favorites :band] #(str "ZZ" % "ZZ"))
#user.Person{:fname "Kyle", :lname "Oba", :age 37,
:favorites #user.Favorites{:movie "Lost in Translation", :band "ZZPanteraZZ"}}
Tuesday, March 19, 13
42. http://clojure.org/state
State http://clojure.org/concurrent_programming
http://clojure.org/vars
http://clojure.org/atoms
http://clojure.org/agents
http://clojure.org/refs
Tuesday, March 19, 13
43. Imperative programming
single-threaded premise
world is stopped
requires mutexes and locks
difficult to get right / extremely complicated
Tuesday, March 19, 13
44. Identity and Value
identity name fav foods today
{cheese
March18th
value Kyle donuts
2013
bacon}
Tuesday, March 19, 13
45. Typical OO
has imperative programming baked into it
identities are conflated with values
not necessarily, but usually, due to the language
Tuesday, March 19, 13
46. Clojure’s approach
separates state from identity
move away from state as “the content of this memory block”
toward “the value currently associated with this identity”
identity is in different states at different times
but, the state at a point in time never changes value
Tuesday, March 19, 13
47. Clojure’s approach
vars - root binding, refers to mutable storage location
vars w/ dynamic binding - per-thread binding
atoms
agents, asynchronous change via a function and values
refs, coordinate change with STM: software transactional memory
Tuesday, March 19, 13
48. Actor Model (not Clojure)
distributed
more complex
see Erlang
Tuesday, March 19, 13
49. Working with State
atoms
refs
dynamic binding
acting at a distance / aspect oriented programming
loss of purity
memoize example
Tuesday, March 19, 13
51. vars allow reference to mutable storage locations
user> (def x 1)
#'user/x
user> x
1
user> (def y 1)
#'user/y
user> (+ x y)
2
Tuesday, March 19, 13
52. dynamic binding
user> (def ^:dynamic x 1)
#'user/x
user> (def ^:dynamic y 1)
#'user/y
user> (+ x y)
2
user> (binding [x 2 y 3] (+ x y))
5
user> (+ x y)
2
Tuesday, March 19, 13
53. Changing State async synchronous
coordinated ref
independent agent atom
Tuesday, March 19, 13
54. atoms shared, synchronous, independent state
user> (def z (atom 5)) user> (swap! z inc)
#'user/z 6
user> z user> @z
#<Atom@f2882ad: 5> 6
user> @z user> (swap! z (constantly 10))
5 10
user> @z
10
Tuesday, March 19, 13
55. agents independent, asynchronous change
user> (def a (agent 1)) user> (send a
#'user/a #(do (Thread/sleep 5000)
(+ % 1)))
user> a #<Agent@474d75ae: 2>
#<Agent@474d75ae: 1>
user> @a
2
user> @a
3
user> (send a inc)
#<Agent@474d75ae: 2>
user> a
#<Agent@474d75ae: 2>
user> @a
2
Tuesday, March 19, 13
56. refs (transactional references)
shared use of mutable storage via STM
user> (def r (ref [1 2 3])) (dosync (alter r #(conj % 4)))
#'user/r [1 2 3 4]
user> r user> @r
#<Ref@369ca84f: [1 2 3]> [1 2 3 4]
user> @r
[1 2 3]
user> (dosync (alter r (partial cons 0)))
(0 1 2 3 4)
user> @r
(0 1 2 3 4)
Tuesday, March 19, 13
64. What’s a Macro?
#!/usr/bin/env ruby -w $ ruby ruby_macro.rb | sh
say_hello = "puts "hello"" hello
hello
ruby_cmd = (0..9).map do |n| hello
say_hello hello
end hello
hello
puts "ruby -e '#{ruby_cmd.join(";")}'" hello
hello
hello
hello
What if we could do this, while staying in the language?
Tuesday, March 19, 13
65. Ruby-like “unless” implementation as a function...
(defn unless [expr form]
(if expr nil form))
user> (unless (= 1 1) (println "hi"))
hi
nil
This won’t work, because arguments are evaluated.
Tuesday, March 19, 13
67. What we want
(unless expr form) -> (if expr nil form)
Tuesday, March 19, 13
68. What we want
(defmacro unless [expr form]
(list 'if expr nil form))
user> (unless true (println "yep"))
nil
user> (unless false (println "yep"))
yep
nil
Tuesday, March 19, 13
69. macroexpand-1
user> (macroexpand-1 '(when-not true (println "hi")))
(if true nil (do (println "hi")))
user> (source when-not)
(defmacro when-not
"Evaluates test. If logical false, evaluates body in an implicit do."
{:added "1.0"}
[test & body]
(list 'if test nil (cons 'do body)))
nil
Tuesday, March 19, 13
70. WAT
text
data
reader evaluation macros
structures
compile
bytecode
Tuesday, March 19, 13
71. macroexpand
user> (.. System (getProperties) (get "os.name"))
"Mac OS X"
user> (macroexpand-1 '(.. System (getProperties) (get "os.name")))
(.. (. System (getProperties)) (get "os.name"))
user> (macroexpand '(.. System (getProperties) (get "os.name")))
(. (. System (getProperties)) (get "os.name"))
Tuesday, March 19, 13
72. Java Interop Bonus
user> (source ..)
(defmacro ..
"form => fieldName-symbol or (instanceMethodName-symbol args*)
Expands into a member access (.) of the first member on the first
argument, followed by the next member on the result, etc. For
instance:
(.. System (getProperties) (get "os.name"))
expands to:
(. (. System (getProperties)) (get "os.name"))
but is easier to write, read, and understand."
{:added "1.0"}
([x form] `(. ~x ~form))
([x form & more] `(.. (. ~x ~form) ~@more)))
nil
Tuesday, March 19, 13
73. One more macro...
user> (source and)
(defmacro and
"Evaluates exprs one at a time, from left to right. If a form
returns logical false (nil or false), and returns that value and
doesn't evaluate any of the other expressions, otherwise it returns
the value of the last expr. (and) returns true."
{:added "1.0"}
([] true)
([x] x)
([x & next]
`(let [and# ~x]
(if and# (and ~@next) and#))))
nil
Tuesday, March 19, 13
75. Areas of Interest:
ClojureScript to any old website
http://blip.tv/clojure/rich-hickey-unveils-clojurescript-5399498
ClojureScript + Three.js, to get at the WebGL bits
ClojureScript embedded in game systems
ClojureScript CLI
Pedestal: http://pedestal.io/documentation/hello-world-service/
It runs on Node.js.
Tuesday, March 19, 13
82. (require ‘[clojure.test :as test])
user> (doc test/run-all-tests)
-------------------------
clojure.test/run-all-tests
([] [re])
Runs all tests in all namespaces; prints results.
Optional argument is a regular expression; only namespaces with
names matching the regular expression (with re-matches) will be
tested.
nil
user> (doc test/run-tests)
-------------------------
clojure.test/run-tests
([] [& namespaces])
Runs all tests in the given namespaces; prints results.
Defaults to current namespace if none given. Returns a map
summarizing test results.
nil
Tuesday, March 19, 13