Clojure basics

1,346 views

Published on

0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,346
On SlideShare
0
From Embeds
0
Number of Embeds
7
Actions
Shares
0
Downloads
17
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Clojure basics

  1. 1. CLOJURE BASICS Kyle Oba koba@pasdechocolat.com @mudphoneTuesday, March 19, 13
  2. 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. 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 GrahamTuesday, March 19, 13
  4. 4. “When you choose technology, you have to ignore what other people are doing, and consider only what will work the best.” Paul GrahamTuesday, March 19, 13
  5. 5. “In a big company, you can do what all the other big companies are doing. But a startup cant do what all the other startups do. I dont think a lot of people realize this, even in startups.” Paul GrahamTuesday, March 19, 13
  6. 6. “So if youre running a startup, you had better be doing something odd. If not, youre in trouble.” Paul GrahamTuesday, March 19, 13
  7. 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 doesnt use it is that programming languages are not merely technologies, but habits of mind as well, and nothing changes slower.” Paul GrahamTuesday, March 19, 13
  8. 8. “Ill begin with a shockingly controversial statement: programming languages vary in power.” Paul GrahamTuesday, March 19, 13
  9. 9. http://pragprog.com/book/shcloj2/programming-clojure http://clojure.orgTuesday, March 19, 13
  10. 10. Tuesday, March 19, 13
  11. 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:15Tuesday, March 19, 13
  12. 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 idiomaticTuesday, March 19, 13
  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/OpenNITuesday, March 19, 13
  14. 14. Why is Clojure necessary?Tuesday, March 19, 13
  15. 15. Multi-Core FutureTuesday, March 19, 13
  16. 16. Why use the JVM? The JVM is advanced technology. Java libraries... lots of them. Widely deployedTuesday, March 19, 13
  17. 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
  18. 18. JVM For example: https://github.com/mmcgrana/clj-redis (ns clj-redis.client (:import java.net.URI) (:import (redis.clients.jedis Jedis JedisPool JedisPoolConfig JedisPubSub)) (:require [clojure.string :as str]) (:refer-clojure :exclude [get set keys type])) (def ^{:private true} local-url "redis://127.0.0.1:6379") (defn init ([] (init {})) ([{:keys [url timeout test-on-borrow] :as opts}] (let [uri (URI. (or url local-url)) tout (or timeout 2000) host (.getHost uri) port (if (pos? (.getPort uri)) (.getPort uri) 6379) uinfo (.getUserInfo uri) pass (and uinfo (last (str/split uinfo #":"))) config (JedisPoolConfig.)] (when test-on-borrow (.setTestOnBorrow config test-on-borrow)) (JedisPool. config host port tout pass))) ([k1 v1 & {:as opts}] (init (assoc opts k1 v1))))Tuesday, March 19, 13
  19. 19. JVM For example: https://github.com/mmcgrana/clj-redis (defn lease [^JedisPool p f] (let [j (.getResource p)] (try (f j) (finally (.returnResource p j))))) (defn ping [p] (lease p (fn [^Jedis j] (.ping j)))) (defn get [p ^String k] (lease p (fn [^Jedis j] (.get j k)))) (defn set [p ^String k ^String v] (lease p (fn [^Jedis j] (.set j k v))))Tuesday, March 19, 13
  20. 20. http://www.paulgraham.com/avg.html Why is Clojure a secret weapon?Tuesday, March 19, 13
  21. 21. Why is Clojure so cool? Homoiconic Modern LISP Functional (but not purely functional)Tuesday, March 19, 13
  22. 22. Homoiconic Lisp code is just lisp data Revenge of the Nerds - Paul Graham Macros == black magic Reprogramming the language with the languageTuesday, March 19, 13
  23. 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 whitespaceTuesday, March 19, 13
  24. 24. List Comprehension user> (take 40 (for [x (range) :when (and (> (* 2 x) 99) (= 0 (mod x 3)))] (* 2 x))) (102 108 114 120 126 132 138 144 150 156 162 168 174 180 186 192 198 204 210 216 222 228 234 240 246 252 258 264 270 276 282 288 294 300 306 312 318 324 330 336)Tuesday, March 19, 13
  25. 25. Functional Functions are first-class Data is immutable Functions are NOT pureTuesday, March 19, 13
  26. 26. Why Functional? Simple Thread-safe Parallelizable GenericTuesday, March 19, 13
  27. 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 functionalTuesday, March 19, 13
  28. 28. “Mutable objects are the new spaghetti code.” - Rich HickeyTuesday, March 19, 13
  29. 29. Concurrency is difficultTuesday, March 19, 13
  30. 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-ValuesTuesday, March 19, 13
  31. 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
  32. 32. Enough chit chat.Tuesday, March 19, 13
  33. 33. Let’s get started there are a few options Emacs, Leiningen, nrepl Emacs Live LightTableTuesday, March 19, 13
  34. 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 replTuesday, March 19, 13
  35. 35. Emacs Get version >= 24.X Pick one (or more): 1) http://emacsformacosx.com 2) $ brew install emacs 3) https://github.com/overtone/emacs-liveTuesday, March 19, 13
  36. 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 didnt work for me, so Im not using it right now)   = REFS: - this isnt required reading, but it works: http://www.kedrovsky.com/blog/clojure-emacs-nrepl-and-leiningenTuesday, March 19, 13
  37. 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
  38. 38. Clojure BasicsTuesday, March 19, 13
  39. 39. Getting Started Symbols Vars Namespaces use == require + refer Help w/ doc, find-doc, sourceTuesday, March 19, 13
  40. 40. Forms Prefix notation false and nil evaluate to false maps are functions of keys, and keys are functions of maps defrecordTuesday, March 19, 13
  41. 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. 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/refsTuesday, March 19, 13
  43. 43. Imperative programming single-threaded premise world is stopped requires mutexes and locks difficult to get right / extremely complicatedTuesday, March 19, 13
  44. 44. Identity and Value identity name fav foods today {cheese March18th value Kyle donuts 2013 bacon}Tuesday, March 19, 13
  45. 45. Typical OO has imperative programming baked into it identities are conflated with values not necessarily, but usually, due to the languageTuesday, March 19, 13
  46. 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 valueTuesday, March 19, 13
  47. 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 memoryTuesday, March 19, 13
  48. 48. Actor Model (not Clojure) distributed more complex see ErlangTuesday, March 19, 13
  49. 49. Working with State atoms refs dynamic binding acting at a distance / aspect oriented programming loss of purity memoize exampleTuesday, March 19, 13
  50. 50. Working with StateTuesday, March 19, 13
  51. 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) 2Tuesday, March 19, 13
  52. 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) 2Tuesday, March 19, 13
  53. 53. Changing State async synchronous coordinated ref independent agent atomTuesday, March 19, 13
  54. 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 10Tuesday, March 19, 13
  55. 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 2Tuesday, March 19, 13
  56. 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
  57. 57. Java InteropTuesday, March 19, 13
  58. 58. Working with Java Calling Java Java interop Java collectionsTuesday, March 19, 13
  59. 59. Java: Examples user> (System/getProperty "java.vm.version") "20.14-b01-447" user> (.toUpperCase "fred") "FRED" user> (.getName String) "java.lang.String" user> Math/PI 3.141592653589793Tuesday, March 19, 13
  60. 60. Java: import and . user> (import [java.util Random]) java.util.Random user> (def r (Random.)) #user/r user> (.nextInt r) 458809484 user> (.nextInt r 100) 42Tuesday, March 19, 13
  61. 61. Java: doto user> (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2)) #<HashMap {b=2, a=1}>Tuesday, March 19, 13
  62. 62. Java: set! user> (def p (java.awt.Point.)) #user/p user> (.x p) 0 user> (set! (. p x) 5) 5 user> p #<Point java.awt.Point[x=5,y=0]>Tuesday, March 19, 13
  63. 63. MacrosTuesday, March 19, 13
  64. 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. 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
  66. 66. Arguments are evaluated first (defn unless [expr form] (println "Doing unless...") (if expr nil form)) user> (unless (= 1 1) (println "hi")) hi Doing unless... nilTuesday, March 19, 13
  67. 67. What we want (unless expr form) -> (if expr nil form)Tuesday, March 19, 13
  68. 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 nilTuesday, March 19, 13
  69. 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))) nilTuesday, March 19, 13
  70. 70. WAT text data reader evaluation macros structures compile bytecodeTuesday, March 19, 13
  71. 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. 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))) nilTuesday, March 19, 13
  73. 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 doesnt 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#)))) nilTuesday, March 19, 13
  74. 74. “Webby Stuff”Tuesday, March 19, 13
  75. 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
  76. 76. ClojureScript $ lein cljsbuild once Compiling ClojureScript. $ tree Compiling "resources/public/hello.js" from "src/cljs"... . ├── README.md Successfully compiled "resources/public/hello.js" in 8.542318 seconds. ├── doc │   └── intro.md ├── docroot │   └── index.html ├── project.clj ├── resources │   └── public │   ├── hello.js │   └── index.html ├── src │   ├── clj │   │   └── hello_world │   │   └── core.clj │   └── cljs │   └── hello.cljs └── test └── hello_world └── core_test.clj 10 directories, 9 filesTuesday, March 19, 13
  77. 77. Pedestal http://pedestal.io/documentation/hello-world-service/ $ tree $ lein repl . ├── README.md nREPL server started on port 63132 ├── config ... │   └── logback.xml helloworld.server=> (use dev) ├── dev nil │   ├── dev.clj helloworld.server=> (start) │   └── user.clj ├── logs nil │   └── helloworld-2013-03-18.0.log helloworld.server=> (stop) ├── project.clj nil ├── src helloworld.server=> Bye for now! │   └── helloworld │   ├── server.clj │   └── service.clj ├── target │   ├── classes │   └── stale │   └── extract-native.dependencies └── test └── helloworld └── service_test.clj 10 directories, 10 filesTuesday, March 19, 13
  78. 78. Pedestal http://pedestal.io/documentation/application-introduction/ (ns hello-world (:require [io.pedestal.app.protocols :as p] (defn count-model [old-state message] [io.pedestal.app :as app] (condp = (msg/type message) [io.pedestal.app.messages :as msg] msg/init (:value message) [io.pedestal.app.render :as render] [domina :as dom])) :inc (inc old-state))) (defmulti render (fn [& args] (first args))) (defmethod render :default [_] nil) (defmethod render :value [_ _ old-value new-value] (dom/destroy-children! (dom/by-id "content")) (dom/append! (dom/by-id "content") (str "<h1>" new-value " Hello Worlds</h1>"))) (defn render-fn [deltas input-queue] (doseq [d deltas] (apply render d))) (def count-app {:models {:count {:init 0 :fn count-model}}}) (defn receive-input [input-queue] (p/put-message input-queue {msg/topic :count msg/type :inc}) (.setTimeout js/window #(receive-input input-queue) 3000)) (defn ^:export main [] (let [app (app/build count-app)] (render/consume-app-model app render-fn) (receive-input (:input app)) (app/begin app)))Tuesday, March 19, 13
  79. 79. Pedestal http://pedestal.io/documentation/application-introduction/(ns hello-world (defn count-model [old-state message] (:require [io.pedestal.app.protocols :as p] [io.pedestal.app :as app] (condp = (msg/type message) [io.pedestal.app.messages :as msg] msg/init (:value message) [io.pedestal.app.render :as render] :inc (inc old-state))) [io.pedestal.app.render.push :as push] [domina :as dom])) (defn render-value [renderer [_ _ old-value new-value] input-queue] (dom/destroy-children! (dom/by-id "content")) (dom/append! (dom/by-id "content") (str "<h1>" new-value " Hello Worlds</h1>"))) (def count-app {:models {:count {:init 0 :fn count-model}}}) (defn receive-input [input-queue] (p/put-message input-queue {msg/topic :count msg/type :inc}) (.setTimeout js/window #(receive-input input-queue) 3000)) (defn ^:export main [] (let [app (app/build count-app) render-fn (push/renderer "content" [[:value [:*] render-value]])] (render/consume-app-model app render-fn) (receive-input (:input app)) (app/begin app)))Tuesday, March 19, 13
  80. 80. TestingTuesday, March 19, 13
  81. 81. Free! (ns oudl.core-test user> (require [clojure.test :as test]) (:use clojure.test nil oudl.core)) user> (test/run-all-tests #"oudl.*-test") (deftest a-test (testing "FIXME, I fail." Testing oudl.core-test (is (= 0 1)))) FAIL in (a-test) (core_test.clj:7) FIXME, I fail. expected: (= 0 1) actual: (not (= 0 1)) Ran 1 tests containing 1 assertions. 1 failures, 0 errors. {:type :summary, :pass 0, :test 1, :error 0, :fail 1}Tuesday, March 19, 13
  82. 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. nilTuesday, March 19, 13
  83. 83. Things I LikeTuesday, March 19, 13
  84. 84. Some things Clojure + Processing = Quil Clojure + Kinect/OpenNI = Bifocals LightTableTuesday, March 19, 13
  85. 85. Tuesday, March 19, 13

×