pipeline operatorが使われているコードを読み解いてミタァ！

## はじめに
2019/6/13、Ruby 2.7にpipeline operatorが入りました。
discussionが盛り上がっててTLでも各Rubyist達の様々な意見が飛び交ってます。
そんな中、@hanachin_さんのツイートを見かけました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">よすぎて何回も眺めている<br><br>&quot;<a href="https://t.co/8sUOXpGLwt">https://t.co/8sUOXpGLwt</a>&quot;.:itself<br>  |&gt;&gt;&gt; URI.:parse<br>  |&gt;&gt;&gt; Net::HTTP.:get<br>  |&gt;&gt;&gt; JSON.:parse<br>  |&gt; call<br>  |&gt; fetch(&quot;stargazers_count&quot;)<br>  |&gt; then { puts <a href="https://twitter.com/1?ref_src=twsrc%5Etfw">@1</a> }</p>&mdash; Miyagi (@hanachin_) <a href="https://twitter.com/hanachin_/status/1139406434846695430?ref_src=twsrc%5Etfw">2019年6月14日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

Ruby 2.7から追加される機能が積極的に使われていてすごく勉強になりそうだったので、まとめてみました。
認識間違っている・不足している箇所もあるかもしれないのでその際は教えてください。
何分はじめてのQiita記事なのでお手柔らかにお願いします。:pray:

## Rubyのバージョン
2.7.0-dev

※2019/6/4時点でまだ2.7は開発中なので、2.7で新しく追加される機能の挙動は今後この記事とは変わってくる可能性があります。

## 本編
### このコードは何をしているの？
まず@hanachin_さんのコードと下記のコードは等価です。

```ruby
puts JSON.parse(Net::HTTP.get(URI.parse("https://api.github.com/repos/ruby/ruby"))).fetch("stargazers_count")
```

URLにGETでリクエストしてJSONを取得、そこから`stargazers_count`を取得して出力する、という処理です。

### このコードを読むために必要な知識
#### 従来からある機能
- `Object#itself`
- `Method#>>`
- `Object#then`

#### 2.7の新機能
- `.:`
- pipeline operator
- Numbered parameters

### 1行目
```ruby
"https://api.github.com/repos/ruby/ruby".:itself
```

#### Object#itself
`self`、つまりレシーバ自身を返します。
ここでは`"https://api.github.com/repos/ruby/ruby"`です。

参考：[Object#itself](https://docs.ruby-lang.org/ja/latest/method/Object/i/itself.html)

#### .:fuga
`method`メソッドのショートハンドです。
`hoge`の`fuga`という`Method`オブジェクトが欲しい場合は

```ruby
hoge.:fuga
```
と書けます。

参考：[Ruby 2.7の新機能メソッド参照演算子](https://qiita.com/hanachin_/items/1aa1ba38a87dee91aed6)

#### 1行目と等価なコード
```ruby
"https://api.github.com/repos/ruby/ruby".method(:itself)
```

### 2行目〜5行目
```ruby
|>>> URI.:parse
|>>> Net::HTTP.:get
|>>> JSON.:parse
|> call
```

#### pipeline operator
`|>`のことです。
メソッドの後の`()`が省略できる以外は`.`でメソッド呼び出すのと同じです。

#### Method#>>
`>>`の場合は左辺の`Method`オブジェクトの戻り値を引数として右辺を実行します。
`<<`の場合は右辺の戻り値を引数として左辺の`Method`オブジェクトを実行します。
なので2行目〜5行目のコードは下記のようなイメージです。

```ruby
|>>> URI.:parse      # URI.parse(1行目の戻り値)を実行
|>>> Net::HTTP.:get  # Net::HTTP.get(URI.parseの戻り値)を実行
|>>> JSON.:parse     # JSON.parse(Net::HTTP.getの戻り値)を実行
```

参考：[Ruby 2.6 の変更点 - Method と Proc](https://tmtms.hatenablog.com/entry/201812/ruby26-method)

#### 5行目までと等価なコード
```ruby
-> {
  JSON.parse(Net::HTTP.get(URI.parse("https://api.github.com/repos/ruby/ruby")))
}.call
```

### 6行目
```ruby
|> fetch("stargazers_count")
```

`Hash#fetch`で5行目までで取得したJSONの`stargazers_count`プロパティを取得しています。

#### 6行目までと等価なコード
```ruby
JSON.parse(Net::HTTP.get(URI.parse("https://api.github.com/repos/ruby/ruby"))).fetch("stargazers_count")
```

### 7行目
```ruby
|> then { puts @1 }
```

#### Object#then
`self`をブロック引数に渡してブロックを評価し、ブロックの評価結果を返します。
ここでは6行目で取得したJSONのプロパティ`stargazers_count`が`self`となります。

参考：[Object#then](https://docs.ruby-lang.org/ja/latest/method/Object/i/then.html)

#### Numbered parameters
ブロック引数の定義を省略して`@1`、`@2`、`@3`... に多重代入できます。
ここでは`@1`には6行目の戻り値が入ってきます。

参考：[Ruby 2.7 の Numbered parameters を試す](https://autopp-tech.hatenablog.com/entry/2019/03/19/095500)

#### 最終的な等価なコード
```ruby
puts JSON.parse(Net::HTTP.get(URI.parse("https://api.github.com/repos/ruby/ruby"))).fetch("stargazers_count")
```

### Ruby 2.7での追加機能超便利！
Ruby 2.7での追加機能、使いこなせれば気持ちよくコードが書けそうですね！
勉強にもなったしめでたしめでたし！・・・ではなかった:scream:

### 演算子がNG
[このコミット](https://github.com/ruby/ruby/commit/d365fd5a024254d7c105a62a015a7ea29ccf3e5d)でpipeline operatorの後に`+`や`-`等の演算子を使えなくなりました。
使えなくなった演算子一覧は[ここ](https://github.com/ruby/ruby/blob/d365fd5a024254d7c105a62a015a7ea29ccf3e5d/parse.y#L2013)のコードで確認できます。(`op`にあたる演算子が使えなくなりました)

※なぜpipeline operationの後に演算子がきてはダメなのか、理由はわからなかったのでQuoraで質問してみてるので返答くれば追記します。

#### 2019/6/15 追記
@yukihiro_matzから返答いただけました！
[Ruby2.7のpipeline operatorで+や-等の演算子をはじいたのはなぜなんでしょうか？](https://qr.ae/TWhH1l)

読みやすくない、むしろ読みにくいというのが主な理由とのことでした。

### その結果
こうなりました。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ふつうにこれで動くしよいのではという気持ちになってきた<br><br>&quot;<a href="https://t.co/8sUOXpGLwt">https://t.co/8sUOXpGLwt</a>&quot;.:itself &gt;&gt; URI.:parse &gt;&gt; Net::HTTP.:get &gt;&gt; JSON.:parse<br>  |&gt; call<br>  |&gt; fetch &quot;stargazers_count&quot;<br>  |&gt; then { puts  <a href="https://twitter.com/1?ref_src=twsrc%5Etfw">@1</a><br>}</p>&mdash; Miyagi (@hanachin_) <a href="https://twitter.com/hanachin_/status/1139547632974516224?ref_src=twsrc%5Etfw">2019年6月14日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#### pipeline operatorは()でラップしている？
上記のコードで`call`を`.`で呼び出すと思い通りの挙動になりません。
関数合成した結果ではなく`JSON.:parse`をレシーバにして`call`を呼び出してしまうからです。
一方、pipeline operatorだと思い通りの挙動をするので、下記のようにレシーバを`()`で囲って評価されてるのかな？という気がしてます。(コード読んだわけではないので確証はないです:sweat:)

```ruby
("https://api.github.com/repos/ruby/ruby".:itself >> URI.:parse >> Net::HTTP.:get >> JSON.:parse).call
```

## 最後に
pipeline operatorは賛否両論ありますが、Rubyistが幸せになれる結果になるといいですね:thinking:
また、@hanachin_さんのコードのおかげで非常に勉強になりました、@hanachin_さんありがとうございます:bow: