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

Intro to Functional Programming

Intro to Functional Programming

869402f85dcbabcef3da1ee61b88a45a?s=128

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. a new way of thinking @reubano #arushacoders

  3. pure functions

  4. pure functions function input output outside world

  5. imperative version @reubano #arushacoders

  6. 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
  7. 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
  8. 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
  9. >>> r = Rectangle(2, 3) >>> r.length 2 >>> r.area

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

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

    6 >>> r.grow(2) >>> r.length 4 >>> r.area 12 same commands, different results!?
  12. functional version @reubano #arushacoders

  13. 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]
  14. 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!
  15. >>> 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
  16. >>> 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!
  17. >>> 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!
  18. >>> big_r = grow_rect(r, 2) assign output of the grow

    function to a new object
  19. >>> big_r = grow_rect(r, 2) >>> get_area(big_r) 12 >>> get_length(big_r)

    4 attributes of new object are as expected
  20. so what?? @reubano #arushacoders

  21. 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
  22. 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
  23. lazy evaluation

  24. eager version @reubano #arushacoders

  25. 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]
  26. lazy version @reubano #arushacoders

  27. 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
  28. can we do better? @reubano #arushacoders

  29. 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)
  30. who cares?? @reubano #arushacoders

  31. 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!!
  32. >>> 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
  33. but wait, there’s more! @reubano #arushacoders

  34. >>> from itertools import count >>> >>> inf_ints = count(1)

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

    >>> double(inf_ints) ... >>> gen = gen_double(inf_ints) >>> next(gen) 42220156 >>> next(gen) 42220158 infinite iterator!!!
  36. stream processing

  37. >>> 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
  38. >>> 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
  39. mini riko demo @reubano #arushacoders

  40. extracting web data

  41. RSS feeds

  42. RSS feeds

  43. >>> 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
  44. >>> 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...', ...}
  45. >>> 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..."
  46. pipeline version @reubano #arushacoders

  47. 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)
  48. head to head functional imperative cacheable ✔ testable ✔ ?

    composable ✔ simple ✔ stateful ✔ @reubano #arushacoders
  49. 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
  50. 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
  51. Thanks! Reuben Cummings @reubano @reubano #arushacoders