Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clojure(script) and Functional Programming

αλεx π
February 21, 2013

Clojure(script) and Functional Programming

Clojure(script)
and Functional Programming
(for anyone who hasn’t done it before)

αλεx π

February 21, 2013
Tweet

More Decks by αλεx π

Other Decks in Programming

Transcript

  1. • imma talk on Functional Programming rather than ClojureScript •

    if you don’t buy FP, you won’t benefit from CS • if you buy FP, you can benefit even without using CS • from backend development experience • more relevant for parallel systems Setting expectations Thursday, February 21, 13
  2. • imma talk on Functional Programming rather than ClojureScript •

    if you don’t buy FP, you won’t benefit from CS • if you buy FP, you can benefit even without using CS • from backend development experience • more relevant for parallel systems WORA Thursday, February 21, 13
  3. Client Side development is simple(r) (depending on what you do

    YMMV, but most of time you don’t push thousands of messages or process TBs of data through browser) Thursday, February 21, 13
  4. • DOM Manipulation • Reacting upon events • Rendering •

    Request/response • Adding interactivity Client Side stuff (simplified) Thursday, February 21, 13
  5. • Streaming • Crunching (lots of) data • Persisting data

    • Validating user input • Doing stuff in parallel Interesting Problems arising on Server Side Thursday, February 21, 13
  6. var BaseMiddleware = Object.extend({ init: function(app){ this.app = app; },

    process: function(env){ // do pre-processing ... this.app(env); // or skip // do post-processing } }); Thursday, February 21, 13
  7. var CookieMiddleware = BaseMiddleware.extend({ process: function(env){ // do cookie-related things,

    prepare // for handler this.app(env); // or skip // do more cookie-related stuff, // prepare for response } }); Thursday, February 21, 13
  8. var handler = function (env) { // Do something with

    env }; function wrapCookies(fn) { var f = function (env) { // do cookie-related things, // prepare for handler fn(env); // do more cookie-related stuff, // prepare for response } return f; } // Run! wrapCookies(handler)(env); Thursday, February 21, 13
  9. (defn handler [env] ;; do something ) (defn wrap-cookies [f]

    (fn [request] ;; do cookie-related things, ;; prepare for handler (f env) ;; do more cookie-related stuff, ;; prepare for response ) Thursday, February 21, 13
  10. • less code • separation of concerns (no coupling of

    verbs with nouns) • => no objects that could potentially store state • => no side effects • easy to test in isolation • easy to compose and reuse • easy to reproduce flows What does it give you? Thursday, February 21, 13
  11. var counter = 1; // Somewhere in a galaxy far,

    far away // ... someobj.doSomething(obj, {callback: function () { // debug it, suckers counter++; }}); Thursday, February 21, 13
  12. var a = {}; function changeIt (b) { b["wat"] =

    "yo" } changeIt(a) a // Object {wat: "yo"} Thursday, February 21, 13
  13. • closures allow handler/callback function to mess up your scope

    • they work well only when enclosed values don't change • mutable objects lead to unpredictable behavior (state sucks) • when you set something, second later you're not sure anymore if it's still set What does it give you? Thursday, February 21, 13
  14. var books = [ {title: "hitchhiker's guide to the galaxy",

    author: {name: "Douglas Adams", birth_year: 1952} }, {title: "Ender's game", author: {name: "Orson Scott Card", birth_year: 1951} }]; _.each(books, function(book) { book.author.age = 2013 - book.author.birth_year; }); Thursday, February 21, 13
  15. (defprotocol Age (age [o])) (extend-protocol Age Person (age [o] ;;

    impl... ) Tree (age [o] ;; impl... )) Thursday, February 21, 13
  16. • coupling data with actions is (usually) not side-effect free

    • decoupling data from functions doesn’t mean you can’t dispatch on type anymore • even if your data is decoupled from action, doesn’t guarantee it doesn’t change Thursday, February 21, 13
  17. • in 99% of cases, coupled with Model (mixins do

    not change fact of coupling) • change internal state • implementer has to remember to clear errors on reload or state change • potentially destructive against the Model • code reuse would require implementing abstraction on higher level (base class or helper) Thursday, February 21, 13
  18. • functional way is simpler • does not affect validated

    object (immutability) • trivial to test • always yields same result given same input (can’t give same guarantee with shared state) • reusable/composable right away • always operating primitives, easy to understand Thursday, February 21, 13
  19. // Pseudo-code, no real framework kept in mind :P class

    OrderController before_filter signed_in_only, fetch_order signed_in_only: ()-> ... fetch_order: ()-> @order = Database.get_me_order(params.id) ... update: ()-> @order ... Thursday, February 21, 13
  20. • try to test dat • oh right, I’ll just

    take a framework that will test it all for me • can you rely on the presence of @order? • or on its consistency? • explicit vs implicit function calls • testing chained calls that rely on state is extremely hard • even implementing can be tricky Thursday, February 21, 13
  21. • functional approach: explicit function calls, threading • test in

    isolation • no shared/mutable state • predictable results • obvious outcome • local bindings for shared state • refs/atoms for transactional mutable state • if STM doesn’t work well for your use-case (too many interleaving state) changes, use message passing Thursday, February 21, 13
  22. • use lazy sequences where possible • in essence, lazy

    sequence is • already calculated part • function to yield next part • controlling flow, avoiding fetching/calculation when you got enough data • avoid needless calculations • (potentially) infinite data structures • (potentially) smaller memory footprint • use chunks to win performance, when you need to prefetch more than just next step Thursday, February 21, 13
  23. • break down your code to smaller steps • understand

    and isolate changes to data structure • when changes are isolated, compose them with mapper/ reducer/merger or any other mean Thursday, February 21, 13
  24. • Changing state doesn't discard old value • You have

    5EUR at 13:55 today • You got 10EUR from a friend at 14:00 • Doesn't change the fact you had 5EUR at 13:55 Thursday, February 21, 13
  25. (def a ‘(1 2 3)) (def b (cons 4 a))

    3 a 2 1 b 4 Thursday, February 21, 13
  26. (def a ‘(1 2 3)) (def b (cons 4 a))

    (def c (cons 5 (rest a)) 3 a 2 1 b 4 c 5 Thursday, February 21, 13
  27. • doesn’t have anything to do with disk persistence •

    efficient creation of “modified” versions • structural sharing • inherently thread & iteration-safe will not mutate in a middle • immutable modification yields new coll • composite • No need for defensive copy • Referential Transparency Thursday, February 21, 13
  28. (reduce + arr) ;; + is a function, passed to

    reduce (map inc [1 2 3]) ;; => (2 3 4) Thursday, February 21, 13
  29. (def people [{:name "Alex" :age 26 :occupation :hacker} {:name "Nick"

    :age 19 :occupation :student} {:name "Ron" :age 27 :occupation :doctor}]) (def youngster? #(< (:age %) 25)) (def hacker? #(= :hacker (:occupation %))) (defn filter-people [f people] (filter f people)) (filter-people youngster? people) ;; => ({:age 19, :name "Nick"}) (filter-people (complement youngster?) people) ;; => ({:age 26, :name "Alex"} {:age 27, :name "Ron"}) Thursday, February 21, 13
  30. (def people [{:name "Alex" :age 26 :occupation :hacker} {:name "Nick"

    :age 19 :occupation :student} {:name "Ron" :age 27 :occupation :doctor}]) (defn filter-people [coll & predicates] (filter #(every? true? ((apply juxt predicates) %)) coll)) ;; ((juxt a b c) x) => [(a x) (b x) (c x)] (filter-people people hacker? (complement youngster?)) Thursday, February 21, 13
  31. • simplifies code • makes interface easier to understand •

    (very) reusable building blocks • smaller pieces are easier to test, write and understand • fns may take fns as arguments Thursday, February 21, 13
  32. ;; Simplest macro? (defmacro call [form x] (list form x))

    (call inc 1) ;; => 2 Thursday, February 21, 13
  33. ;; From Clojure.core, bit shortened (defmacro -> ([x] x) ([x

    form] (if (seq? form) `(~(first form) ~x ~@(next form)) (list form x))) ([x form & more] `(-> (-> ~x ~form) ~@more))) (-> 1 inc inc dec) ;; => 2 Thursday, February 21, 13
  34. (defmacro when [test & body] (list 'if test (cons 'do

    body))) (when (too-cool?) (give-me-more!) (and-more!)) Thursday, February 21, 13
  35. (defmacro time [expr] `(let [start# (. System (nanoTime)) ret# ~expr]

    (prn (str "Elapsed time: " (- (. System (nanoTime)) start#)) " msecs")) ret#)) (time (* 1 10000000)) ;; "Elapsed time: 0.021 msecs" ;; => 10000000 Thursday, February 21, 13
  36. (def arr ["first" "second" "third"]) (let [[first second third] arr]

    (println first) ;; "first" (println second) ;; "second" (println third) ;; "third" ) Thursday, February 21, 13
  37. (def m {:first 1 :second 2 :third 3}) (let [{:keys

    [first second third] :as all} m] (println first) ;; 1 (println second) ;; 2 (println third) ;; 3 (println all) ;; {:first 1 :second 2 :third 3} ) Thursday, February 21, 13
  38. (map (fn [[k v]] (println k “=” v)) {:first 1

    :second 2 :third 3}) ;; :first = 1 ;; :second = 2 ;; :third = 3} Thursday, February 21, 13