From: matz@... (Yukihiro Matsumoto) Date: 2003-02-07T10:01:19+09:00 Subject: [ruby-list:37063] Re: Local variables & blocks まつもと ゆきひろです In message "[ruby-list:37059] Re: Local variables & blocks" on 03/02/06, ichimal@fenix.ne.jp writes: | さて、ここから本題 (1) です。 | | 前述の Klemmer 氏の例の foo という変数は、blocktest の引数が空であっ |た場合でも (新スコープルール案下では) 定義されます。しかし、その場合明 |示的な代入による初期化は行われません。そして、この場合でも foo には何 |らかの値で初期化されます。前述のように、まつもとさんは nil を想定して |いるようです。nil は元来「slot が未初期化であることを表す特別な値」だ |からだそうです。 | | しかし、Ruby の nil は本当に「未初期化」を表しているんでしょうか? そ |れがそもそもの疑問点です。私は nil を「無効な値」の代表値と考えるべき |だと主張します。実際、Ruby での nil はそのような目的で使われています。 nilが無効な値の代表値であることには同意します。ただ、用語の 使い方がたぶん異なっていると思うので、あらかじめ定義しておき ます。 「未定義」 そのスコープ中で一度も代入されていないこと。つまり、その識 別子はそもそもローカル変数を指していないこと。その識別子に 対する通常のアクセスはエラーになる。defined? <識別子>が偽 になる。 「未初期化」 ローカル変数が初期化されていないこと。つまりどこかに代入 があるが、その代入は実行されていないこと。その識別子に対 するアクセスはエラーにならず、値はnil。 鈴木さんの「未初期化」はどちらかというと前者の意味が強いと思 います。現在のルールでも[ruby-list:37060]で田中さんが指摘さ れたように「未定義」ではないが「未初期化」である状況はいくら でも発生します。 変数を未定義に戻す方法はありません。nilを変数に代入するとい うことは、未初期化と同じ無効な値を代入するというだけのことで す。この意味で | nil を変数に代入することは、その変数が「(ある文脈において) 無効とみ |なされるべきオブジェクトを指している」という状態にすることです。しかし、 |変数を未初期化状態に戻すことではありません。なぜなら、「無効である」と |いう概念と「初期化されていない」という概念は全く異なる次元のものだから |です。 というのは成立していないと思います。「未定義」と「無効である」 は全く異なる次元のものですが、「未初期化」と「無効である」は ほぼ同じです。 | しかし、新提案のスコープルールは、「定義されているが未初期化」という |状態を容認する意味論に即したものです。そこに nil を旧来の定義に即した |未初期化値として持ち込むのは、double meaning です。 すでに述べましたが、「定義されているが未初期化」という状態は 現在も存在しています。 | 実害もあります。nil で明示的に初期化したのか本当に未初期化なのかの区 |別をしなければならないときのために、特別なメソッドを NilClass に定義し |なければならなくなります。場合によっては、下記のような冗長なコードが必 |要になるかもしれません。 「nil で明示的に初期化したのか本当に未初期化なのかの区別をし なければならないとき」ってのが存在するとは思えないのですが。 「有効なnil」というのも「有効な無効値」という自己矛盾を起こ してますし。 それにNilClassから、その値が未初期化か無効化を判断する方法は ないと思います。未定義かどうかならdefined?を使うことができま すが、未定義かどうかの判定って(コンパイル時に決まるので)たぶ ん意味がないですよね。 | 本題 (1) をまとめます。 | (*) 「無効」と「未初期化」は別概念 私はここに異議を唱えます。未初期化つまり、実行時に代入されて ないってことはその変数は有効な値を持たないということで、それ は無効値を持つと同義ではないかと考えます。 未定義、つまりその変数が定義されていない場合にはアクセスその ものがエラーになりますから、値はありません。ですから、未定義 の変数の値は存在しないので定義する必要はありません。 |----- | さて、本題 (2) です。 | これは無効値や未初期化値が偽値なのはアレじゃないか? という議論です。 | 本題 (2) をまとめます。 | (*) 無効値や未初期化値を他の概念から切り離した方が良い | (**) 切り離したくないのであれば、to_str の類で明に変換 仮に上で述べた「未定義値」が存在する(必要がある)のであれば、 その値は本題(1)で示されたような性質 | この # に求められる主な性質は以下の通りです。 | (*) first class object | (*) a = # のようなコードは禁止 (*1) | (*) ==, !=, defined?, non_initialized? (仮称) などに対応 に加えて真偽値としての性質も持たない方が良いという主張は理解 できます。しかし、そもそも未定義値は存在しない(する必要がな い)と考えます。 | たとえば talk [63746] に書いた以下の例を御覧下さい。 | || def questionnaire || print_somewhat_Q || str = gets || parse_answer(str) if str || end | |このメソッドはアンケートを採るためのものです。このメソッドは無効値とし |て nil を返すか、そうでなければ何らかの「有効な値」を返すことが期待さ |れます。nil が返って来るのは、空入力を得た場合か、あるいは入力を |parse_answer が無効なものとみなした場合です。 | | このコードを書いたプログラマが、既定回答を「偽値」と定めていた場合、 |結果が nil かどうかの判断用のコードは省略できます。しかし、既定回答を |用意しなかったり真値と設定していた場合には省略できません。これは非対称 |的だし、妙なコーディングスタイル (「既定回答を偽値にするようにコーディ |ングするべき」等) に誘導してしまう可能性もあります。 falseとnilの両方が偽であることによって発生しうる問題のひとつ ですね(もうひとつは英語での説明がややこしいこと - false and nil are false values...)。 ただ、これはトレードオフで妥協できるのではないかと考えていま す。また、無効であることを示す方法にはもうひとつ例外がありま す。無自覚にnilを返すよりは例外をあげた方がスタイルとしては 良いのではないかと思います。 |英語でもこれくらい書けなきゃマズいんだよな、俺 (^^; あはは、私も同じです。鈴木さんが日本人でまだ良かった。ロシア 人とこういうやりとりをしなくちゃいけない日にはもうわけわかん ないです。 まつもと ゆきひろ /:|)