Save 37% off PRO during our Black Friday Sale! »

Intro to Functional Programming

Intro to Functional Programming

869402f85dcbabcef3da1ee61b88a45a?s=128

Reuben Cummings

July 06, 2016
Tweet

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