Method#to_procを利用して、ブロックを省略する方法

以下の`&method`部分の意味が分からなかったので調べてみました。

```ruby
['foo', 'bar', 'baz'].each(&method(:puts))
```

今回理解したいコードはこちらです。

```ruby
class Hoge
  def positive_even_list
    even_filter(list)
      .then(&method(:positive_filter))
  end

  def list
    [10, -5, 0, 21, -30]
  end

  def even_filter(list)
    list.filter(&:even?)
  end

  def positive_filter(list)
    list.filter(&:positive?)
  end
end


Hoge.new.positive_even_list
# => [10]
```

## まとめ

- Procオブジェクトの前に`&`プレフィックスをつけると、ブロックとして扱われる
- Procオブジェクトではなくても、`to_proc`メソッドが定義されているオブジェクトであれば、暗黙的にto_procを呼び出してProcオブジェクトへ変換される
- これを使うことで、ブロックを省略することができる
- `Method#to_proc`と`Symbol#to_proc`が便利

## サンプルコード1 Symbol#to_proc

最初の例の`&method`を理解する前に、比較的よく見る、Symbolのケースを使って説明していきます。

```ruby
['foo', 'bar', 'baz'].map(&:upcase)
=> ["FOO", "BAR", "BAZ"]
```

このような使い方を目にすることがあると思います。

[Array#each (Ruby 3.1 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/method/Array/i/each.html) を見ると、eachは`each {|item| .... } -> self`のように、ブロックを受け取り、各要素に対してブロックを評価するようです。

上記の書き方はブロックが省略されていますが、省略しないとこのようになります。

```ruby
# ['foo', 'bar', 'baz'].map(&:upcase)
['foo', 'bar', 'baz'].map { |item| item.upcase }
=> ["FOO", "BAR", "BAZ"]
```

よく見るものの、どうしてこうなるのかが理解できていなかったのですが、実はこの変換には、[Symbol#to_proc](https://docs.ruby-lang.org/ja/latest/method/Symbol/i/to_proc.html)が使われていました。

なぜ急にto_procが出てきたかを理解するために、上の例のブロック部分をProcオブジェクトに置き換えた例を見てみます。

```ruby
# ['foo', 'bar', 'baz'].map { |item| item.upcase }
upcase_proc = lambda { |item| item.upcase }
['foo', 'bar', 'baz'].map(&upcase_proc)
```

Procオブジェクトの前に &プレフィックスをつけると、ブロックとして扱うことができます。
ブロックはオブジェクトではないのですが、Procオブジェクトにすることで、オブジェクトとして引数に渡したりすることができます。
上記の例では、 `upcase_proc`というProcオブジェクトに対して&プレフィックスをつけたことで、ブロックとして扱うことができ、mapにブロックを渡すことができていました。

```ruby
upcase_proc.class
=> Proc
```

ただ、先程の例では&プレフィックスのあとにあったのはProcオブジェクトではなく、Symbolでした。

```ruby
['foo', 'bar', 'baz'].map(&:upcase)
```

このとき、&プレフィックスの後ろのオブジェクトに`to_proc`メソッドが定義されていると、暗黙的に呼ばれて変換されるようになっています。

明示的に記述すると、

```ruby
['foo', 'bar', 'baz'].map(&(:upcase.to_proc))
```

[Symbol#to_procのリファレンスマニュアル](https://docs.ruby-lang.org/ja/latest/method/Symbol/i/to_proc.html)には以下のように書かれています。 

> self に対応する Proc オブジェクトを返します。
> 
> 生成される Proc オブジェクトを呼びだす(Proc#call)と、 Proc#callの第一引数をレシーバとして、 self という名前のメソッドを残りの引数を渡して呼びだします。
> (中略)
> ```ruby
> :to_i.to_proc["ff", 16]  # => 255 ← "ff".to_i(16)と同じ
> ```

Symbol#to_procにより、第1引数をレシーバとするProcオブジェクトになります。

これによって、SymbolがProcオブジェクトになり、&プレフィックスによってブロックとなり、mapでブロックが評価されています。

```ruby
# ['foo', 'bar', 'baz'].map(&:upcase)

['foo', 'bar', 'baz'].map { |item| item.upcase }
```

## サンプルコード2 Method#to_proc

`&method`について理解を深めるために、シンプルな例として以下を用います。

```ruby
['foo', 'bar', 'baz'].each(&method(:puts))
```

とは言っても先程の例と同様です。
&プレフィックスがあるため、以下のように暗黙的にto_procが呼ばれ、Procオブジェクトに変換されています。

```ruby
['foo', 'bar', 'baz'].each(&method(:puts).to_proc)
```

[Method#to_proc (Ruby 3.1 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/method/Method/i/to_proc.html)を見てみると、

> self を call する Proc オブジェクトを生成して返します。

とあります。つまり、以下のような意味になります。

```ruby
method_proc = lambda { |arg| method(:puts).call(arg) }
['foo', 'bar', 'baz'].each(&method_proc)
```

この`method`メソッドは、[Object#method](https://docs.ruby-lang.org/ja/latest/method/Object/i/method.html)で、

> オブジェクトのメソッド name をオブジェクト化した Method オブジェクトを返します。

とあります。メソッドもブロックと同じようにオブジェクトではないのですが、Methodオブジェクトにすることでオブジェクトとして扱うことができます。
例えば、`'foo bar'.reverse`をMethodオブジェクトにすると、

```ruby
reverse_method = 'foo bar'.method(:reverse)
=> #<Method: String#reverse()>
reverse_method.call
=> "rab oof"
```

となります。よって、

```ruby
# ['foo', 'bar', 'baz'].each(&method(:puts))
# ↓
# ['foo', 'bar', 'baz'].each(&method(:puts).to_proc)
# ↓
# ['foo', 'bar', 'baz'].each(&lambda { |arg| method(:puts).call(arg) })
# ↓
['foo', 'bar', 'baz'].each(&lambda { |arg| puts(arg) })

# ↓

['foo', 'bar', 'baz'].each { |arg| puts(arg) }
```

というように変換できます。

### Symbol#to_procとMethod#to_procの使い分け

使い分けという程ではないですが、こんがらがりそうなので、並べてみると、

Symbol

```ruby
# ['foo', 'bar', 'baz'].map(&:upcase)
['foo', 'bar', 'baz'].map { |item| item.upcase }
```

Method

```ruby
# ['foo', 'bar', 'baz'].each(&method(:puts))
['foo', 'bar', 'baz'].each { |item| puts(item) }
```

となり、Symbolはそのitemをレシーバとして呼び出すとき、Methodは引数として呼び出す時に使えます。

## 最初のサンプルコード

ここまで来るともう読めるはずです。

```diff_ruby
class Hoge
  def positive_even_list
    even_filter(list)
      .then(&method(:positive_filter))
+#    .then { |list| positive_filter(list) }
  end

  def list
    [10, -5, 0, 21, -30]
  end

  def even_filter(list)
    list.filter(&:even?)
+#  list.filter { |item| item.even? }
  end

  def positive_filter(list)
    list.filter(&:positive?)
+#  list.filter { |item| item.positive? }
  end
end


Hoge.new.positive_even_list
# => [10]
```

このように使うことができます。

## まとめ

- Procオブジェクトの前に`&`プレフィックスをつけると、ブロックとして扱われる
- Procオブジェクトではなくても、`to_proc`メソッドが定義されているオブジェクトであれば、暗黙的にto_procを呼び出してProcオブジェクトへ変換される
- これを使うことで、ブロックを省略することができる
- `Method#to_proc`と`Symbol#to_proc`が便利

## おまけ

`&method`のように、`&`と`method`が一体となっているわけではないので、以下のようにも使えます。

```ruby
require 'json'

'{"hoge":"fuga"}'
  .then(&JSON.method(:parse))
=> {"hoge"=>"fuga"}
```

JSONを取得してパースするときにブロックを省略して書くこともできます。 (ブロックを書いたほうがわかりやすいです)

## 参考

- [Symbol#to_proc (Ruby 3.1 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/method/Symbol/i/to_proc.html)
- [Method#to_proc (Ruby 3.1 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/method/Method/i/to_proc.html)
- [プログラミング言語 Ruby | まつもと ゆきひろ, David Flanagan, 卜部 昌平 (監訳), 卜部 昌平 (監訳), 長尾 高弘 |本 | 通販 | Amazon](https://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E-Ruby-%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8-%E3%82%86%E3%81%8D%E3%81%B2%E3%82%8D/dp/4873113946)