「FizzBuzzクイズ」といふものするなり

元ネタ: [「FizzBuzzクイズ」クイズ－Ruby編](https://qiita.com/sumim/items/f26902f2b813c075a619)

## まずはコードから

と言うことで私が書いたコードは下記になりました。Ruby 2.4.0以上で実行してください。古いRubyではうまくいかない部分があります。

```rb
# frozen_string_literal: true

require 'delegate'

class NamedInteger < DelegateClass(Integer)
  NAME_MAP = {
    fizz: 3,
    buzz: 5,
    pezz: 7,
  }.freeze

  def initialize(n, name = nil)
    super(n)
    @name = name&.-@()
    freeze
  end

  def to_s
    @name || super
  end

  def inspect
    @name&.inspect || super
  end

  NAME_MAP.each do |key, value|
    define_method(key) do
      if (self % value).zero?
        NamedInteger.new(to_i, @name.to_s + key.to_s.capitalize)
      else
        self
      end
    end
  end
end

module IntegerNameable
  refine Integer do
    NamedInteger::NAME_MAP.each_key do |key|
      define_method(key) do
        NamedInteger.new(self).__send__(key)
      end
    end
  end
end

using IntegerNameable

p 1.fizz.buzz #=> 1
p 3.fizz.buzz #=> "Fizz"
p 5.fizz.buzz #=> "Buzz"
p 15.fizz.buzz #=> "FizzBuzz"
p 15.buzz.fizz #=> "BuzzFizz"

p 7.fizz.buzz.pezz #=> "Pezz"
p 21.fizz.buzz.pezz #=> "FizzPezz"
p 35.fizz.buzz.pezz #=> "BuzzPezz"
p 105.fizz.buzz.pezz #=> "FizzBuzzPezz"
p 105.fizz.pezz.buzz #=> "FizzPezzBuzz"
p 105.pezz.buzz.fizz #=> "PezzBuzzFizz"

p 1.fizz.buzz.pezz #=> 1
p 3.fizz.buzz.pezz #=> "Fizz"
p 5.fizz.buzz.pezz #=> "Buzz"
p 15.fizz.buzz.pezz #=> "FizzBuzz"
p 15.buzz.fizz.pezz #=> "BuzzFizz"
p 104.fizz.buzz.pezz #=> 104
```

以下、**妄想**です。

## 元ネタで思ったこと

元ネタの回答を見たときに思ったことは三つほどです(三つとはいっていない)。

1. Stringにインスタンス変数を追加するのはいかがなものなのだろうか？
2. 同じようなコードが2回以上ある。世の中はDRYでなければならない。
3. pezz実装に5行(実質3行)は多すぎな気がする。
4. モンキーパンチと言えばルパン三世だろ。[^1]

[^1]: たぶん、monkey patch(モンキーパッチ)とrefine(リファイン)をかけたダジャレを言いたかっただけだと思う。

これらを無理矢理解決していきたいと思います。

## 解説っぽい何か

### 名前付き整数というもの

まずは、一枚目。ドロー、新たなクラス！Integerを委譲して追加メソッド！

冗談です。始めに`NamedInteger`を解説します。

さて、`3.fizz`としたら`"Fizz"`な何かになるとの話でした。これは果たして整数なのでしょうか文字列なのでしょうか？`"Fizz"`と言っているのだから文字列ではありそうですが、その後の`buzz`などのメソッド考えると整数のままとも考えられます。概念的にはどちらが主であるかとすると私は整数を主としました。なぜなら、もともとそれは`3`だったのだから、相変わらず`3`であるべきだし、ただ普通の`3`とは違うのは、ひとたび`p`とか`puts`とかしたら`"Fizz"`になるというだけだからです。

そこで作ったのが**名前付き整数**クラス`NamedInteger`です。この名前付き整数であれば、`"Fizz"`という名前がついた`3`が実現できます。もちろん無名の場合もありますが、そのときは通常の`3`と同じです。何と言ってもその特徴は、`Integer`を委譲しているので、普通の整数として扱えます。ただ違うのは、`to_s`と`inspect`です。つまり、文字列にしたいときは名前があれば名前を出すようにしますし、それが`inspect`で本質も見たいのであれば、やはりそれも名前があれば名前にします。最後に、通常の整数と同じくfreezeしておきます。これが名前付き整数です。

あとは、このクラスに`fizz`なりなんなりを実装すればメソッドチェーンでどんどん渡せます。

まぁ、ぶっちゃけ私としては、`p`で確認することがおかしいんじゃないのかと思っていたりもするのですが。

### 君はDRYなやつだな

世の中にはすごく厳しい人がいます。コードレビュー時に

「この行とこの行で同じ事している、やり直し」

とバスバス切り刻んでくる人です。そういう人を**DRYな人**と言います[^2]。ということで似たような処理はまとめて書きます。メッソド定義でもなんでも動的にサクサクとできてしまうRubyにかかれば、同じ事をまとめて書くのなんて朝飯前です[^3]。`Hash`でまとめて、`each`で回して、`define_method`で定義すれば良いだけですから。

[^2]: 信用しちゃ駄目だぞ。単に[DRY(Don't repeat yourself)原則](https://ja.wikipedia.org/wiki/Don%27t_repeat_yourself)にかけているだけですから。

[^3]: この記事は、夕飯喰って風呂入り終わった寝る前に投稿しているけどな。

### DRYなやつのおかげでした

先ほどまとめたおかげで一つ利点が生まれました。pezzについてはたった一行で済むと言うことです。9行目の`pezz: 7,`が全てです。この後さらに何でも一行で足せます。本当に最小限。

### refineとusingが織り成す奇跡を見よ

君はRefinementsの栄光を知っているのか？知らない、ならば今すぐ[Rubyマガジンに書いてある愚痴](http://magazine.rubyist.net/?0041-200Special-refinement)を読むべきだ。

2.0で追加されるはずだったが先延ばしされて、2.1から正式導入されたのがRefinementsです。これを語るにはRubyの特徴から言う必要があります。

Rubyは**とてつもなく**動的です。動的型付けだからとかそういう話ではなく、その仕組みが動的なのです。特にクラスは、オープンクラスと言われ、後からいかようにも動作を変えられます。自分で作ったクラスだけでは無く、`require`した他人のライブラリは愚か、組み込みクラスですら変えられます。active_supportなんてその力によって実現していることがほとんどです。

この力は非常に強力であるが故に非常に危険です。有名所では[mathnライブラリ](https://docs.ruby-lang.org/ja/latest/library/mathn.html)でしょう。ひとたびmathnを`require`すれば、`1/2`が½になります。何を言っているのかわからないと思いますが、そういうことです。

今回のクイズでは`3.fizz`のように`Integer`には元々無いメソッド呼び出しています。`Integer`そのものに手を加えないことにはこれを実現することはできません。オープンクラスの恩恵により、Rubyでは組み込みクラスである`Integer`ですら改造可能です。ですが、これは**非常に危険なこと**です。下手な拡張はどこでどんな副作用が出てくるのか想像もできません。何が起こるかわからないドキドキ感を味わいたいわけでは無いので、他にはなるべく影響が無いようにしたいです。

そこで登場するのがRefinementsであり、それを実現する`refine`と`using`です。適当なモジュールを一つ作って、そこで、`refine`を使って、拡張したいクラスを拡張します。そして、そのモジュールを`using`すると、その場所でのみ、その拡張が有効になるのです。他の場所では、その拡張はなかったことになったままです。

`using`はトップレベルとモジュールまたはクラス定義内でのみ使用できます。トップレベルの場合は`using`を使った行からファイルの終わりまでのその**ファイル内**でしか有効になりません。モジュールやクラスも`using`を使った行から、モジュールやクラスの定義が終わるまでです。このように、影響が出る範囲を極めて狭くすることができるため、余計な副作用を心配しなくて済みます。

## で、結局何？

今回一番難しかったのは、一番最初の`NamedInteger`です。正直`IntegerizedString`にすべきか1日悩みました。その結果、流行に乗り遅れてしまったので、ポエムを書くしかなくなりました。元ネタは文字列に変わることを本質に捉えているようですが、私としては整数は整数のままであることが本質だと思います。途中愚痴りましたが、`p`ではなく`puts`で結果が普通のFizzBuzzのようになるというほうが、自由度が出て、意味のあるものだったような気がします。`p`は本来デバッグ用で、プログラムにおいてはあまり意味のある物ではないですし。

なお、この文章は、「モンキーパッチとモンキーパンチって似ているなー。お、リファインとルパンも似ているような気がする。このネタ使おう」って思い立ってしまったがゆえに書き殴ったただの**ポエム**です。