Ruby メタプログラミング 入門〜わかってたつもりだけどわかってなかった6選〜

## はじめに
これは私がRuby Goldを受験に向けて勉強する中で、主にメタプログラミングで役立ったこと・知らなかったことをまとめた記事になります。
何気なく使っていたことでも、勉強するうちに、じゃあこれは何でそうなるのか？これはどうやったら動くのか？と思うことが増えたのですが、学び理解することで以前に比べ明確な意思を持ってコーディングできるようになったと思います。
メタプロと言っても、その範囲は膨大なため、特に普段から使える、使っていたけどなんで動いてるのかわかってなかったものを~~５つ~~ 6つピックアップしてみました。（絞り込みたかったのに絞り込めませんでした:innocent:）
メタプロやったことない！という方や、メタプロ知ってるけど復習したい！という方の役に立てば幸いです。


## 環境
MacOS（irbが使える環境であれば問題ありません）
Ruby　2系（筆者の実行環境は `2.6.3`）


## メタプログラミングとは？
> メタプログラミングとは、言語要素を実行時に操作するコードを記述することである。

もっと端的に言うなら、 `コードを記述するためのコードを記述する`のがメタプログラミングです。
例えば`Active Record`は`ActiveRecord::Base`クラスを継承するだけで、実行時に各要素へのアクセサメソッドが定義されますよね。`ActiveRecord::Base`にはそういった定義がされていて、その定義の仕方こそが`コードを記述するためのコードを記述すること`なのです。

（※`Rails`で`model`を作る時、対象のModelは`ApplicationRecord`を継承させると思いますが、その`ApplicationRecord`は`ActiveRecord::Base`を継承しています。）

これをみるとなんとなく、`メタプロって自分で使うことは滅多にないんじゃない？`と思う方が出てくるかもしれません。少なくとも、学習当初は私自身がそう思っていました。
けれど、実際に学習すると、無意識のうちに自分がメタプログラミングを使用していたことに気付きます。`Ruby` という言語がメタプロと密接に関係しているからです。
今回は「メタプロっぽくないけど実はメタプロ」という項目について触れていきます。

## クラスについて
### 定数の相対参照時の探索順
定数は`Ruby`の厄介な要素の１つだと思っています。
何故なら、定数をなんとなくで使うと「一見取得できそうな値が取れない」という事象が多発するからです。
以下のソースを実行した時、コンソールには `Taro` と `Ichiro` どちらが表示されると思いますか。

```ruby

class Parent
  NAME = "Ichiro"

  def name
    NAME
  end
end

class Yamada < Parent
  NAME = "Taro"
end

puts Yamada.new.name
```

・
・
・
・
・
・
・
答えは `Ichiro` です。私は最初 `Taro` が表示されるのだと思っていました。
定数はインスタンスに存在するのではなく、クラスに存在します。
`#name` 内の `NAME` のような定数の書き方を相対参照と呼びます。
相対参照で参照した場合、探索対象の定数は、selfではなく定数を呼び出した、メソッド定義位置から探索が始まります。その為、`#name`が定義してある`Humanクラス`の`NAME = Ichiro`が呼び出されたというわけです。


以下の場合はどうでしょう。

```ruby
module Human
  module Infant
    GREET = 'Hello!'

    class Parent
      GREET = 'Hello, mum!'
    end

    class Yamada < Parent
      def greet
        puts GREET
      end     
    end
  end
end

Human::Infant::Yamada.new.greet
```

Yamadaくんはなんと挨拶すると思いますか。
実行して確認してみてください。

・
・
・
・
・
・

これは定数の相対参照時の探索順の問題になります。
定数の相対参照の場合、まずレキシカルスコープ、次に継承関係にあるクラス、といった順番で定数を探しにいきます。
レキシカルスコープとはソースコード上の物理的な位置のことです。難しいですね。
私的に分かりやすく言い換えると、 `自分自身の持っている情報` を確認した後に `祖先`を確認しにいく、ということです。
ここでいう自分自身とは `Human::Infant::Yamada` です。 `Human`, `Infant`, `Yamada` の中で該当する定数がないかをまず探します。
もし該当するものがレキシカルスコープ内にないのであれば、今度は継承関係のある祖先の中に定数を持っているものがいないかを探しにいきます。
以上の探索順から、 `Infant` に定義されている `GREET` の値が参照されるわけです。

定数には絶対参照や修飾つき参照もあるので、自信がない方は確認すると良いと思います。
ややこしいところはあれど、定数について理解してからは、定数で悩む機会が少なくなりました。

参考：[Rubyの定数が怖いなんて言わせない](https://qiita.com/fursich/items/a1b742795cf10eebc73f) (例題もあり、面白い記事でした。分かりやすかったです。例題が解けた時の気持ちよさは格別でした。)

### alias
`alias`。メソッドに別名をつける為に何気なく使っていましたが、エイリアスを付けた後の振る舞いについて深く考えたことがなかったのでその動きを確認した時驚きました。
以下のように書くことでメソッドに別名をつけることができます。

```ruby
class A
  def method_a
    puts 'A'
  end

  alias new_a method_a
end

A.new.new_a
```

では以下のように `method_a` を再定義した場合、 `method_a` と `new_a` の返り値はどうなるでしょうか。

```ruby
class A
  def method_a
    puts 'A'
  end

  alias new_a method_a

# 上記 method_a と同名の新しいメソッドを再定義
  def method_a
    puts 'B'
  end
end

A.new.method_a
A.new.new_a
```
`new_a` については `alias` を定義した時点の情報が保持されていることがわかります。
`method_a` を `undef` （メソッド定義を消す） しても、エイリアスを付けた方のメソッドは生き残ります。すごい。

じゃあaliasって何してんの？という話なんですけど、[こちら](https://docs.ruby-lang.org/ja/latest/doc/spec=2fdef.html#alias)に書いてありました。

>別名を付けられたメソッドは、その時点でのメソッド定義を引き継ぎ、元のメソッドが再定義されても、再定義前の古いメソッドと同じ働きをします。あるメソッドの動作を変え、再定義するメソッド で元のメソッドの結果を利用したいときなどに利用されます。

ほーん:open_mouth:


## ブロックについて
`yield`や`&修飾`って何となく使っていませんか？私はそうでした。仕組みを理解した上で使うと「何でそう動くか」ということが明確になります。

### yield
メソッドにブロックを渡すと、`yield`キーワードでブロックをコールバックできるのはみなさんご存知だと思います。
`yield`は引数を受け取ることができます。例えば、以下のようなメソッドがあったとします。

```ruby

def sample_method(x, y)
 x + yield(x, y)
end
```

以下のように実行するとどうなるでしょうか？

```ruby
sample_method(2, 4) { |a, b| (a + b) * 3 }
```
・
・
・
・
・

`2 + （(2 + 4) * 3）`の結果として20と表示されると思います。
ちなみにyieldで受け取る引数の数と、ブロック変数の数は異なっても問題がありません。

ブロックの存在は `Kernel#block_given?`メソッドを使用して確認することができます。
ブロックが存在しない場合に`yield`を使用するとエラーになります。実装時には気をつけなければなりません。

#### ところでブロックって結局何だ？
と、思ったことはありませんか。何気なく使っていたけれど、その実態を私は理解していませんでした。
> ブロックとは、メソッド呼び出しの際に引数と一緒に渡すことのできる処理のかたまりのことです。

と `たのしいRuby` には記載されています。
ブロックはオブジェクトではありません。ではブロックをオブジェクトとして扱いたい時はどうすればいいのでしょうか。

### Proc(lambda)
メソッドでブロックを受け取って `yield` で実行する以外にも、Procオブジェクトとしてブロックを持ち運び、任意の場所で実行することができます。
以下のソースを実行してみてください。

```ruby

say_word = Proc.new do |word|
  puts "say #{word}"
end

say_word.call('hello')
say_word.call('good-bye')
```
Procオブジェクトは、他のオブジェクトと同様にインスタンスを生成して使用します。インスタンス生成時にブロック内に記載した処理を、 `Proc#call` を使用した際に実行します。`call`メソッドの引数に渡した値はブロック引数として使用します。
※ちなみに以下のような書き方でも実行できます。

- say_word['hello']
- say_word.yield('hello')
- say_word.('hello')
- say_word === 'hello' （※引数がある時のみ）

### &修飾
では引数で受け取ったブロックを別のメソッドに渡したい時、どうしたらいいでしょうか。
ブロックを渡されたメソッド内で別メソッドを読んだ場合、別メソッドにもブロック渡されるんじゃない？と私は思っていました。
以下のコードを実行してみてください。実際にどうなるかがわかります。

```ruby

def sample_a
  if block_given?
    puts 'Yes:a'
  else
    puts 'No:a'
  end
  sample_b
end

def sample_b
  if block_given?
    puts 'Yes:b'
    yield
  else
    puts 'No:b'
  end
end

sample_a { puts 'I am a block' }
```

ブロックは渡されたメソッド内でのみ有効だということが分かります。
では渡されたブロックを別メソッドに渡したい時はどうしたらいいのでしょうか。
ここで役立つのが先ほど出てきた `Proc`オブジェクトです。
`&修飾`を使うことで受け取ったブロックを `Proc` オブジェクトとして使用することができます。(Procインスタンスが生成される＝＞メタ！)
以下の書き出しを元に、期待する実行結果が得られるように`sample_a`と`sample_b`を書き換えてみてください、

```ruby
def sample_a(&block)

# 期待値（以下のように出力される）
Yes:a
Yes:b
I am a block
```

・
・
・
・
・
・

答えは以下です。

```ruby

def sample_a(&block)
  if block_given?
    puts 'Yes:a'
  else
    puts 'No:a'
  end
  sample_b(&block)
end

def sample_b(&block)
  if block_given?
    puts 'Yes:b'
    block.call
  else
    puts 'No:b'
  end
end

sample_a { puts 'I am a block' }
```

こうすることでブロックをProcオブジェクトとして別メソッドに渡すことができるようになりました。
`block`はProcオブジェクトなので、実行するには`call`メソッドを呼ぶ必要があります。

この `&修飾` 、どこかで見覚えがありませんか。

```ruby
%w(a b c).map(&:upcase)

=> ["A", "B", "C"]
```
これです。
この形、結構よく使いますが、実際にどんな風に動いているかご存知でしょうか。
`&修飾` を使用していることからProcが関わっているということは察しがつくと思います。

```ruby

p = Proc.new {|word| word.upcase }
%w(a b c).map(&p)
```

こう記述すると今までの知識を元に行われていることをイメージすることができます。
では`＆修飾`にシンボルを渡した時はどうなるのでしょうか。

> to_proc メソッドを持つオブジェクトならば、`&' 修飾した引数として渡すことができます。デフォルトで Proc、Method オブジェクトは共に to_proc メソッドを持ちます。to_proc はメソッド呼び出し時に実行され、Proc オブジェクトを返すことが期待されます。

[メソッド呼び出し(super・ブロック付き・yield)](https://docs.ruby-lang.org/ja/2.2.0/doc/spec=2fcall.html#block)

シンボルは `to_proc` メソッドを持っています。

> self に対応する Proc オブジェクトを返します。
生成される Proc オブジェクトを呼びだす(Proc#call)と、 Proc#callの第一引数をレシーバとして、 self という名前のメソッドを 残りの引数を渡して呼びだします。

つまり、`(&:upcase)`は、実行されると動的にProcオブジェクトを生成し、それを実行していると言うことになります。つまりこれもまた黒魔術なのです。私は自覚なく黒魔術を１年使っていたということに気付いた時、とても驚きました。「Procなんていつ使うの？」なんて学習当初に思っていたのが恥ずかしいくらいです。凄く使っていました。

※ちなみに `&mehtod` も同じ理屈です。Procってすごい。

### 小ネタ
メタプロらしいメタプロがあんまりなかった気がしたので、私の中のメタプロといえばこれ、みたいなイメージをピックアップしました。

#### Module#define_method(メソッドの動的定義）
```ruby
class DefineMethodSample
  %w(a b c).each do |str|
    define_method str do
      puts str
    end
  end
end

# そのクラス内で定義されているインスタンスメソッドが配列で取得できる
DefineMethodSample.public_instance_methods(false)

DefineMethodSample.new.a
DefineMethodSample.new.b
DefineMethodSample.new.c
```

#### Object#send(メソッドの動的呼び出し)
```ruby
class SendSample
  def show_word(word)
    puts "It is a #{word}"
  end
end

# Object＃sendは動的にメソッドを呼び出せる。第２引数にメソッドに渡す引数を指定できる
SendSample.new.send(:show_word, 'cake')
```

### 最後に
何気なく使っていたことも、紐解いてみると黒魔術に塗れていたことがわかりました。
継承とかevalとか、実は苦しんだprivateメソッドについて省略してしまったので、別記事でまとめたいです。
まだまだ理解しきれていない部分も多いですが、メタプロってパズルみたいで面白いですね。
本当はなんちゃってRSpecを作る記事を書きたかったのですが、次回に持ち越します……。

## 参考
[メタプログラミング Ruby 第２版](https://www.amazon.co.jp/%E3%83%A1%E3%82%BF%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0Ruby-%E7%AC%AC2%E7%89%88-Paolo-Perrotta/dp/4873117437/ref=sr_1_1?adgrpid=55925379040&gclid=EAIaIQobChMIn5T40-jo5AIVT7aWCh29VwMpEAAYASAAEgJ5SvD_BwE&hvadid=338568618134&hvdev=c&hvlocphy=1009537&hvnetw=g&hvpos=1t1&hvqmt=e&hvrand=10038850096125477068&hvtargid=kwd-332399423896&hydadcr=27266_11561146&jp-ad-ap=0&keywords=%E3%83%A1%E3%82%BF%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0ruby&qid=1569306068&s=books&sr=1-1)
[たのしいRuby 第６版](https://www.amazon.co.jp/%E3%81%9F%E3%81%AE%E3%81%97%E3%81%84Ruby-%E7%AC%AC6%E7%89%88-Informatics-IDEA-%E9%AB%98%E6%A9%8B/dp/4797399848/ref=pd_sbs_14_8?_encoding=UTF8&pd_rd_i=4797399848&pd_rd_r=89ea6850-b9e6-431a-8a47-4ae8e19df66d&pd_rd_w=Yb85r&pd_rd_wg=iqA7C&pf_rd_p=ad2ea29d-ea11-483c-9db2-6b5875bb9b73&pf_rd_r=5SB392MGTSFSFZ9SYRFA&psc=1&refRID=5SB392MGTSFSFZ9SYRFA)