まつもと ゆきひろです

In message "[ruby-dev:17764] Re: self in block"
    on 02/07/23, masaki <GEC01122 / nifty.ne.jp> writes:

||  Foo.new {<a,b,c>
||    ...
||  }
||
||の場合には、ブロック内部での
||
||  * selfはFooのインスタンス
||  * @fooはFooのインスタンス変数
||  * foo()はFooのメソッド呼び出し
||
||になるということでしょうか。とするとたとえば、
|
|その通りです。ただし @foo はこの段階では未定義のはずですが。

インスタンス変数は初期化されていなくても、参照・更新ともにで
きますし、未定義であるかどうかはさほど重要ではないと思います。
さらにいえば、initializeの中でブロックを評価する前に初期化さ
れている可能性があるのでなんで「@foo はこの段階では未定義の
はず」とはならないと思います。

||  @title = "widget title"
||  Foo.new {<>
||    set_title @title
||  }
||
||のようなものは動かないってことですよね。こういう場合には
||
||  @title = "widget title"
||  Foo.new {||
||    set_title @title
||  }
||
||としろということでしょうか。なんだか <> に複数の意味を与えて
||いるので混乱しそうな気がします。
|
|なにが混乱するのか全然分かりません。
|というより、上の script の意図が全く分かりません。

これは実際にRuby/Tkでレポートが来た例です。Ruby/Tkではnewに
与えられたブロック内部では(instance_evalを使って)selfを新規
に作られたオブジェクトにすり替えています(これはよいですよね)。

上の例は「全体を管理するオブジェクトがtitleに関する情報を持っ
ていて、それを自分が生成したWidgetにも設定した」というシナリ
オを表現しています。

さて、普通のRubyプログラムでは同じメソッド内部でのインスタン
ス変数は同じオブジェクトのインスタンス変数ですから、すり替え
が起きるとその期待が裏切られることになります。

|もしこれが、なにか別の class の method 定義のなかというのでなければ
|  @title = "widget title"
|の所で error になりそうですが?

上記の例はコード片で、全体としては別のクラスのメソッドの定義
の中だと解釈してください(そうでなくても実はエラーにはなりま
せんけどね)。

|次の
|  def initialize
|    @lambda=eval "lambda{"+yield+"}"
|  end
|
|  def initialize(&block)
|    @lambda=block
|  end
|
|のようにすっきり書きたいということです。

で、たったそれだけのために{<>...}という記法全体を巻き込む必
要があるとは私には思えません。正木さんはProc.newのような手続
きオブジェクトにおいて「すっきり書きたい」だけなんですよね。

|Proc.new{<n> n==0 ? 1:n*self[n-1]}
|
|が現在はできません。

いや、確かにできないでしょうけど、できたとしてそれがどれだけ
うれしいのか、実はさほどうれしくもないだろうというのが私の見
解です。

|他の人にとっても、
|自己参照もできる無名関数が簡単にできるというだけでも非常に有用だ
|と思います。

「非常に有用」とは私には思えないんですが。どうしてそう思われ
るんですか。正木さんの日々のプログラミングでは自己参照のでき
る無名関数が頻繁に必要になるんですか?

|それとひきかえに失うものが、実装するための苦労も含めて非常に大きく、
|しかもそれに対する対策もないということを示していただければ、私の
|提案はすぐに取り下げます。

えーと、正木さんは提案をなさいましたが、この提案の実装コスト
はちょっと考えただけでもそんなに小さいものではありません。イ
ンタプリタの大改造が必要になるでしょう。

一方、私の印象では正木さんはこの提案を実現することで得られる
メリットをいくつか示されましたが、今まできれいにできなかった
ことができるようになるということは示されましたが、その「嬉し
さ」を十分に示しているとは思いません。

私はRuby/Tkの経験からselfのすり替えに否定的ですし(インスタン
ス変数は禁止してもメソッド呼び出しは禁止できませんし)、全体
のバランスから考えても導入することの嬉しさは大きくないだろう
と思います。少なくとも私をやる気にさせるには不十分です。

一番簡単なテストは、ドキュメントを書いてみることです。

  {<> ...}という形式のブロックではブロックパラメータはブロッ
  クローカルになります。..ローカル変数の扱いについては未定な
  ので省略... この形式のブロックが(直接あるいは間接に)クラス
  のnewメソッドを経由してinitializeメソッドに渡されたときに
  は、このブロック内部でのselfは外側のselfではなく、新しく生
  成されたオブジェクトになります。このブロック内部からレシー
  バを指定せずに呼び出されるメソッドは新しく作られたオブジェ
  クトのメソッドです。

  この形式のブロック内部でのインスタンス変数へのアクセスはエ
  ラーになります。$SAFEが1以上のときこのブロック形式を
  initializeに渡すことはエラーになります。

ざっと書いてみただけでこんな感じです。正直なところ、私はこの
仕様から背後の意図をすんなり読みとることはできません。おせじ
にも分かりやすい仕様とはいえないのでは。

一方、たとえば

   proc = function(a,b){....}

のような文法を導入し、その中では変数のスコープがローカルであ
るとするようにすれば、実装も比較的簡単ですし、他にあまり悪影
響を与えずに正木さんのやりたいことも実現できるんではないんで
しょうか。「やる」とは言わないけど。

                                まつもと ゆきひろ /:|)