KEMBAR78
Functional programming with clojure | PDF
Functional Programming
with
Lucy Fang
@crushoncode
Imperative
OOP
Declarative
Functional
Programming paradigms
A comparison...
Object-oriented Functional
The HOW
How to perform tasks and how to track changes
in state
The WHAT
What information is desired and what
transformations are required
State changes are important Immutable state and data structures
Loops, conditionals, and method calls
Order of execution is important.
Function calls and managing loops through
recursion.
Instances of structures or classes Functions as first-class objects and data
collections.
What is Clojure?
● Dialect of the Lisp programming language
(+ 1 2)
● Hosted language running on the JVM (java virtual machine)
● Interoperable with the host language
● Written like a data structure
Basic Clojure Syntax
Define a value or data e.g. (def name “value”)
Define a function e.g. (defn functionName [argument] (behavior))
Binding a value e.g. (let [value])
Collections:
(list 1 2 3) => ‘(1 2 3)
(vec ‘(1 2 3) => [1 2 3] //similar to an array, zero indexed
(set ‘(1 2 1) => #{1 2} // set of unique values
(map + [1 2 3] [4 5 6]) => (5 7 9)
First-class functions
● Treats functions as values so you can assign it to a variable, pass it to other
functions, store it in data structures e.g arrays, lists
● Can be used in higher-order functions - takes functions as arguments or
returns a function as a result
● Constructed at runtime
Why is this useful?
● When your inputs are yet to be provided
● reduce unnecessary code
● Increase reusability - keep it DRY
First-class functions and higher order functions
(first class function)
=> (def double-it (number)
(fn [number] (* 2 number))
(higher-order function)
=> (map double-it [1 2 3])
=> (2 4 6)
(def double-it (partial * 2))
same as
(fn [number] (* 2 number)) or
#(* 2 %)
Function Composition
Takes a set of functions and returns a function that is the composition of those
functions.
(filter (comp not zero?) [0 1 0 2 0 3 0 4])
=> (1 2 3 4)
Pure function?
(def age 20)
(defn whats-my-age [name]
(str name “: ” age))
=> (whats-my-age “Prashant”)
=> “Prashant: 20”
Impure Pure
(def age 20)
(defn my-age [name]
(str name “:” age))
=> (my-age “Prashant”)
=> “Prashant:20”
Create another age...
(defn my-age [name]
(let [age 20]
(str name “:” age)))
=> (my-age “Prashant”)
=> “Prashant:20”
Libraries
(:import java.util.Date)
(defn race-complete [racer-name]
(str race-name “ finished on "(java.util.Date.))
=> (race-complete “Prashant")
=> "Prashant finished on Sun Oct 01 10:03:59 BST 2017"
=> (race-complete “Prashant")
=> "Prashant finished on Sun Oct 01 10:04:48 BST 2017"
Almost a lottery scam!
(defn scratchie-lottery []
(if (= (rand) 3)
"Congrats! You’re a millionaire!"
"Hahaha your money is mine!"))
=> (scratchie-lottery)
=> "Hahaha your money is mine!"
Pure doesn’t always mean Idempotent
A pure function
- calling f(x) will give the same result no matter how many times you call
it. E.g. (defn add-1 [n] (+ 1 n)
An idempotent function
f(f(x)) is the same as f(x).
E.g.
(clojure.string/upper-case (clojure.string/upper-case “hello”)) => “HELLO”
(float (float 2))
Why are pure functions useful?
● when given the same input, will always return the same output and does not
have any observable side effect - you have CERTAINTY!
● makes testing much easier - we have less set up as we simply give it the
input and assert on the output e.g. no need for a payment gateway
● they can be run anywhere, when do you find you are copying the code over?
A value is something that doesn’t change.
20 is 20.
1 January 2017 is just that.
$5 doesn’t become $10.
My favorite fruits are bananas and cherries
(hmmmm….but not together).
What if my favorite fruit is now figs?
Well it will be a different set.
An identity with a state whose value is a value
at a point in time.
Immutability - Persistent data structures
(list 1 2 3) => ‘(1 2 3)
(vec ‘(1 2 3) => [1 2 3] //similar to an array, zero indexed
(set ‘(1 2 1) => #{1 2} // set of unique values
(map + [1 2 3] [4 5 6]) => (5 7 9)
Memory is shared
So what if we do need to change state?
● Memory state can be shared readily between threads which makes
concurrency easier
● Software transactional memory (STM) supports sharing of changing state
between threads in a synchronous and coordinated manner.
● Vars are for when you need to store something on a per-thread basis. If you
have a multi-threaded program and each thread needs its own private state,
put that state in a var.
Coordinated Uncoordinated
synchronous Ref (STM) atoms
asynchronous - agents
Recursion vs tail recursion
Traditional recursive
(defn factorial [n]
(if (zero? n)
1
(* n (factorial (dec n)))))
You don’t get the result of your
calculation until you have returned from
every recursive call. Generates a new
stackframe for each call.
Tail recursion
(defn factorial [n]
(loop [count n
result 1]
(if (pos? count)
(recur (dec count) (* result count))
result )))
Processing occurs before recursive call.
Reuse existing stackframes.
Compare on the call stack
Traditional recursive Tail recursion
(factorial 5) : 5 * _
(factorial 4) : 4 * _
(factorial 3) : 3 * _
(factorial 2) : 2 * _
(factorial 1) : 1 * _
(factorial 0) : 1
(factorial 5)
1
1
2
6
24
(factorial 5) = 120 (factorial 5) =
(loop 5 0)(loop 4 5)(loop 3 20)(loop 2 60)(loop 1 120)
120
count
n
Lazy evaluation
● Only return a value when necessary “call-by-need”
● Things are calculated not when they are defined, but when they are actually
needed.
● Maintain precision
● Optimise evaluation
Examples: ratios, lazy sequence such as range and repeat
TDD using midje
● Test driven development - Red/Green/Refactor
● Clean, tested, lean
● Midje - testing framework for clojure
Clojure resources
- Lots of resources
- Clojure documentation
- https://www.braveclojure.com/
- https://clojure.org/community/books
- Clojure koans
- tryclj.com/ online REPL
- https://clojure.org/api/cheatsheet
(def time-to “code!”)

Functional programming with clojure

  • 1.
  • 2.
  • 3.
    A comparison... Object-oriented Functional TheHOW How to perform tasks and how to track changes in state The WHAT What information is desired and what transformations are required State changes are important Immutable state and data structures Loops, conditionals, and method calls Order of execution is important. Function calls and managing loops through recursion. Instances of structures or classes Functions as first-class objects and data collections.
  • 5.
    What is Clojure? ●Dialect of the Lisp programming language (+ 1 2) ● Hosted language running on the JVM (java virtual machine) ● Interoperable with the host language ● Written like a data structure
  • 7.
    Basic Clojure Syntax Definea value or data e.g. (def name “value”) Define a function e.g. (defn functionName [argument] (behavior)) Binding a value e.g. (let [value]) Collections: (list 1 2 3) => ‘(1 2 3) (vec ‘(1 2 3) => [1 2 3] //similar to an array, zero indexed (set ‘(1 2 1) => #{1 2} // set of unique values (map + [1 2 3] [4 5 6]) => (5 7 9)
  • 8.
    First-class functions ● Treatsfunctions as values so you can assign it to a variable, pass it to other functions, store it in data structures e.g arrays, lists ● Can be used in higher-order functions - takes functions as arguments or returns a function as a result ● Constructed at runtime Why is this useful? ● When your inputs are yet to be provided ● reduce unnecessary code ● Increase reusability - keep it DRY
  • 9.
    First-class functions andhigher order functions (first class function) => (def double-it (number) (fn [number] (* 2 number)) (higher-order function) => (map double-it [1 2 3]) => (2 4 6) (def double-it (partial * 2)) same as (fn [number] (* 2 number)) or #(* 2 %)
  • 10.
    Function Composition Takes aset of functions and returns a function that is the composition of those functions. (filter (comp not zero?) [0 1 0 2 0 3 0 4]) => (1 2 3 4)
  • 12.
    Pure function? (def age20) (defn whats-my-age [name] (str name “: ” age)) => (whats-my-age “Prashant”) => “Prashant: 20”
  • 13.
    Impure Pure (def age20) (defn my-age [name] (str name “:” age)) => (my-age “Prashant”) => “Prashant:20” Create another age... (defn my-age [name] (let [age 20] (str name “:” age))) => (my-age “Prashant”) => “Prashant:20”
  • 14.
    Libraries (:import java.util.Date) (defn race-complete[racer-name] (str race-name “ finished on "(java.util.Date.)) => (race-complete “Prashant") => "Prashant finished on Sun Oct 01 10:03:59 BST 2017" => (race-complete “Prashant") => "Prashant finished on Sun Oct 01 10:04:48 BST 2017"
  • 15.
    Almost a lotteryscam! (defn scratchie-lottery [] (if (= (rand) 3) "Congrats! You’re a millionaire!" "Hahaha your money is mine!")) => (scratchie-lottery) => "Hahaha your money is mine!"
  • 16.
    Pure doesn’t alwaysmean Idempotent A pure function - calling f(x) will give the same result no matter how many times you call it. E.g. (defn add-1 [n] (+ 1 n) An idempotent function f(f(x)) is the same as f(x). E.g. (clojure.string/upper-case (clojure.string/upper-case “hello”)) => “HELLO” (float (float 2))
  • 17.
    Why are purefunctions useful? ● when given the same input, will always return the same output and does not have any observable side effect - you have CERTAINTY! ● makes testing much easier - we have less set up as we simply give it the input and assert on the output e.g. no need for a payment gateway ● they can be run anywhere, when do you find you are copying the code over?
  • 18.
    A value issomething that doesn’t change. 20 is 20. 1 January 2017 is just that. $5 doesn’t become $10. My favorite fruits are bananas and cherries (hmmmm….but not together).
  • 19.
    What if myfavorite fruit is now figs? Well it will be a different set. An identity with a state whose value is a value at a point in time.
  • 21.
    Immutability - Persistentdata structures (list 1 2 3) => ‘(1 2 3) (vec ‘(1 2 3) => [1 2 3] //similar to an array, zero indexed (set ‘(1 2 1) => #{1 2} // set of unique values (map + [1 2 3] [4 5 6]) => (5 7 9)
  • 22.
  • 23.
    So what ifwe do need to change state? ● Memory state can be shared readily between threads which makes concurrency easier ● Software transactional memory (STM) supports sharing of changing state between threads in a synchronous and coordinated manner. ● Vars are for when you need to store something on a per-thread basis. If you have a multi-threaded program and each thread needs its own private state, put that state in a var. Coordinated Uncoordinated synchronous Ref (STM) atoms asynchronous - agents
  • 24.
    Recursion vs tailrecursion Traditional recursive (defn factorial [n] (if (zero? n) 1 (* n (factorial (dec n))))) You don’t get the result of your calculation until you have returned from every recursive call. Generates a new stackframe for each call. Tail recursion (defn factorial [n] (loop [count n result 1] (if (pos? count) (recur (dec count) (* result count)) result ))) Processing occurs before recursive call. Reuse existing stackframes.
  • 25.
    Compare on thecall stack Traditional recursive Tail recursion (factorial 5) : 5 * _ (factorial 4) : 4 * _ (factorial 3) : 3 * _ (factorial 2) : 2 * _ (factorial 1) : 1 * _ (factorial 0) : 1 (factorial 5) 1 1 2 6 24 (factorial 5) = 120 (factorial 5) = (loop 5 0)(loop 4 5)(loop 3 20)(loop 2 60)(loop 1 120) 120 count n
  • 26.
    Lazy evaluation ● Onlyreturn a value when necessary “call-by-need” ● Things are calculated not when they are defined, but when they are actually needed. ● Maintain precision ● Optimise evaluation Examples: ratios, lazy sequence such as range and repeat
  • 27.
    TDD using midje ●Test driven development - Red/Green/Refactor ● Clean, tested, lean ● Midje - testing framework for clojure
  • 28.
    Clojure resources - Lotsof resources - Clojure documentation - https://www.braveclojure.com/ - https://clojure.org/community/books - Clojure koans - tryclj.com/ online REPL - https://clojure.org/api/cheatsheet
  • 29.