Project

General

Profile

Actions

Feature #17353

open

Functional chaining operator

Added by fulcanelly (Maks Kompanienko) 11 months ago. Updated 11 months ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:101140]

Description

Since ruby already moving in that direction(functional), I would like to propose add to it OCaml-like chaining/pipe operator into ruby.
Which would allow such syntax

def handle(requests) = requests
  |> Array.filter { not _1.from.user.banned? }
  |> Array.map { _1 |> main_router.emit }
  |> Array.each &awaiter

What exactly happens here ?

Let's look at a bit easier example:

gets.to_i
|> make_stuff
|> format "the number is %d"
|> puts

Which is expands exactly to the code below

puts(format("the number is %d", make_stuff(gets.to_i)))

So what this operator does is nothing but just tricky form of AST building

Advantages:

  • Increase readability
  • It's more duck type-ish

Limitations:

  • cant be overloaded

Related issues

Related to Ruby master - Feature #15799: pipeline operatorClosedActions
Related to Ruby master - Feature #16794: Rightward operatorsOpenActions

Updated by zverok (Victor Shepelev) 11 months ago

"Let's take |> from Elixir" is proposed roughly once a month. The problem with this proposal is that it doesn't work well with Ruby syntax. Ruby's core chaining module is Enumerable ("core" in the sense it shows how the things are designed here), and core chainable item is block.

So let's look at your example:

def handle(requests) = requests
  |> Array.filter { not _1.from.user.banned? }
  |> Array.map { _1 |> main_router.emit }
  |> Array.each &awaiter

There is no way (besides "it is Elegant Because It Is Elegant In Elixir") how exactly it is better than

def handle(requests) = requests
  .filter { not _1.from.user.banned? }
  .map { main_router.emit(_1) }
  .each &awaiter

Now, this (one-item chaining):

gets.to_i
|> make_stuff
|> format "the number is %d"
|> puts

is handled by Object#then, existing since 2.6:

gets.to_i
  .then(&method(:make_stuff))
  .then { format "the number is %d", _1 }
  .then(&method(:puts))

Which is exactly "ideologically compatible" with how Enumerable chaining works, trivially implemented and place nice with all possible Ruby intuitions.

Now, method references and currying is another, sadder, story, but it would be sad with any chaining syntax.

Actions #2

Updated by matz (Yukihiro Matsumoto) 11 months ago

Actions #3

Updated by matz (Yukihiro Matsumoto) 11 months ago

Updated by nobu (Nobuyoshi Nakada) 11 months ago

Off topic:

fulcanelly (Maks Kompanienko) wrote:

def handle(requests) = requests
  |> Array.filter { not _1.from.user.banned? }
  |> Array.map { _1 |> main_router.emit }
  |> Array.each &awaiter

This made me want to disable fluent-dots in endless-def...

Actions

Also available in: Atom PDF