Slide 1

Slide 1 text

An intro to Functional Programming Arusha Coders July 6, 2016 by Reuben Cummings @reubano #arushacoders

Slide 2

Slide 2 text

a new way of thinking @reubano #arushacoders

Slide 3

Slide 3 text

pure functions

Slide 4

Slide 4 text

pure functions function input output outside world

Slide 5

Slide 5 text

imperative version @reubano #arushacoders

Slide 6

Slide 6 text

class Rectangle(object): def __init__(self, length, width): self.length = length self.width = width @property def area(self): return self.length * self.width def grow(self, amount): length = self.length * amount self.length = length

Slide 7

Slide 7 text

class Rectangle(object): def __init__(self, length, width): self.length = length self.width = width @property def area(self): return self.length * self.width def grow(self, amount): length = self.length * amount self.length = length internal state

Slide 8

Slide 8 text

class Rectangle(object): def __init__(self, length, width): self.length = length self.width = width @property def area(self): return self.length * self.width def grow(self, amount): length = self.length * amount self.length = length internal state state mutation

Slide 9

Slide 9 text

>>> r = Rectangle(2, 3) >>> r.length 2 >>> r.area 6 >>> r.grow(2) >>> r.length 4 >>> r.area 12

Slide 10

Slide 10 text

>>> r = Rectangle(2, 3) >>> r.length 2 >>> r.area 6 >>> r.grow(2) >>> r.length 4 >>> r.area 12 same commands, different results!?

Slide 11

Slide 11 text

>>> r = Rectangle(2, 3) >>> r.length 2 >>> r.area 6 >>> r.grow(2) >>> r.length 4 >>> r.area 12 same commands, different results!?

Slide 12

Slide 12 text

functional version @reubano #arushacoders

Slide 13

Slide 13 text

def make_rect(length, width): return (length, width) def grow_rect(rect, amount): return (rect[0] * amount, rect[1]) def get_length (rect): return rect[0] def get_area (rect): return rect[0] * rect[1]

Slide 14

Slide 14 text

def make_rect(length, width): return (length, width) def grow_rect(rect, amount): return (rect[0] * amount, rect[1]) def get_length (rect): return rect[0] def get_area (rect): return rect[0] * rect[1] no state!

Slide 15

Slide 15 text

>>> r = make_rect(2, 3) >>> get_length(r) 2 >>> grow_rect(r, 2) (4, 3) >>> get_area(r) 6 >>> get_length(r) 2 >>> get_area(r) 6 returned a new object

Slide 16

Slide 16 text

>>> r = make_rect(2, 3) >>> get_length(r) 2 >>> grow_rect(r, 2) (4, 3) >>> get_area(r) 6 >>> get_length(r) 2 >>> get_area(r) 6 attributes didn’t change!

Slide 17

Slide 17 text

>>> r = make_rect(2, 3) >>> get_length(r) 2 >>> grow_rect(r, 2) (4, 3) >>> get_area(r) 6 >>> get_length(r) 2 >>> get_area(r) 6 attributes didn’t change!

Slide 18

Slide 18 text

>>> big_r = grow_rect(r, 2) assign output of the grow function to a new object

Slide 19

Slide 19 text

>>> big_r = grow_rect(r, 2) >>> get_area(big_r) 12 >>> get_length(big_r) 4 attributes of new object are as expected

Slide 20

Slide 20 text

so what?? @reubano #arushacoders

Slide 21

Slide 21 text

from time import sleep class ExpensiveRectangle(Rectangle): @property def area(self): sleep(5) return self.length * self.width >>> r = ExpensiveRectangle(2, 3) >>> r.area 6 expensive!! >>> r.area 6

Slide 22

Slide 22 text

from functools import lru_cache @lru_cache() def exp_get_area (rect): sleep(5) return rect[0] * rect[1] >>> r = make_rect(2, 3) >>> exp_get_area(r) >>> exp_get_area(r) 6 6 expensive!! memoization

Slide 23

Slide 23 text

lazy evaluation

Slide 24

Slide 24 text

eager version @reubano #arushacoders

Slide 25

Slide 25 text

def double(ints): doubled = [] for i in ints: doubled.append(i * 2) return doubled >>> some_ints = range(1, 11) >>> double(some_ints) [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

Slide 26

Slide 26 text

lazy version @reubano #arushacoders

Slide 27

Slide 27 text

def gen_double(ints): for i in ints: yield i * 2 >>> gen_double(some_ints) >>> gen = gen_double(some_ints) >>> next(gen) 2 >>> next(gen) 4

Slide 28

Slide 28 text

can we do better? @reubano #arushacoders

Slide 29

Slide 29 text

def gen_double(ints): >>> gen_double(some_ints) >>> gen = gen_double(some_ints) >>> next(gen) 2 >>> next(gen) 4 return (i * 2 for i in ints)

Slide 30

Slide 30 text

who cares?? @reubano #arushacoders

Slide 31

Slide 31 text

def exp_double(ints): doubled = [] for i in ints: sleep(5) doubled.append(i * 2) return doubled def exp_gen_double(ints): for i in ints: sleep(5) yield i * 2 expensive!! expensive!!

Slide 32

Slide 32 text

>>> exp_double(some_ints) >>> gen = gen_exp_double(some_ints) >>> next(gen) >>> next(gen) [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] 2 4 >>> next(gen) 6

Slide 33

Slide 33 text

but wait, there’s more! @reubano #arushacoders

Slide 34

Slide 34 text

>>> from itertools import count >>> >>> inf_ints = count(1) >>> double(inf_ints) infinite iterator!!!

Slide 35

Slide 35 text

>>> from itertools import count >>> >>> inf_ints = count(1) >>> double(inf_ints) ... >>> gen = gen_double(inf_ints) >>> next(gen) 42220156 >>> next(gen) 42220158 infinite iterator!!!

Slide 36

Slide 36 text

stream processing

Slide 37

Slide 37 text

>>> stream = range(1, 10) >>> doubled = [2 * x for x in stream] >>> doubled [2, 4, 6, 8, 10, 12, 14, 16, 18] >>> old = [x for x in doubled if x > 10] >>> old [12, 14, 16, 18] >>> divided = [x / 3 for x in old] >>> divided [4.0, 4.6667, 5.3333, 6.0] transform stream at each stage

Slide 38

Slide 38 text

>>> stream = range(1, 10) >>> doubled = [2 * x for x in stream] >>> doubled [2, 4, 6, 8, 10, 12, 14, 16, 18] >>> old = [x for x in doubled if x > 10] >>> old [12, 14, 16, 18] >>> divided = [x / 3 for x in old] >>> divided [4.0, 4.6667, 5.3333, 6.0] user pure functions to return new streams

Slide 39

Slide 39 text

mini riko demo @reubano #arushacoders

Slide 40

Slide 40 text

extracting web data

Slide 41

Slide 41 text

RSS feeds

Slide 42

Slide 42 text

RSS feeds

Slide 43

Slide 43 text

>>> from riko.modules import ( ... fetch, truncate, sort, ... xpathfetchpage as xfetchpage) >>> >>> url = 'news.ycombinator.com/rss' >>> stream = fetch.pipe( ... conf={'url': url}) >>> stream_l = list(stream) >>> len(stream_l) 30 >>> stream_l[0] {'comments': 'http...', 'title': 'Think...', 'published': 'Tue, 5 Ju...'} convert generator into a list

Slide 44

Slide 44 text

>>> truncated = truncate.pipe( ... stream_l, conf={'count': 5}) >>> truncated_l = list(truncated) >>> len(truncated_l) 5 >>> sorted = sort.pipe( ... truncated_l, conf={ ... 'rule': { ... 'sort_key': 'title'}}) >>> >>> sorted_l = list(sorted) >>> sorted_l[0] {'title': 'Building a BitTorre...', ...}

Slide 45

Slide 45 text

>>> xpath = '/html/body/center/table...' >>> xconf = { ... 'url': {'subkey': 'comments'}, ... 'xpath': xpath} >>> comments = [ ... xfetchpage.pipe(s, conf=xconf) ... for s in sorted_l] >>> item = next(comments[0]) >>> item.keys() ['p', 'div', 'class', 'content'] >>> item['content'] "Great work! Quite annoying actually..."

Slide 46

Slide 46 text

pipeline version @reubano #arushacoders

Slide 47

Slide 47 text

from riko.collections import sync Pipe = sync.SyncPipe sconf = {'rule': {'sort_key': 'title'}} comments = ( Pipe('fetch', conf={'url': url}) .truncate(conf={'count': '5'}) .sort(conf=sconf) .xpathfetchpage(conf=xconf) .output) item = next(comments)

Slide 48

Slide 48 text

head to head functional imperative cacheable ✔ testable ✔ ? composable ✔ simple ✔ stateful ✔ @reubano #arushacoders

Slide 49

Slide 49 text

Resources https:/ /docs.python.org/3.5/library/itertools.html https:/ /docs.python.org/3.5/library/functools.html https:/ /lodash.com/ http:/ /coffeescript.org/ https:/ /www.youtube.com/watch?v=7aYS9PcAITQ https:/ /github.com/nerevu/riko @reubano #arushacoders

Slide 50

Slide 50 text

Resources https:/ /github.com/reubano/meza http:/ /ocw.mit.edu/courses/electrical-engineering- and-computer-science/6-001-structure-and- interpretation-of-computer-programs- spring-2005/ https:/ /www.infoq.com/presentations/Simple- Made-Easy @reubano #arushacoders

Slide 51

Slide 51 text

Thanks! Reuben Cummings @reubano @reubano #arushacoders