1. 16
  1. 8

    Good writeup, but I wouldn’t recommend using this approach at work.

    Fwiw, I did a bunch of stuff like this a few years into my ruby career, and saw others do them too, and after working on a medium sized ruby team with normal dev turnover for 4 years or so concluded that refinements, as well as everything else that changes the core language, should be entirely avoided. The small enhancement in readability is not worth the extra cognitive load on newer devs, and the increased chance of someone making a mistake.

    Better to just embrace the core, boring, vanilla language.

    1. 6

      I am inclined to agree, mostly because while it’s cute, it doesn’t really stand to support errors in a meaningful capacity (ie. the “railroad” style of error handling). Unless you really warp and change what kind of data your functions are taking in/returning, you’re only scratching the surface of what Haskell and F# can do. Ruby I feel is more suited to method chaining with something like [1,2,3].map{|x| x+1 }.filter{|x| x.even? } which is probably a better way of concreting complex business logic.

    2. 4

      Here’s a way of writing a pipeline in Ruby that is more verbose but more idiomatic than the article’s solution. It uses Kernel#then (formerly Object#then):

      def find_by_login(login) = …
      def confirm_user_account(user) = …
      def send_confirmation_notification(user) = …
      
      "johnsmith"
        .then &method(:find_by_login)
        .then &method(:confirm_user_account)
        .then &method(:send_confirmation_notification)
      

      With this approach, you can mix and match pipeline steps with normal Ruby that has not been written as pipeline steps:

      find_by_login("johnsmith")
        .then &method(:confirm_user_account)
        .then { |user| … } # send confirmation notification
      
      1. 4

        This is an interesting experiment, but in practice I would strongly recommend using dry-monads for this behavior because of the additional error-handling.

        I like the use of Refinements here, I think it’s a very underrated feature of Ruby, although beware that using them in a block is quite slow on >= 3.0. Using them file-level or class-level performs fine.