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

Enumerable's Ugly Cousin - GORUCO Microtalk

Enumerable's Ugly Cousin - GORUCO Microtalk

A condensed, "highlights" version of the longer talk I gave at Ancient City Ruby Conf.

Ross Kaffenberger

June 25, 2016
Tweet

More Decks by Ross Kaffenberger

Other Decks in Technology

Transcript

  1. Enumerator
    Enumerable’s Ugly Cousin
    @rossta GORUCO X

    View Slide

  2. What we say about
    Enumerable

    View Slide

  3. What we say about
    Enumerator

    View Slide

  4. View Slide

  5. PRACTICAL
    EXPRESSIVE
    CLEAN
    ELEGANT
    MINASWAN
    READABLE
    SIMPLE
    SIMPLE
    HAPPINESS
    BEAUTIFUL
    CONCISE

    View Slide

  6. View Slide

  7. View Slide

  8. You are all wrong,
    it’s also illegal

    View Slide

  9. View Slide

  10. 2008
    Rails 2
    Ruby 1.8.7

    View Slide

  11. 2008

    View Slide

  12. What’s the estimate?
    Oh, about 4 days

    View Slide

  13. 4 days
    weeks

    View Slide

  14. How to model
    infinite, enumerable,
    recurring events?

    View Slide

  15. { every: :month, on: { tuesday: 2 } }

    View Slide

  16. Enumerator!

    View Slide

  17. Enumerator
    – Ruby Docs
    “A class which allows
    both internal and
    external iteration.”

    View Slide

  18. enum = [1, 2, 3].to_enum
    # => #
    enum.next # => 1
    enum.next # => 2
    enum.next # => 3
    enum.next # => StopIteration
    enum.each { |x| # ... }

    View Slide

  19. “Ok, so what?”

    View Slide

  20. Enumerators can be
    generators

    View Slide

  21. $ rails g scaffold

    View Slide

  22. View Slide

  23. Generators
    produce
    data
    on the
    fly

    View Slide

  24. async
    infinite sequences
    fetching data
    list comprehensions
    concurrency
    lazy evaluation
    coroutines

    View Slide

  25. JavaScript
    Generators in

    View Slide

  26. View Slide

  27. –AirBnB JavaScript Style Guide
    “Don't use generators.”

    View Slide

  28. View Slide

  29. View Slide

  30. View Slide

  31. View Slide

  32. Python
    Generators in

    View Slide

  33. Awesome
    Simple, expressive.
    Love them.
    List comprehensions…
    insanely readable and
    easy to maintain

    View Slide

  34. Generators let us be
    lazy

    View Slide

  35. def fibonacci(n):
    result = []
    a = b = 1
    for i in xrange(n):
    result.append(a)
    a, b = b, a + b
    return result
    for a in fibonacci(20):
    print a
    def fibonacci(n):
    def fibonacci(n):
    result = []
    def fibonacci(n):
    result = []
    a = b = 1
    for i in xrange(n):
    result.append(a)
    a, b = b, a + b
    return result
    def fibonacci(n):
    result = []
    a = b = 1
    for i in xrange(n):
    def fibonacci(n):
    result = []
    a = b = 1
    for i in xrange(n):
    result.append(a)
    a, b = b, a + b

    View Slide

  36. result = []
    result.append(a)
    return result
    def fibonacci(n):
    a = b = 1
    for i in xrange(n):
    a, b = b, a + b
    for a in fibonacci(20):
    print a
    Eager!

    View Slide

  37. yield a
    def fibonacci(n):
    a = b = 1
    for i in xrange(n):
    a, b = b, a + b
    for a in fibonacci(20):
    print a
    Lazy!

    View Slide

  38. Can we write
    generators in Ruby?*
    *yes, we can!
    Q:

    View Slide

  39. def fibonacci(n):
    a = b = 1
    for i in xrange(n):
    yield a
    a, b = b, a + b
    for a in fibonacci(20):
    print a

    View Slide

  40. def fibonacci(n)
    a = b = 1
    n.times do
    yield a
    a, b = b, a + b
    end
    end
    fibonacci(20) { |a| puts a }
    Not
    Enumerable!

    View Slide

  41. Enumeratorize it!

    View Slide

  42. def fibonacci(n)
    a = b = 1
    n.times do
    yield a
    a, b = b, a + b
    end
    end
    def fibonacci(n)
    a = b = 1
    n.times do
    yield a
    a, b = b, a + b
    end
    end
    return to_enum(__method__, n) unless block_given?

    View Slide

  43. return to_enum(__method__) unless block_given?
    This seems like a big hack
    Too magical
    That’s ugly

    View Slide

  44. def fibonacci(n)
    a = b = 1
    n.times do
    yield a
    a, b = b, a + b
    end
    end
    def fibonacci(n)
    a = b = 1
    n.times do
    yield a
    a, b = b, a + b
    end
    end
    return to_enum(__method__, n) unless block_given?
    ]
    [ Enumerable!

    View Slide

  45. fibonacci(25).each { |a| puts a }
    fibonacci(25).map { |a| a * 3 }.select(&:odd?)
    fibonacci(100).find { |a| a > 50 }.take(5)

    View Slide

  46. obj_to_enum(int argc, VALUE *argv, VALUE obj)
    {
    VALUE enumerator, meth = sym_each;
    if (argc > 0) {
    --argc;
    meth = *argv++;
    }
    enumerator = rb_enumeratorize_with_size(obj, meth,
    argc, argv, 0);
    if (rb_block_given_p()) {
    enumerator_ptr(enumerator)->size =
    rb_block_proc();
    }
    return enumerator;
    }
    static VALUE
    enumerator.c

    View Slide

  47. obj_to_enum(int argc, VALUE *argv, VALUE obj)
    {
    VALUE enumerator, meth = sym_each;
    if (argc > 0) {
    --argc;
    meth = *argv++;
    }
    enumerator = rb_enumeratorize_with_size(obj, meth,
    argc, argv, 0);
    if (rb_block_given_p()) {
    enumerator_ptr(enumerator)->size =
    rb_block_proc();
    }
    return enumerator;
    }
    rb_enumeratorize_with_size(obj, meth,
    static VALUE
    enumerator.c
    obj_to_enum

    View Slide

  48. Enumerators can be
    streams

    View Slide

  49. (1..Float::INFINITY).
    map { |n| n * 3 }. # => ?
    select(&:odd?).
    take(100_000).
    reduce(&:+)
    (1..Float::INFINITY)

    View Slide

  50. (1..Float::INFINITY).
    lazy.
    map { |n| n * 3 }.
    select(&:odd?).
    take(100_000).
    reduce(&:+)
    # => 30000000000

    View Slide

  51. lazy augments how
    data is processed in an
    enumerator chain

    View Slide

  52. Eager Pipeline
    [] map select reduce

    View Slide

  53. Lazy Pipeline
    [] map select reduce

    View Slide

  54. Enumerators
    solve difficult problems

    View Slide

  55. Generating infinite sequences
    Processing large files
    Streaming API clients

    View Slide

  56. Recurring events

    View Slide

  57. Recurrence
    Enumerator
    find next event
    yield
    stop or continue

    View Slide

  58. $ gem install montrose
    github.com/rossta/montrose
    # recurring events in Ruby

    View Slide

  59. Enumeratorize it!

    View Slide

  60. View Slide

  61. View Slide

  62. Enumerator is ugly
    beautiful

    View Slide

  63. View Slide

  64. Sometimes, ugly code can
    teach us something

    View Slide

  65. @rossta
    rossta.net
    Ross Kaffenberger

    View Slide

  66. Live the questions now.
    Perhaps then, someday far in
    the future, you will gradually,
    without even noticing it,
    live your way into the answer…
    - Rilke

    View Slide