Introduction to FP

kapware

Paweł Kapała

in practice

Feedback loop

http://imgs.xkcd.com/comics/compiling.png

kapware

kapware

Read

Eval

Print

Loop

Interactively with REPL!

All while the app is running!

kapware

Part 1: ABC

nil

kapware

false

true

kapware

0 1 2 3

0.2

2/3

2

5302542730

kapware

"str"

"Wawel"

"Kraków"

"hello world"

"John"

"Mścigniew"

kapware

:key

:title

:id

:chupacabra

:name

kapware

(list)

("a" "b" "c" "d" "e" "f")

(0 1 2 3 4 5 6 7)

(:a :b :c :d :e)

kapware

()

[vec]

[0 1 2 3 4 5 6]

["John" "Jane" "Lucy"]

[:title :name :id]

kapware

{map}

{:id 1 :name "John" :surname "Doe"}

{:subject "this" :predicate "is"
 :preposition "a" :object "map"}

kapware

and they were all immutable!

kapware

immutablility leads to clean code

kapware

(def name binding)

a-symbol

bar

namespace/example

iamfoo

kapware

(foo arg1 arg2)

(str "a" "b" "c")

(conj [] 1 2 3 4)

 

(+ 2 4 6 7)

(first [1 2 3 4])

 

(assoc {:id 1 :name "John"} :surname "Doe")

(= {:id 1 :name "Joe"} {:id 1 :name "Joe"})

kapware

(defn fname [arg1 arg2]
body
)

(defn greeter [arg1 arg2]
    (str "Hello " arg1 " " arg2 "!"))

(defn plus [arg1 arg2]
    (+ arg1 arg2))

kapware

#(body %1 %2)

#(+ %1 %2)

#(str "Hello " %1 " " %2 "!")

kapware

and they were pure*!

*at least for the most part

kapware

kapware

Part 2: The story

1 404 years ago...

kapware

There lived...

kapware

...Krakus

{:name "Krakus" :health 100}
(defn citizen [name]
  {:name name :health 100})

(citizen "Krakus")

kapware

and he was king!

{:name "Krakus" :health 100 :title :king}
(defn promote [citizen title]
  (assoc citizen :title title))

(defn king [name] 
  (promote (citizen name) :king))

(king "Krakus")

assoc returns new map with key value added to it

kapware

He had the greatest castle!

kapware

{:name "Wawel"}
(defn castle [name]
  {:name name})

(castle "Wawel")
{:name "Wawel" :citizens [
  {:name "Krakus" :health 100 :title :king}]}
(defn occupy [what whom]
   (update what :citizens 
     (fnil conj []) whom))

(occupy (castle "Wawel") (king "Krakus"))

update returns map with a specified key changed by given function

conj returns collection with element appended

fnil executes specified function passing argument in case of nil

kapware

He was rich!

kapware

He had lots and lots of goods

{:energy 10 :size 10}
(defn food [energy]
  {:energy energy :size 10})

(defn ram[] (food 10))
(defn cow[] (food 20))

(ram)

kapware

... stored at the castle

{:name "Wawel" :goods [{:energy 10 :size 10}]}
(defn store [structure food]
  (update structure :goods 
    (fnil conj []) food))

(store (castle "Wawel") (ram))

kapware

... I said a lot!

({:energy 20 :size 10}
 {:energy 20 :size 10}
 {:energy 20 :size 10}
;... 15 more
 {:energy 20 :size 10}
 {:energy 20 :size 10})
(repeatedly 20 cow)

repeatadly returns (lazy) collection containing function result times the number specified, or infinite if size is not specified

kapware

He had bravest knights

kapware

(defn knight [name]
  (assoc 
    (promote (citizen name) :knight)
    :power 100))

(knight "Małowuj")
{:name "Małowuj" :title :knight :power 100}

kapware

... a lot of them!

{:name "Wawel" :citizens [
  {:name "Małowuj" :title :knight :power 100 :health 100}
  {:name "Minigniew" :title :knight :power 100 :health 100}
  {:name "Gromisław" :title :knight :power 100 :health 100}
  {:name "Nowosiodł" :title :knight :power 100 :health 100}
  {:name "Twardomir" :title :knight :power 100 :health 100}
  {:name "Włościobyt" :title :knight :power 100 :health 100}]}
(reduce occupy (castle "Wawel") 
  (map knight ["Małowuj" "Minigniew" "Gromisław" 
  "Nowosiodł" "Twardomir" "Włościobyt"]))

map executes function for each collection element and returns a collection of results

kapware

reduce calls two-arg function on a first element of the collection, then passes the return back to the function as a first argument and takes next element of the collection...

... but one day around 1:31pm

kapware

Dragon appeared!

(defn dragon [name]
  {:name name :health 1000 :power 1000})

(dargon "Smok")
{:name "Smok" :health 1000 :power 1000}

... and it flew right onto Wawel!

(occupy (castle "Wawel") (dragon "Smok"))

kapware

{:name "Wawel" :citizens [{:name "Smok" 
  :health 1000 :power 1000}]

... and it was ever hungry!

(defn eat [who what]
  (merge-with + who what))

(eat (dragon "Smok") (ram))
{:name "Smok" :health 1000 :power 1000 
 :energy 10 :size 10}

kapware

merge-with combines two map into one using provided function (+ in this case) on the values of matching keys

King ordered: "kill the beast!"

(defn attack [target who]
  (update target :health 
          (fnil - 0) (:power who 0)))

(attack (dragon "Smok") (knight "Minigniew"))
{:name "Smok" :health 900 :power 1000}

kapware

kapware

but the dragon struck back!

; {:name "Minigniew" :health -900
;  :title :knight :power 100}
true
(defn dead? [who]
  (<= (:health who 0) 0))

(dead? 
  (attack (knight "Minigniew") (dragon "Smok")))

kapware

Skuba had an idea and took ram and some sulfur and tar

(defn sulfur []
  {:thirst 500})

(defn tar []
  {:size 500))

(defn ram-with-sulfur-and-tar []
  (merge-with + (ram) (sulfur) (tar)))

(ram-with-sulfur-and-tar)
{:energy 10 :size 510 :thirst 500}

kapware

Trojan Ram!

... and the dragon ate it

(eat (dragon "Smok") 
     (ram-with-sulfur-and-tar))
{:name "Smok" :health 1000 :power 1000
 :energy 10 :size 510 :thirst 500}

kapware

with a headache...

kapware

...and after drinking 100 galons

(defn water []
  {:thirst -1 :size 10})

(defn vistula []
  (repeatedly water))

(take 100 (vistula))

kapware

({:thirst -1 :size 10}
 {:thirst -1 :size 10}
 {:thirst -1 :size 10}
 {:thirst -1 :size 10}
 ;... 96x more
)

the dragon blew out to pieces!

kapware

({:name "Smok-0" :health -1000 :power 1000} 
{:name "Smok-1" :health -1000 :power 1000} 
{:name "Smok-2" :health -1000 :power 1000} 
{:name "Smok-3" :health -1000 :power 1000}
;... 96x more
)
(defn pieceof [who no]
  (-> who
      (update :name str "-" no)
      (assoc :health -1000)))

(map #(pieceof (dragon "Smok") %1) (range 100))

-> Is called "a threading macro" that passes result of previous function call as first parameter to next call in block, the above is equivalent to:

(assoc (update who :name str "-" no) :health -1000)

He did it for Kraków !

kapware

The end

kapware

Simple? Perfect!

kapware

https://github.com/kapware/clj-krakus/

So you want to learn clojure?

kapware

4clojure.com

Clojure for the Brave and True

clojure.org

Structure and interpretation of computer programs

Mastering Clojure

clojurekoans.com

Find your mentor!

kapware

Thank you!

kapware

Introduction to functional programming in practice

By kapware

Introduction to functional programming in practice

  • 733