August 3, 2017 by Daniel P. Clark

Elixir Envy ᐅ Ruby

When a Rubyist learns about Elixir’s pipe operator and how it behaves similarly to Linux/Unix command line piping he, or she, very likely envies that feature and wishes to have it in Ruby.  How can I say that or how could I know that?  Well it’s a reasonable deduction when I see others, as well as myself implementing this behavior in Ruby as a gem.

Surprisingly just one implementation doesn’t seem to cut it.  Maybe, like myself, an implementation wasn’t discovered before building the gem.  Or maybe the implementation wasn’t what was desired.  Who knows.  This post is just a brief mention and overview of those projects.  We’ll start from the most recent to the oldest Ruby gems of Elixir’s pipe operator.

pipe_envy

pipe_envy by hopsoft on Github is a monkey patch to redefine the standard pipe operator to behave more like Elixir/Linux.  This can have unwanted side effect on Ruby objects such as Array and Integer as the methods been re-purposed.  Consider this project more of a “for fun” project to play around with.  Here’s an example usage from the Github repo:

magic = (1..100) \
  | :to_a \
  | [:select!, -> (i) { i.even? }] \
  | [:map, -> (i) { i ** 10 }] \
  | :sum \
  | Math.method(:sqrt) \
  | :to_s \
  | :chars \
  | :reverse \
  | [:[], 3] \
  | :to_i

# => 7

Notice that methods which require arguments are piped as Arrays.

elixirize

elixirize by me danielpclark on Github is an implementation that was intended to stay true to the spec Elixir had for it’s pipe operator |> but as that’s not valid Ruby code I took the Unicode character which closely resembles it as the new pipe operator for Ruby.  The Github README teaches how to type this character across different operating systems and text editors.

The Elixir documentation clearly states that what’s on the left of the pipe operator gets passed into the method on the right as its first parameter.  That’s not complicated to do… to implement it with Procs I wrote it as:

def ᐅ other, *args, &block
  other.call(self, *args, &block)
end

I chose to use Proc format as that is easier to create and pass around within limited scopes.  When using the gem you can write code in this manner:

def add a, b
  a + b
end

subtract = ->a, b{ a - b }

add(4, 5).ᐅ subtract, 15
# => -6

And if you want to use regular methods as a Proc you can use the method method.

def divide a, b
  a / b
end

add(40, 60).ᐅ method(:divide), 20
# => 5

I actually prefer the clarity this provides as it’s reads a little bit more like English.  You can of course chain the methods with the pipe operator.  I’ve aliased the tilde (~) to to_proc on Symbol to allow shorthand use of method calls directly on the object to be called from the left side of the pipe operator.

s = "a b c d"
val = s.
  ᐅ(~:split, " ").
  ᐅ(~:join, "-").
  ᐅ ~:capitalize

val
# => "A-b-c-d"

piped_ruby

piped_ruby by tiagopog on Github has simply implemented a shovel operator on Proc to continue passing the value forward with an unwrap method to return the value.

-> { 'Pipe' }
  .>> { |e| "#{e} things"  }
  .>> { |e| "#{e} in Ruby!" }
  .unwrap #=> "Pipe things in Ruby!"

I kind of like this in its own respect.  I might feel more inclined to use do and end rather that curly braces with naming the input for the context.  I think that this can be used in such a way where it is quite readable and easy to understand the flow of the code.

chainable_methods

chainable_methods by akitaonrails on Github was the first well known gem of this kind to publish the Elixir pipe operator but he wasn’t the first to publish code for the idea.  piped_ruby above took its inspiration from this gem and uses the same kind of behavior of wrapping all results in an object and returning a result when unwrap is called.  Although where piped_ruby uses a Proc object chainable_methods uses it’s own object wrapper.

The implementation is a bit elaborate underneath but it’s super simple to use.  There is a CM method globally available on Object which wraps the first item and method calls work as if it’s the inner object but returning the wrapped chainable method object until you finally call unwrap.  He’s also added a chain method to insert additional transformations within the method call chain.

include Nokogiri
CM("hello http:///www.google.com world")
  .URI.extract.first
  .URI.parse
  .chain { |uri| open(uri).read }
  .HTML.parse
  .css("h1")
  .first.text.strip
  .unwrap

This doesn’t look like or behave like Elixir’s pipe operator since it clearly lacks the “passing the object from the left as the first parameter on the right” rule and it uses standard method call behavior.  But it does allow continuous method chaining without hand written variable assignments.

The author has also given a conference presentation on this gem.

pipeable.rb

pipeable.rb by pcreux on Github Gist published a working pipeline implementation that is more transformation focused than becoming part of the core of Rubys operators.  It works by letting you define your transformations as Procs from which you can put in any point in a method pipe chain.  Here are some example transformations (excerpts from the gist).

Reverse = ->(string) { string.reverse }
Leet    = ->(string) { string.gsub(/[aeiost]/,'a'=>'4','e'=>'3','i'=>'1','o'=>'0','s'=>'5','t'=>'7') }
Mooify  = ->(string) { "Cow said: " + string }
Say     = ->(string) { system %|say "#{string}"|; string }

TweetTo = Struct.new(:recipient) do
  def call(input)
    puts %|Tweeting "#{input}" to #{@recipient}!|
    input
  end
end

And a few ways to use them.

# We make the first element pipable and we can then just pipe through!
result = pipable("moo") | Reverse | Leet | Mooify | :downcase | TweetTo.new('@pcreux') | { delete: 'o' }
puts result
# => cw said: 00m

# Pipe without defining any `|` method
puts pipe("moo", Mooify, :upcase)
# => COW SAID: MOO

# Multi-Multiplexing... let me tell you...
p Pipeline.new(Mooify, [:downcase, :upcase], Reverse, [:reverse, Leet]).call("moo")
# => [["cow said: moo", "00m :d145 w0c"], ["COW SAID: MOO", "OOM :DIAS WOC"]]

Blog: Elixir Pipes in Ruby

The Elixir Pipes in Ruby blog by Mo Lawson describes implementing Elixir’s pipe operator in Ruby in a similar way to what chainable_methods ended up implementing it — namely to wrap it in an object.

Monads

Whether your going for more aesthetic code or an implementation design, your choice of how you to go about getting this behavior may vary from others.  Before we were envious of Elixir we still had design patterns which allow us to implement the same kind of behavior.  The chief pattern in my mind that fits the bill of passing the value from the left as the first parameter on the right is monads which keeps a common protocol on how methods get called (much like Proc).

When I first watched a presentation of what monads are it seemed very complex because the examples are a bit elaborate.  It takes time and hands on experience to get comfortable with them as well as being a bit more seasoned as a developer.  But you don’t need to take my word on that.  Here’s the conference talk which first introduced me to monads by Tom Stuart.  It really is a well made and given presentation.

Summary

Piping is a common thing many of us do on the command line day to day so it’s nice to be able to work with what’s familiar to us in the same way we expect it to behave.  For this reason pipes are a very desirable thing to have in a language.  But they don’t make a language better, it’s simply a tool which developers would enjoy having as an option.

Perhaps the best Ruby implementation is yet to come?  Or maybe one of these will suit you better than any other.  What it should come down to is what benefit it provides you in helping your code have more clarity, more modularity, or easier to work with code base.  Choose the values you wish to prioritize and write great code.

I hope you enjoyed this blog post! I look forward to your feedback. Remeber to comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan .

God Bless!
-Daniel P. Clark

Image by Phoebe via the Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Generic License

#elixir#envy#monad#operator#pipe#pipe operator#proc#ruby

Comments

  1. zverok
    August 4, 2017 - 4:19 pm

    Nice compendium! Mostly proofs that envy without trying to be Ruby-idiomatic leads to any cool trick possible, just not useful in any way 🙂

    It is a pity that it lacks mentioning yield_self, already introduced in Ruby 2.5 and being exactly the “Ruby way of implementing Elixir pipe”:

    "hello http:///www.google.com world"
      .yield_self(&URI.method(:extract)).first
      .yield_self(&URI.method(:parse))
      .yield_self { |uri| open(uri).read }
      .yield_self(&HTML.method(:parse))
      .css("h1")
      .first.text.strip
    
    • Daniel P. Clark
      August 7, 2017 - 11:24 am

      Thank you for sharing that. I’d seen yield_self but had never seen it shown to work in that way. That’s great!

      I’ve pointed out in other posts that I thought yield_self was a terrible thing to add because you could just alias that to instance_eval and get the same result. But I was corrected by being told that yield_self has access to the outer context as its binding rather than the inner context of the instance.

      • Csaba
        January 8, 2020 - 4:36 am

        Also with Ruby 2.6 yield_self got aliased to then, which is much less burdening to type and read, especially if used in abundance.

Leave a Reply

Your email address will not be published / Required fields are marked *