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

Ruby Function Composition

Ruby Function Composition

Ruby can elegantly blend both Functional Programming and Object Oriented design patterns together with minimal effort. In this talk, I'll combine both of these design patterns by briefly walking you through the fundamentals of functional composition, appling monads, and finally wrapping all of this up in a nice transactional pipeline object composed of multiple -- and fault tolerant -- operations for building more robust architectures. You'll learn a lot and walk away with new patterns to apply to your own code base.

Brooke Kuhlmann

April 18, 2024
Tweet

More Decks by Brooke Kuhlmann

Other Decks in Programming

Transcript

  1. en.wikipedia.org/wiki/Function_composition_(computer_science) What "[F]unction composition is an act or mechanism to

    combine simple functions to build more complicated ones. Like the usual composition of functions in mathematics, the result of each function is passed as the argument of the next, and the result of the last one is the result of the whole." -- Wikipedia alchemists.io
  2. History Ruby 2.6.0 Methods: #>> #<< (forward composition) (backward composition)

    alchemists.io/articles/ruby_function_composition alchemists.io
  3. History Ruby 2.6.0 Methods: Proc Method #>> #<< Objects: (forward

    composition) (backward composition) alchemists.io/articles/ruby_function_composition alchemists.io
  4. Fundamentals m m m m m m m m m

    m m m m m m m m m m m m m m m m m m 3| | | | | | | | | | | Procs alchemists.io/articles/ruby_function_composition alchemists.io
  5. Fundamentals m m m m m m m m m

    m m m m m m m m m m m m m m m m m m 3| | | | | | | | | | | Procs alchemists.io/articles/ruby_function_composition alchemists.io
  6. Fundamentals m m m m m m m m m

    m m m m m m m m m m m m m m m m m m 3| | | | | | | | | | | m m m m m m m m m m m c c c c c # # # # # Procs alchemists.io/articles/ruby_function_composition alchemists.io
  7. Fundamentals m m m m m m m m m

    m m m m m m m m m m m m m m m m m m 3| | | | | | | | | | | m m m m m m m m m m m c c c c c # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Procs alchemists.io/articles/ruby_function_composition alchemists.io
  8. Fundamentals m m m m m m m m m

    m m m m m m m m m m m m m m m m m m 3| | | | | | | | | | | m m m m m m m m m m m c c c c c # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # m m m m m m m m m m m m m m m 3 # # Procs alchemists.io/articles/ruby_function_composition alchemists.io
  9. Fundamentals multiplier = proc { |number, by = 3| number

    * by } multiplier.class # Proc multiplier.inspect # #<Proc:0x00000001059b5248 (irb):22> multiplier.call 3 # 9 multiplier.call 3, 10 # 30 Procs alchemists.io/articles/ruby_function_composition alchemists.io
  10. Fundamentals m m m m m m m m m

    m m m m m m m m m m m m m m 3 Lam bdas alchemists.io/articles/ruby_function_composition alchemists.io
  11. Fundamentals m m m m m m m m m

    m m m m m m m m m m m m m m 3 Lam bdas alchemists.io/articles/ruby_function_composition alchemists.io
  12. Fundamentals m m m m m m m m m

    m m m m m m m m m m m m m m 3 m m m m m m m m m m m c c c c c # # # # # Lam bdas alchemists.io/articles/ruby_function_composition alchemists.io
  13. Fundamentals m m m m m m m m m

    m m m m m m m m m m m m m m 3 m m m m m m m m m m m c c c c c # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Lam bdas alchemists.io/articles/ruby_function_composition alchemists.io
  14. Fundamentals m m m m m m m m m

    m m m m m m m m m m m m m m 3 m m m m m m m m m m m c c c c c # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Lam bdas alchemists.io/articles/ruby_function_composition alchemists.io
  15. Fundamentals m m m m m m m m m

    m m m m m m m m m m m m m m 3 m m m m m m m m m m m c c c c c # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # m m m m m m m m m m m m m m m 3 # # Lam bdas alchemists.io/articles/ruby_function_composition alchemists.io
  16. Fundamentals multiplier = -> number, by = 3 { number

    * by } multiplier.class # Proc multiplier.inspect # "#<Proc:0x0000000104ba68a0 (irb):19 (lambda)>" multiplier.call 3 # 9 multiplier.call 3, 10 # 30 Lam bdas alchemists.io/articles/ruby_function_composition alchemists.io
  17. Fundamentals C C C C C C C C C

    C C C C C C C C C C C C d d d d d d d .m m m m m m m m ( ( ( ( ( ( ( ( ( ( ( 3) ) ) ) ) ) ) ) ) ) ) ) M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  18. Fundamentals C C C C C C C C C

    C C C C C C C C C C C C d d d d d d d .m m m m m m m m ( ( ( ( ( ( ( ( ( ( ( 3) ) ) ) ) ) ) ) ) ) ) ) M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  19. Fundamentals C C C C C C C C C

    C C C C C C C C C C C C d d d d d d d .m m m m m m m m ( ( ( ( ( ( ( ( ( ( ( 3) ) ) ) ) ) ) ) ) ) ) ) M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  20. Fundamentals C C C C C C C C C

    C C C C C C C C C C C C d d d d d d d .m m m m m m m m ( ( ( ( ( ( ( ( ( ( ( 3) ) ) ) ) ) ) ) ) ) ) ) m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  21. Fundamentals C C C C C C C C C

    C C C C C C C C C C C C d d d d d d d .m m m m m m m m ( ( ( ( ( ( ( ( ( ( ( 3) ) ) ) ) ) ) ) ) ) ) ) m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  22. Fundamentals C C C C C C C C C

    C C C C C C C C C C C C d d d d d d d .m m m m m m m m ( ( ( ( ( ( ( ( ( ( ( 3) ) ) ) ) ) ) ) ) ) ) ) m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m c c c c c # # # # # # # M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  23. Fundamentals C C C C C C C C C

    C C C C C C C C C C C C d d d d d d d .m m m m m m m m ( ( ( ( ( ( ( ( ( ( ( 3) ) ) ) ) ) ) ) ) ) ) ) m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m c c c c c # # # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  24. Fundamentals C C C C C C C C C

    C C C C C C C C C C C C d d d d d d d .m m m m m m m m ( ( ( ( ( ( ( ( ( ( ( 3) ) ) ) ) ) ) ) ) ) ) ) m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m c c c c c # # # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  25. Fundamentals C C C C C C C C C

    C C C C C C C C C C C C d d d d d d d .m m m m m m m m ( ( ( ( ( ( ( ( ( ( ( 3) ) ) ) ) ) ) ) ) ) ) ) m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m c c c c c # # # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # m m m m m m m m m m m m m m m 3 # # M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  26. Fundamentals C C C C C C C C C

    C C C C C C C C C C C C d d d d d d d .m m m m m m m m ( ( ( ( ( ( ( ( ( ( ( 3) ) ) ) ) ) ) ) ) ) ) ) m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m c c c c c # # # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # m m m m m m m m m m m m m m m 3 # # m m m m m m m m m m m m m m m 3, 1 1 # # # M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  27. Fundamentals C C C C C C C C C

    C C C C C C C C C C C C d d d d d d d .m m m m m m m m ( ( ( ( ( ( ( ( ( ( ( 3) ) ) ) ) ) ) ) ) ) ) ) m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m c c c c c # # # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # m m m m m m m m m m m m m m m 3 # # m m m m m m m m m m m m m m m 3, 1 1 # # # f f f f f f f f f f f f f f f f f f f f f f f f f f f M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  28. Fundamentals C C C C C C C C C

    C C C C C C C C C C C C d d d d d d d .m m m m m m m m ( ( ( ( ( ( ( ( ( ( ( 3) ) ) ) ) ) ) ) ) ) ) ) m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m c c c c c # # # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # m m m m m m m m m m m m m m m 3 # # m m m m m m m m m m m m m m m 3, 1 1 # # # f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f f c c c c c # # # # # M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  29. Fundamentals Calculate = Module.new { def self.multiply(number, by = 3)

    = number * by } multiplier = Calculate.method :multiply multiplier.class # Method multiplier.inspect # "#<Method: Calculate.multiply(number, by=...) (irb):1>" multiplier.call 3 # 9 multiplier.call 3, 10 # 30 function = multiplier.to_proc function.class # Proc function.inspect # #<Proc:0x0000000104906f78 (lambda)> M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  30. Fundamentals Calculate = Module.new { def self.multiply(number, by = 3)

    = number * by } multiplier = Calculate.method :multiply multiplier.class # Method multiplier.inspect # "#<Method: Calculate.multiply(number, by=...) (irb):1>" multiplier.call 3 # 9 multiplier.call 3, 10 # 30 function = multiplier.to_proc function.class # Proc function.inspect # #<Proc:0x0000000104906f78 (lambda)> M ethods alchemists.io/articles/ruby_function_composition alchemists.io
  31. Fundamentals c c c c c d d d 3

    @ @ @ e e e d d d c c c c ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( p p p p p p p e e e C lasses alchemists.io/articles/ruby_function_composition alchemists.io
  32. Fundamentals c c c c c d d d 3

    @ @ @ e e e d d d c c c c ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( p p p p p p p e e e C lasses alchemists.io/articles/ruby_function_composition alchemists.io
  33. Fundamentals c c c c c d d d 3

    @ @ @ e e e d d d c c c c ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( p p p p p p p e e e C lasses alchemists.io/articles/ruby_function_composition alchemists.io
  34. Fundamentals c c c c c d d d 3

    @ @ @ e e e d d d c c c c ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( p p p p p p p e e e m m m m m m m m m m m m m m m m m m m m m m m m m C lasses alchemists.io/articles/ruby_function_composition alchemists.io
  35. Fundamentals c c c c c d d d 3

    @ @ @ e e e d d d c c c c ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( p p p p p p p e e e m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m c c c c c # # # # # # # # # # # C lasses alchemists.io/articles/ruby_function_composition alchemists.io
  36. Fundamentals c c c c c d d d 3

    @ @ @ e e e d d d c c c c ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( p p p p p p p e e e m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m c c c c c # # # # # # # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C lasses alchemists.io/articles/ruby_function_composition alchemists.io
  37. Fundamentals c c c c c d d d 3

    @ @ @ e e e d d d c c c c ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( p p p p p p p e e e m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m c c c c c # # # # # # # # # # # m m m m m m m m m m m m m m m m m m # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # m m m m m m m m m m m m m m m 3 # # C lasses alchemists.io/articles/ruby_function_composition alchemists.io
  38. Fundamentals class Multiplier def initialize by = 3 @by =

    by end def call(number) = number * by private attr_reader :by end multiplier = Multiplier.new multiplier.class # Multiplier multiplier.inspect # "#<Multiplier:0x0000000103761908 @by=3>" multiplier.call 3 # 9 Multiplier.new(10).call 3 # 30 C lasses alchemists.io/articles/ruby_function_composition alchemists.io
  39. Fundamentals class Multiplier def initialize by = 3 @by =

    by end def call(number) = number * by private attr_reader :by end multiplier = Multiplier.new multiplier.class # Multiplier multiplier.inspect # "#<Multiplier:0x0000000103761908 @by=3>" multiplier.call 3 # 9 Multiplier.new(10).call 3 # 30 C lasses alchemists.io/articles/ruby_function_composition alchemists.io
  40. Composition module Composable def >>(other) = method(:call) >> other def

    <<(other) = method(:call) << other def call = fail NotImplementedError, "`#{self.class.name}##{__method__}` must be implemented." end alchemists.io/articles/ruby_function_composition alchemists.io
  41. Composition module Composable def >>(other) = method(:call) >> other def

    <<(other) = method(:call) << other def call = fail NotImplementedError, "`#{self.class.name}##{__method__}` must be implemented." end alchemists.io/articles/ruby_function_composition alchemists.io
  42. Composition module Composable def >>(other) = method(:call) >> other def

    <<(other) = method(:call) << other def call = fail NotImplementedError, "`#{self.class.name}##{__method__}` must be implemented." end Pipeable alchemists.io/articles/ruby_function_composition alchemists.io
  43. Composition class Divider include Composable def initialize by = 3

    @by = by end def call(number) = number / by private attr_reader :by end Class alchemists.io/articles/ruby_function_composition alchemists.io
  44. Composition class Divider include Composable def initialize by = 3

    @by = by end def call(number) = number / by private attr_reader :by end Class alchemists.io/articles/ruby_function_composition alchemists.io
  45. Composition module Calculate def self.multiply(number, by = 3) = number

    * by end Method alchemists.io/articles/ruby_function_composition alchemists.io
  46. Composition adder = proc { |number, by = 3| number

    + by } subtracter = -> number, by = 3 { number - by } divider = Divider.new multiplier = Calculate.method :multiply alchemists.io/articles/ruby_function_composition alchemists.io
  47. Composition adder = proc { |number, by = 3| number

    + by } subtracter = -> number, by = 3 { number - by } divider = Divider.new multiplier = Calculate.method :multiply Proc alchemists.io/articles/ruby_function_composition alchemists.io
  48. Composition adder = proc { |number, by = 3| number

    + by } subtracter = -> number, by = 3 { number - by } divider = Divider.new multiplier = Calculate.method :multiply Proc Lambda alchemists.io/articles/ruby_function_composition alchemists.io
  49. Composition adder = proc { |number, by = 3| number

    + by } subtracter = -> number, by = 3 { number - by } divider = Divider.new multiplier = Calculate.method :multiply Proc Lambda Class alchemists.io/articles/ruby_function_composition alchemists.io
  50. Composition adder = proc { |number, by = 3| number

    + by } subtracter = -> number, by = 3 { number - by } divider = Divider.new multiplier = Calculate.method :multiply Proc Lambda Class Method alchemists.io/articles/ruby_function_composition alchemists.io
  51. Composition adder = proc { |number, by = 3| number

    + by } subtracter = -> number, by = 3 { number - by } divider = Divider.new multiplier = Calculate.method :multiply Proc Lambda Class Method Composable alchemists.io/articles/ruby_function_composition alchemists.io
  52. Composition multiplier (method) subtractor (lambda) adder (proc) divider (class) >>

    >> >> alchemists.io/articles/ruby_function_composition alchemists.io
  53. Composition multiplier (method) subtractor (lambda) adder (proc) divider (class) >>

    >> >> alchemists.io/articles/ruby_function_composition alchemists.io
  54. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 alchemists.io/articles/ruby_function_composition alchemists.io
  55. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 Forward composition alchemists.io/articles/ruby_function_composition alchemists.io
  56. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 Forward composition Backward composition alchemists.io/articles/ruby_function_composition alchemists.io
  57. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 alchemists.io/articles/ruby_function_composition alchemists.io
  58. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 alchemists.io/articles/ruby_function_composition alchemists.io
  59. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 (10 + 3) * 3 = 39 alchemists.io/articles/ruby_function_composition alchemists.io
  60. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 (10 + 3) * 3 = 39 📖 alchemists.io/articles/ruby_function_composition alchemists.io
  61. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 (10 + 3) * 3 = 39 📖 1 alchemists.io/articles/ruby_function_composition alchemists.io
  62. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 (10 + 3) * 3 = 39 📖 1 alchemists.io/articles/ruby_function_composition alchemists.io
  63. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 (10 + 3) * 3 = 39 📖 1 alchemists.io/articles/ruby_function_composition alchemists.io
  64. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 (10 + 3) * 3 = 39 📖 1 2 alchemists.io/articles/ruby_function_composition alchemists.io
  65. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 (10 + 3) * 3 = 39 📖 1 2 alchemists.io/articles/ruby_function_composition alchemists.io
  66. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 alchemists.io/articles/ruby_function_composition alchemists.io
  67. Composition (adder >> multiplier).call 10 # 39 (multiplier << adder).call

    10 # 39 ⭐ alchemists.io/articles/ruby_function_composition alchemists.io
  68. Composition (adder >> multiplier >> subtracter >> divider).call 10 #

    12 (divider << subtracter << multiplier << adder).call 10 # 12 alchemists.io/articles/ruby_function_composition alchemists.io
  69. Composition (adder >> multiplier >> subtracter >> divider).call 10 #

    12 (divider << subtracter << multiplier << adder).call 10 # 12 (((10 + 3) * 3) - 3) / 3 = 12 alchemists.io/articles/ruby_function_composition alchemists.io
  70. Composition (adder >> multiplier >> subtracter >> divider).call 10 #

    12 (divider << subtracter << multiplier << adder).call 10 # 12 (((10 + 3) * 3) - 3) / 3 = 12 alchemists.io/articles/ruby_function_composition alchemists.io
  71. Composition (adder >> multiplier >> subtracter >> divider).call 10 #

    12 (divider << subtracter << multiplier << adder).call 10 # 12 (((10 + 3) * 3) - 3) / 3 = 12 alchemists.io/articles/ruby_function_composition alchemists.io
  72. Composition a a a a a a a a a

    a a a a a a a a a a a a a a a a a a a a a a a a a alchemists.io/articles/ruby_function_composition alchemists.io
  73. Composition a a a a a a a a a

    a a a a a a a a a a a a a a a a a a a a a a a a a s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s alchemists.io/articles/ruby_function_composition alchemists.io
  74. Composition a a a a a a a a a

    a a a a a a a a a a a a a a a a a a a a a a a a a s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a alchemists.io/articles/ruby_function_composition alchemists.io
  75. Composition add_and_multiply = adder >> multiplier subtract_and_divide = subtracter >>

    divider add_multiply_subtract_and_divide = add_and_multiply >> subtract_and_divide add_multiply_subtract_and_divide.call 10 # 12 alchemists.io/articles/ruby_function_composition alchemists.io
  76. Composition add_and_multiply = adder >> multiplier subtract_and_divide = subtracter >>

    divider add_multiply_subtract_and_divide = add_and_multiply >> subtract_and_divide add_multiply_subtract_and_divide.call 10 # 12 (((10 + 3) * 3) - 3) / 3 = 12 alchemists.io/articles/ruby_function_composition alchemists.io
  77. Composition (adder >> multiplier).call 10 # 39 (adder << multiplier).call

    10 # 33 Order of operations matters! alchemists.io/articles/ruby_function_composition alchemists.io
  78. Composition (adder >> Divider.new(0) >> multiplier).call 10 # ZeroDivisionError: divided

    by 0 Any error will halt the operation. alchemists.io/articles/ruby_function_composition alchemists.io
  79. ✅ ❌ ✅ ✅ Success Success Success Pipeline (Railway Pattern)

    alchemists.io alchemists.io/projects/pipeable
  80. ✅ ❌ ✅ ✅ Success Success Success Failure Failure Failure

    Pipeline (Railway Pattern) alchemists.io alchemists.io/projects/pipeable
  81. ✅ ❌ ✅ ✅ Success Success Success Failure Failure Failure

    Pipeline No Exceptions! (Railway Pattern) alchemists.io alchemists.io/projects/pipeable
  82. Pipeable require "bundler/inline" gemfile true do source "https://rubygems.org" gem "amazing_print"

    gem "debug" gem "http" gem "dry-monads" gem "pipeable" end Install alchemists.io/articles/ruby_function_composition alchemists.io
  83. Pipeable require "bundler/inline" gemfile true do source "https://rubygems.org" gem "amazing_print"

    gem "debug" gem "http" gem "dry-monads" gem "pipeable" end Install alchemists.io/articles/ruby_function_composition alchemists.io
  84. Pipeable require "bundler/inline" gemfile true do source "https://rubygems.org" gem "amazing_print"

    gem "debug" gem "http" gem "dry-monads" gem "pipeable" end Install alchemists.io/articles/ruby_function_composition alchemists.io
  85. Pipeable require "bundler/inline" gemfile true do source "https://rubygems.org" gem "amazing_print"

    gem "debug" gem "http" gem "dry-monads" gem "pipeable" end Install alchemists.io/articles/ruby_function_composition alchemists.io
  86. Pipeable require "bundler/inline" gemfile true do source "https://rubygems.org" gem "amazing_print"

    gem "debug" gem "http" gem "dry-monads" gem "pipeable" end Install alchemists.io/articles/ruby_function_composition alchemists.io
  87. class Pinger include Pipeable def initialize client: HTTP @client =

    client end def call url pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status), :report end end Im plem entation alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  88. class Pinger include Pipeable def initialize client: HTTP @client =

    client end def call url pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status), :report end end Im plem entation alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  89. class Pinger include Pipeable def initialize client: HTTP @client =

    client end def call url pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status), :report end end Im plem entation alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  90. class Pinger include Pipeable def initialize client: HTTP @client =

    client end def call url pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status), :report end end Im plem entation alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  91. class Pinger include Pipeable def initialize client: HTTP @client =

    client end def call url pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status), :report end end Im plem entation alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  92. class Pinger include Pipeable def initialize client: HTTP @client =

    client end def call url pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status), :report end end Im plem entation alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  93. class Pinger include Pipeable def initialize client: HTTP @client =

    client end def call url pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status), :report end end Im plem entation alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  94. Pipeable class Pinger include Pipeable def initialize client: HTTP @client

    = client end def call url pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status), :report end end Im plem entation alchemists.io/articles/ruby_function_composition alchemists.io
  95. Pipeable class Pinger include Pipeable def initialize client: HTTP @client

    = client end def call url pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status), :report end end Im plem entation Domain Speci fi c Language (DSL) alchemists.io/articles/ruby_function_composition alchemists.io
  96. Pipeable class Pinger include Pipeable def initialize client: HTTP @client

    = client end def call url pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status), :report end end Im plem entation Domain Speci fi c Language (DSL) alchemists.io/articles/ruby_function_composition alchemists.io
  97. class Pinger include Pipeable # ... private attr_reader :client def

    get result result.fmap { |url| client.timeout(1).get url } rescue HTTP::TimeoutError => error Failure error.message end end Privates alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  98. class Pinger include Pipeable # ... private attr_reader :client def

    get result result.fmap { |url| client.timeout(1).get url } rescue HTTP::TimeoutError => error Failure error.message end end Privates 🎗:get alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  99. class Pinger include Pipeable # ... private attr_reader :client def

    get result result.fmap { |url| client.timeout(1).get url } rescue HTTP::TimeoutError => error Failure error.message end end Privates Monad alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  100. class Pinger include Pipeable # ... private attr_reader :client def

    get result result.fmap { |url| client.timeout(1).get url } rescue HTTP::TimeoutError => error Failure error.message end end Privates alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  101. class Pinger include Pipeable # ... private attr_reader :client def

    get result result.fmap { |url| client.timeout(1).get url } rescue HTTP::TimeoutError => error Failure error.message end end Privates Function Map alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  102. class Pinger include Pipeable # ... private attr_reader :client def

    get result result.fmap { |url| client.timeout(1).get url } rescue HTTP::TimeoutError => error Failure error.message end end Privates alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  103. class Pinger include Pipeable # ... private attr_reader :client def

    get result result.fmap { |url| client.timeout(1).get url } rescue HTTP::TimeoutError => error Failure error.message end end Privates alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  104. class Pinger include Pipeable # ... private attr_reader :client def

    get result result.fmap { |url| client.timeout(1).get url } rescue HTTP::TimeoutError => error Failure error.message end end Privates alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  105. class Pinger include Pipeable # ... private # ... def

    report(result) = result.fmap { |status| status == 200 ? "Site is up!" : status } end Privates alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  106. class Pinger include Pipeable # ... private # ... def

    report(result) = result.fmap { |status| status == 200 ? "Site is up!" : status } end Privates 🎗:report alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  107. class Pinger include Pipeable # ... private # ... def

    report(result) = result.fmap { |status| status == 200 ? "Site is up!" : status } end Privates Monad alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  108. class Pinger include Pipeable # ... private # ... def

    report(result) = result.fmap { |status| status == 200 ? "Site is up!" : status } end Privates alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  109. include Dry::Monads[:result] url = "https://xkcd.com" case Pinger.new.call(url) in Success(message) then

    puts "Success: #{message}" in Failure(error) then puts "Site is down or invalid. Reason: #{error}" end Execution alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  110. include Dry::Monads[:result] url = "https://xkcd.com" case Pinger.new.call(url) in Success(message) then

    puts "Success: #{message}" in Failure(error) then puts "Site is down or invalid. Reason: #{error}" end Execution alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  111. include Dry::Monads[:result] url = "https://xkcd.com" case Pinger.new.call(url) in Success(message) then

    puts "Success: #{message}" in Failure(error) then puts "Site is down or invalid. Reason: #{error}" end Execution Input alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  112. include Dry::Monads[:result] url = "https://xkcd.com" case Pinger.new.call(url) in Success(message) then

    puts "Success: #{message}" in Failure(error) then puts "Site is down or invalid. Reason: #{error}" end Execution Pipe alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  113. include Dry::Monads[:result] url = "https://xkcd.com" case Pinger.new.call(url) in Success(message) then

    puts "Success: #{message}" in Failure(error) then puts "Site is down or invalid. Reason: #{error}" end Execution alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  114. Checking: http://xkcd.com... Site is down or invalid. Reason: http://xkcd.com Input:

    http://xkcd.com Failure alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  115. Checking: https://www.unknown.com... Site is down or invalid. Reason: Timed out

    after using the allocated 1 seconds. Input: https://www.unknown.com Failure alchemists.io/articles/ruby_function_composition alchemists.io Pipeable
  116. pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status),

    :report The URL (raw input). alchemists.io alchemists.io/projects/pipeable Pipeable
  117. pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status),

    :report The URL (raw input). Print info to the console. alchemists.io alchemists.io/projects/pipeable Pipeable
  118. pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status),

    :report The URL (raw input). Print info to the console. Check if the URL is secure. alchemists.io alchemists.io/projects/pipeable Pipeable
  119. pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status),

    :report The URL (raw input). Print info to the console. Check if the URL is secure. Make the HTTP GET request. alchemists.io alchemists.io/projects/pipeable Pipeable
  120. pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status),

    :report The URL (raw input). Print info to the console. Check if the URL is secure. Make the HTTP GET request. Ask the HTTP response for status. alchemists.io alchemists.io/projects/pipeable Pipeable
  121. pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status),

    :report The URL (raw input). Print info to the console. Check if the URL is secure. Make the HTTP GET request. Ask the HTTP response for status. Report the HTTP status. alchemists.io alchemists.io/projects/pipeable Pipeable
  122. Pipe pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get,

    as(:status), :report alchemists.io alchemists.io/projects/pipeable Pipeable
  123. Pipe :get url tee check as :report ↓ ↓ ↓

    ↓ ↓ pipe url, tee(Kernel, :puts, "Checking: #{url}..."), check(/\Ahttps/, :match?), :get, as(:status), :report alchemists.io alchemists.io/projects/pipeable Pipeable
  124. Architecture :get url tee check as :report ↓ ↓ ↓

    ↓ ↓ alchemists.io alchemists.io/projects/pipeable
  125. Architecture :get url tee check as :report ↓ ↓ ↓

    ↓ ↓ Input alchemists.io alchemists.io/projects/pipeable
  126. Architecture :get url tee check as :report ↓ ↓ ↓

    ↓ ↓ Step Step Step Step Step alchemists.io alchemists.io/projects/pipeable
  127. Architecture :get url tee check as :report ↓ ↓ ↓

    ↓ ↓ Monad Monad Monad Monad Monad alchemists.io alchemists.io/projects/pipeable
  128. Architecture :get url tee check as :report ↓ ↓ ↓

    ↓ ↓ Pipe alchemists.io alchemists.io/projects/pipeable
  129. Architecture :get url tee check as :report ↓ ↓ ↓

    ↓ ↓ Pipe alchemists.io alchemists.io/projects/pipeable
  130. Architecture :get url tee check as :report ↓ ↓ ↓

    ↓ ↓ Pipe alchemists.io alchemists.io/projects/pipeable
  131. Architecture :get url tee check as :report ↓ ↓ ↓

    ↓ ↓ Pipe alchemists.io alchemists.io/projects/pipeable