Ruby
メタプログラミング
Swift

Rubyのキーワード引数に外部引数名を付けて遊ぶ

More than 1 year has passed since last update.

実用性はありません。

目的

RubyでもSwiftのように外部引数名を使いたい

// Swift
func range(from start: Int, to end: Int) {
  print(start)
  print(end)
}

range(from: 0, to: 5)

Syntax

新しいメソッドの書き方を導入します。とは言え既存のシンタックスに沿って解釈できる形です。

func range(from: :start, to: :end) do |args|
  puts args.start
  puts args.end
end

funcメソッドにrange(from: :start, to: :end)とblockを渡している、という解釈になります。
あとは、

  • method_missingrange(from: :start, to: :end)の実行をフック
  • funcの中で引数をセット
  • メソッドを定義

するだけです。

実装

class BasicObject
  class << self
    def func(args, &block)
      method = args[:method]
      args_map = args[:args_map]
      define_method(method) do |args|
        keys = args_map.values.map { |v| ":#{v}" }.join(',')
        values = args_map.keys.map { |k| "'#{args[k]}'"}.join(',')
        s = eval("Struct.new(#{keys}).new(#{values})")
        self.instance_exec(s, &block)
      end
    end

    def method_missing(method, *args)
      super unless args.first.is_a?(Hash)
      return {:args_map => args.first, :method => method}
    end
  end
end

テスト

class A
  func range(from: :start, to: :end) do |args|
    puts args.start
    puts args.end
  end
end

a = A.new
a.range(from: 0, to: 5)

出来ましたね。

問題点

一応やりたいことはできましたが、method_missingでフックされる範囲が広すぎるため、失うものが多すぎます。
全く実用性はありません。

やりたかったこと

Binding#local_variable_setで引数をコンテキストに渡してやるつもりでしたが、Binding#execProcを評価出来ないため、諦めてStructargsに渡しています。
うまい書き方教えてください。