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

Intro to Functional Programming

Intro to Functional Programming

Reuben Cummings

July 06, 2016
Tweet

More Decks by Reuben Cummings

Other Decks in Programming

Transcript

  1. An intro to Functional Programming Arusha Coders July 6, 2016

    by Reuben Cummings @reubano #arushacoders
  2. 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
  3. 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
  4. 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
  5. >>> r = Rectangle(2, 3) >>> r.length 2 >>> r.area

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

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

    6 >>> r.grow(2) >>> r.length 4 >>> r.area 12 same commands, different results!?
  8. 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]
  9. 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!
  10. >>> 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
  11. >>> 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!
  12. >>> 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!
  13. 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
  14. 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
  15. 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]
  16. def gen_double(ints): for i in ints: yield i * 2

    >>> gen_double(some_ints) <generator object gen_double at 0x...> >>> gen = gen_double(some_ints) >>> next(gen) 2 >>> next(gen) 4
  17. def gen_double(ints): >>> gen_double(some_ints) <generator object gen_double at 0x...> >>>

    gen = gen_double(some_ints) >>> next(gen) 2 >>> next(gen) 4 return (i * 2 for i in ints)
  18. 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!!
  19. >>> from itertools import count >>> >>> inf_ints = count(1)

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

    >>> double(inf_ints) ... >>> gen = gen_double(inf_ints) >>> next(gen) 42220156 >>> next(gen) 42220158 infinite iterator!!!
  21. >>> 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
  22. >>> 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
  23. >>> 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
  24. >>> 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...', ...}
  25. >>> 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..."
  26. 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)
  27. head to head functional imperative cacheable ✔ testable ✔ ?

    composable ✔ simple ✔ stateful ✔ @reubano #arushacoders