
Show HN: Elixir/Unix style pipe operations in Ruby - bonquesha99
https://github.com/LendingHome/pipe_operator
======
pmontra
I quote one of the examples

    
    
        "https://api.github.com/repos/ruby/ruby".pipe do
          URI.parse
          Net::HTTP.get
          JSON.parse.fetch("stargazers_count")
          yield_self { |n| "Ruby has #{n} stars" }
          Kernel.puts
        end
        #=> Ruby has 15115 stars
    

Not having to type the |> like in Elixir is two shift keys less, which is
good. I'm not sure about readability, because one has to spot the .pipe at the
beginning of the block, but it shouldn't be a problem.

Now, if we only had pattern matching with the exact syntax of Elixir and not
some monstrosity I saw around in proposals and other languages...

~~~
bonquesha99
Thanks for your feedback!

Check out this other proof of concept demonstrating ES6 style object
destructuring in Ruby:

[https://github.com/lendingHome/destruct](https://github.com/lendingHome/destruct)

I think this same type of concept could be applied to port Elixir style
pattern matching as well e.g.

    
    
        data = {
          name: "John Smith",
          age: 35,
          prefs: {
            lang: "en",
            tz: "UTC",
          }
        }
        
        User = Pattern { name age prefs[lang] }
        
        user = User =~ data
        user.name
        user.age
        user.lang
        
        [data].map(&User)
        
        case object
        when Pattern { some attrs[:nested][real][deep, fields] }
          Pattern!.some
          Pattern!.real
          Pattern!.deep
          Pattern!.fields
          Pattern!.nested #=> NoMethodError
        end
        
        # or define "locals" by defining temporary methods on
        # the block receiver when the "then" block is evaluated
        case object
        when Pattern { some nested[data] }.then do
          puts some
          puts data
        end

~~~
pmontra
Thanks. Destructuring is nice and I'll check your link.

I'm using Elixir's pattern matching also in function definitions, to do
without ifs and switches. A trivial example with Elixir syntax (hopefully
correct).

    
    
       def div(a, 0), do: {:error, "can't divide by 0"}
       def div(a, 1), do: {:ok, a}
       def div(0, b), do: {:ok, 0}
       def div(a, b), do: {:ok, a/b}
    

I'd like to have a pattern matching like that in Ruby as well. A more real
world example:

    
    
       def something(%{"a" => %{"b" => b, "c" => c}}), do {:ok, b + c}
       def something(_), do {:error, "a didn't contain b and c"}
    

Much more readable than having to write ifs inside the function/method.

All considered, I find Ruby a more pleasant language than Elixir because of
syntax, verbosity, state keeping (probably unfair, because OO is made for that
vs GenServers), deployment story. I'd like to keep using it for applications
that can scale by adding processes, but pattern matching is a killer feature.

Pipelines are important in Elixir because it's functional. It would be a pain
to code without them. Object oriented languages can somewhat pipeline by
calling methods on results of previous methods, provided a consistent design
of classes (each.map.uniq.sort). It seems that in Ruby it could remove lots of
useless variables. A lot less head scratching to find sensible variable names,
faster programming. I hope it gets into the language or that this gem gets
popular.

~~~
bonquesha99
Thanks! Agreed it feels like a good fit for Ruby as well!

RE the pattern matching stuff I think the destructuring concept could be
applied to look something like:

    
    
        def div(*args)
          case args
          when Pattern{[a, 0]} then [:error, "can't divide by 0"]
          when Pattern{[a, 1]} then [:ok, Pattern.last.a]
          when Pattern{[0, b]} then [:ok, 0]
          when Pattern{[a, b]} then [:ok, Pattern.last.a / Pattern.last.b]
          end
        end
    
        def something(object)
          case object
          when Pattern{a[b, c]} then [:ok, Pattern.last.b + Pattern.last.c]
          else [:error, "a didn't contain b and c"]
          end
        end
    

Some shorthand to access `Pattern.last` e.g. "$~" for regex would make things
even nicer!

