正木です。

In message "[ruby-math:00410] Re: class Real"
    on 01/03/19, Shin-ichiro HARA <sinara / blade.nagaokaut.ac.jp> writes:

|これは一般的な話になってしまうのですが、例えばブロックの値の2倍を得よ
|うとして
|
|def foo
|   x = 2
|   eval(yield) * x
|end
|
|という関数がを作ったとき、うっかり
|
|foo { "x = 100001; x * x" }
|
|などとした場合、意図しない動作をしますよね。

確かにブロックの値の x 倍を得ようとして
def foo(x)
   eval(yield)*x
end
としたらまづいですね。
(method 定義の仮引数にどんな変数名が使われているか分からないと、その
method が安全に使えないというのはおかしいという意味です。)
これを見ていままで勘違いしていたことに気が付きました。
method 定義の冒頭では全ての変数が未定義だと、錯覚していました。
仮引数が定義されていることをうっかりしていました。
そうすると

class Sequence
  def initialize(list=[],*x)
    @proc=eval("proc{"+yield+"}")
.....

は block の中で list や x を変更されるとまづいことになります。
これは今の仕様では解決策は無さそうなので、(引数を無しにして全ての
情報を、文字列で block に渡すという方法が無いこともないですが)
とりあえずの弥縫策として class Sequence の冒頭部分を

class Sequence
  def initialize(sequence_initilal_list=[],*sequence_parameter)
    @proc=eval("proc{"+yield+"}")
    @list=sequence_initilal_list
    @para=sequence_parameter
  end

  def [](n)
    @list[n] || @list[n]=@proc.call(n,*@para)
  end

に変更しておくことにします。
まだ何か錯覚していると困るので確認したいのですが、上記の場合 block 内の
変数は sequence_initilal_list,sequence_parameter 以外全て、ブロック内
ローカル変数となると思って間違いないですか?


|外部の変数の値を不変にするには、ブロックの中でも代入しないように気をつ
|けるだけです。事がローカルに決定されるという点で大域変数のような不安は
|ありません。もちろん、ブロックパラメータも代入式と考える必要があります。

そういってしまうと身も蓋も無くなります。
どんなに気を付けても間違えることはあるので(例えば上に述べた例は指摘されれば
すぐに分かるようなことですが、それでもうっかりしていました)、なるべく
間違いにくい仕様にした方が良いという話なんですが。
上述の例も、前の mail で提案したような(block 内の変数の scope は
できるかぎり block 内に限定する)仕様ならば問題になりません。
Object 指向の目的のひとつはそこにあると理解していたのですが、違いますか。
「情報はできるだけ局所的にする」というのが原則だったはずです。

|こちらの方はブロックでその変数を使用して良いかどうかローカルに決定でき
|ません。もし foo がライブラリの関数であれば、そのソースコードを調べる
|必要が出てきてしまいます。それが eval を避けたい理由の一つです。

これはどういうことか良く理解できませんでした。できたら実例をあげて説明
していただけますか?

|メソッドの中だから、トップレベルだから、という違いは特にないでしょう。
|メソッドの中でもローカル変数を延々と使い回せば状況はわかりにくくなるし、

メソッドというのは短いものが多いのと、汎用性が要求され何回も呼ばれる
ものなので、慎重に注意して書きますが(それでも上述したような見落としがあ
ります)、トップレベルというのは、(私の場合だけかも知れませんが)頻繁に
変更追加することが多く、その際 editor で切り貼りすることが多いのですが、そこ
で変数名の衝突に気付かなければ、見付けにくい bug になり、気付いたとして
も変数名の書き換えをするのが結構面倒です。

|メソッドの中で他のメソッドにブロックを渡せばやはり同様な問題は生じるわ
|けだから。

これが今のブロックの仕様が使いにくいと思う理由です。
メソッドにブロックを渡す時に、そのメソッドの code を見なければいけない
ということを誰もおかしいと思わないのが不思議です。
method というのは仕様さえ知っていれば、その実装は知らなくて良い筈です。
例えば最初に書いた例の foo(x) の仮引数の x は本来なら dummy 変数のはず
ですから、これが x か y かなんていうことはどうでもいいことの筈なのに
現在の仕様ではそうなっていません。
これは Object 指向の原則の一つ「情報隠蔽」に反しませんか?

block はもともとは iterater だったので iterater として使うときには今の
仕様が便利なのかもしれません。
これを、「名前を付ける必要の無い関数」として使おうとするから無理があるの
かも知れません。

いまさら block の仕様を変えるわけにもいかないでしょうから、もう少し現実性
のある提案をします。
名前は何でも構いませんが、block 内変数の scope を可能な限り狭く取る
  another_lambda{...}
のようなものを作ってもらうわけにはいきませんか。
その変数が未定義だとして現在の仕様で scope を決めて、それで error に
なる変数だけ外部変数にするだけですから可能だと思いますが。
あるいは block 内に scope が限られない外部変数を明示的に与える
  another_proc(x,y){...}
みたいなものでもいいですが。