$30 off During Our Annual Pro Sale. View Details »

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

    View Slide

  2. a new way
    of thinking
    @reubano #arushacoders

    View Slide

  3. pure functions

    View Slide

  4. pure functions
    function
    input output
    outside
    world

    View Slide

  5. imperative
    version
    @reubano #arushacoders

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  12. functional
    version
    @reubano #arushacoders

    View Slide

  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]

    View Slide

  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!

    View Slide

  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

    View Slide

  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!

    View Slide

  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!

    View Slide

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

    View Slide

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

    View Slide

  20. so what??
    @reubano #arushacoders

    View Slide

  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

    View Slide

  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

    View Slide

  23. lazy evaluation

    View Slide

  24. eager
    version
    @reubano #arushacoders

    View Slide

  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]

    View Slide

  26. lazy
    version
    @reubano #arushacoders

    View Slide

  27. 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

    View Slide

  28. can we do
    better?
    @reubano #arushacoders

    View Slide

  29. 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)

    View Slide

  30. who
    cares??
    @reubano #arushacoders

    View Slide

  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!!

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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!!!

    View Slide

  36. stream processing

    View Slide

  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

    View Slide

  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

    View Slide

  39. mini riko
    demo
    @reubano #arushacoders

    View Slide

  40. extracting web data

    View Slide

  41. RSS feeds

    View Slide

  42. RSS feeds

    View Slide

  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

    View Slide

  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...', ...}

    View Slide

  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..."

    View Slide

  46. pipeline
    version
    @reubano #arushacoders

    View Slide

  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)

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  51. Thanks!
    Reuben Cummings
    @reubano
    @reubano #arushacoders

    View Slide