Rubyでメタプログラミングをやってみた
はじめに
最近メタプログラミングという言葉を聞きました。なんとなく分かっているつもりではいましたが、実際にメタプログラミングを書いたことはなく、ふわっとしか理解していなかったので実際にメタプログラミングをやってみて理解を深めてみました。
メタプログラミングとは
wikipediaより引用
メタプログラミング (metaprogramming) とはプログラミング技法の一種で、ロジックを直接コーディングするのではなく、あるパターンをもったロジックを生成する高位ロジックによってプログラミングを行う方法、またその高位ロジックを定義する方法のこと。主に対象言語に埋め込まれたマクロ言語によって行われる。
とありますが、私はメソッドを動的に作るロジックと解釈しました。
ChatGPTに『メタプログラミングはメソッドを動的に作るロジックですか?』と尋ねてみたところ正しくはあるものの、それだけだと足りないと指摘されました。
↓GPT産
メタプログラミングはそれだけに限らず、プログラムそのものを動的に生成、変更、または拡張する技法全般を指します。
- クラスやモジュールの動的定義
- メソッドのオーバーライドやエイリアス
- メソッドミッシング
- DSL(ドメイン固有言語)の作成
メソッドの動的生成以外にも上記などがメタプログラミングになるそうです。
全てやると頭がパンクするので、今回の記事ではメソッドの動的生成を試してみたいと思います。
実装
今回はRubyのattr_accessorの機能を自前で作ってみようと思います。attr_accessorとは、外部からクラス内の変数を参照するためのゲッターメソッドとその変数を変更するためのセッターメソッドを定義するものです。
attr_accessorを使わないと外部から参照できない
class Hoge
def initialize(name)
@name = name
end
end
hoge = Hoge.new('山田哲人')
puts hoge.name # attr_accessorでnameを定義していないのでエラーになる
attr_accessorがあるので変数を外部から参照できる
class Hoge
attr_accessor :name
def initialize(name)
@name = name
end
end
hoge = Hoge.new('山田哲人')
puts hoge.name #=>山田哲人
このattr_accessorの機能をメタプログラミングで自前で作ってみます。
class Hoge
def self.my_attr_accessor(*attrs)
attrs.each do |attr|
define_method(attr) do
instance_variable_get("@#{attr}")
end
define_method("#{attr}=") do |value|
instance_variable_set("@#{attr}", value)
end
end
end
my_attr_accessor :name
end
hoge = Hoge.new
hoge.name = "山田哲人"
puts hoge.name #=> "山田哲人"
define_methodを使って、属性名に対応するゲッターメソッドを動的に定義します。このメソッドは、インスタンス変数@attrの値を返します。
define_method(attr) do
instance_variable_get("@#{attr}")
end
define_methodを使って、属性名に対応するセッターメソッドを動的に定義します。このメソッドは、インスタンス変数@attrに引数valueを設定します。
define_method("#{attr}=") do |value|
instance_variable_set("@#{attr}", value)
end
my_attr_accessorがattr_accessorの役割を果たしているので、ゲッターセッターは個別に定義していませんが、メタプログラミングによって動的に定義することができました。
最後に
メタプログラミングは、コードの再利用性を高め、開発をより効率的にするための強力な技法です。今回の例では、Rubyのattr_accessor機能を自作することで、メソッドを動的に生成するプロセスを理解しました。これにより、同様のパターンが複数のクラスで必要な場合に、一度の定義で済ませることができます。
メタプログラミングを学ぶことで、Rubyをさらに深く理解し、柔軟でパワフルなコードを書くことが可能になります。しかし、その強力さゆえに、過度に使用するとコードの可読性が低下し、デバッグが難しくなることもあります。適切なバランスを見つけることが重要です。
Discussion