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.

B0169a78f851962058d63337ad0147d6?s=128

Ross Kaffenberger

June 25, 2016
Tweet

Transcript

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

  2. What we say about Enumerable

  3. What we say about Enumerator

  4. None
  5. PRACTICAL EXPRESSIVE CLEAN ELEGANT MINASWAN READABLE SIMPLE SIMPLE HAPPINESS BEAUTIFUL

    CONCISE
  6. None
  7. None
  8. You are all wrong, it’s also illegal

  9. None
  10. 2008 Rails 2 Ruby 1.8.7

  11. 2008

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

  13. 4 days weeks

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

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

  16. Enumerator!

  17. Enumerator – Ruby Docs “A class which allows both internal

    and external iteration.”
  18. enum = [1, 2, 3].to_enum # => #<Enumerator: [1, 2,

    3]:each> enum.next # => 1 enum.next # => 2 enum.next # => 3 enum.next # => StopIteration enum.each { |x| # ... }
  19. “Ok, so what?”

  20. Enumerators can be generators

  21. $ rails g scaffold

  22. None
  23. Generators produce data on the fly

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

    coroutines
  25. JavaScript Generators in

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

  28. None
  29. None
  30. None
  31. None
  32. Python Generators in

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

    easy to maintain
  34. Generators let us be lazy

  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
  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!
  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!
  38. Can we write generators in Ruby?* *yes, we can! Q:

  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
  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!
  41. Enumeratorize it!

  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?
  43. return to_enum(__method__) unless block_given? This seems like a big hack

    Too magical That’s ugly
  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!
  45. fibonacci(25).each { |a| puts a } fibonacci(25).map { |a| a

    * 3 }.select(&:odd?) fibonacci(100).find { |a| a > 50 }.take(5)
  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
  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
  48. Enumerators can be streams

  49. (1..Float::INFINITY). map { |n| n * 3 }. # =>

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

    take(100_000). reduce(&:+) # => 30000000000
  51. lazy augments how data is processed in an enumerator chain

  52. Eager Pipeline [] map select reduce

  53. Lazy Pipeline [] map select reduce

  54. Enumerators solve difficult problems

  55. Generating infinite sequences Processing large files Streaming API clients

  56. Recurring events

  57. Recurrence Enumerator find next event yield stop or continue

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

  59. Enumeratorize it!

  60. None
  61. None
  62. Enumerator is ugly beautiful

  63. None
  64. Sometimes, ugly code can teach us something

  65. @rossta rossta.net Ross Kaffenberger

  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