メタプログラミング Ruby 3章を読んだ。

ブロックの章。2-3回読んだ気がする。
タイトルで想像していたものより,内容の深いものであった。
ブロックで、変数のスコープをうまくコントロールする妙技がこの章のキーの一つになる。

さわりだけ解説。

times = 3
[1, 2, 3].map{ |n|
   n * times
}

timesがスコープ内なので、mapのブロック内でも参照できる。
ブロックの特徴の1つである。

times = 3
def my_times value
   value * times  # NameError: undefined local variable or method `times'
end
puts my_times 4

上記だと、defのスコープゲートで、timesはスコープ外となり実行できない。

times = 3
Kernel.send :define_method, :my_times do |value|
  value * times
end
my_times 4

上記は、defの代わりに、define_methodのプロックを使ってメソッド定義している。
なので、times が参照可能となる。

  • クラス定義を class の代わりに Class.new
  • モジュール定義を module の代わりに Module.new
  • メソッド定義を def の代わりに define_method

と置き換えるとブロックが使えて、変数のスコープが変わり、プログラミングスタイルの幅が広がる。

そのあと、instance_evalと ブロックを組み合わせた技の説明に入っていく。

最後のDSLの説明の箇所はなるほどねぇと感心する。

  • 変数のスコープ限定を lambdaで
  • 変数の共有を Kernel.send :define_methodで
  • ブロックをいったん配列に格納し
    • まとめて評価
    • 実行順序を制御
    • 条件の合うやつだけを実行
  • ブロックで記述された設定ファイルのような Ruby Code(DSL)の実行を load & instance_evalで


読んでいて、DSLsのObject Scopingの章のRubyサンプルと類似/相違点が頭に浮かんだ。
DSLsのObject Scopingのサンプルコードの場合は、lambda & define_method ブロックの妙技を使わず、
Builderクラス内のインスタンス変数の共有、self.load & instance_eval を使っていた。

メタプログラミングRuby

メタプログラミングRuby

Domain-Specific Languages (Addison-Wesley Signature Series (Fowler))

Domain-Specific Languages (Addison-Wesley Signature Series (Fowler))