Gauche > Archives > 2010/05/19

2010/05/19 09:03:07 UTCshiro
#
@valvallow @finalfusion 括弧のざわめきに混じって俺を呼ぶ声が聞こえた、ような気がした。
#
nil問題はいくつかの要素が絡んでいるので、分けて考えると良いかと思う。
#
とりあえず返しておける値があると便利、という話と、それがリストのゼロ元である()であると便利、という話。
#
とりあえず返しておける値、というのは、例えばwhenの条件不成立時とか、findで値が見つからなかった時とか、そういう何かの不成立を示すユニバーサルな値ってこと。
#
値そのものは何でも良いんだけど、条件判断と組み合わせた時に偽と判断されるようになってると色々便利。
#
ちょっと一休み
2010/05/19 09:23:04 UTCshiro
#
一方で、主要なデータ型のゼロ元が偽になるようになっていると便利、ということがある。再帰/繰り返ししていってゼロ元に出会ったら終わり、という処理が短く書けることが多いから。
#
リストが主要なデータ型であれば、空リストが偽であると便利。
#
整数が主要なデータ型であれば、0が偽であると便利 (C言語とかね)。
#
中には、リストのゼロ元も整数のゼロ元も文字列のゼロ元も偽にしちゃおうって言語もある (Perlとか)。
#
ただ、この二つの要素、どちらもトレードオフがある。
#
不成立/不存在を示す値=偽値、という設計だと、「偽値が見つかった」という答えを返すのがちょっと困る。gethashでnilが返って来た時、値は存在しなかったのか、それともnilという値だったのか。
#
CLでは多値を使ったりしてこの問題を回避するけど、アドホックな感じは否めない。厳密にやるなら、「どんな値とも一致しない値」を使って不存在を示すしかなくて、そうすると例えばHaskellのMaybeみたいに値を一段ラップしてやることになる。
#
ゼロ元=偽、という設計についても、そのデータ型にとっては便利になるんだけど、他のデータ型にとっては便利にならないっていう非一貫性がある。CLで()が偽で便利なのは、リストばっかり使うからそう思ってるだけ。Cで書いてて0が偽で便利なのは、整数のドメインで作業することが多いから (あと、不存在を示すヌルポインタってものもあるけど)。
#
つまり、「便利」という時には、「その機能が便利になるようなコードの書き方をしているから便利なのだ」ということになってる可能性がある。
2010/05/19 09:37:09 UTCshiro
#
Schemeは前者 (不存在を示す値) については不徹底で、findで#fが返ってくる問題とかあるんだけど、後者についてはいろんなデータ型に対して中立ってことなんじゃないかと思う。
#
結局、()と#fが区別されたらそれに依存したコードを書けて、それもまた「便利」ではあるので、どういうモデルで世界を見てるかってことなんだよね。
#
あと、シンボルのnilとtを特別扱いすることに対しては私は多いに不満。プログラマから使える単語を取り上げる言語は嫌い。
2010/05/19 09:42:54 UTCvalvallow@twitter
#
すごくよくわかりました!ありがとうございました m(_ _)m
2010/05/19 09:49:49 UTCshiro
#
CLで書いてる時は確かに()=nil=偽にばりばり依存したコードを書いてるけど。たまに「シンボルかリストかで分岐したい」なんてコードを書きそうになってnilのせいできーってなることはある。
2010/05/19 10:42:36 UTC齊藤
#
そういや R6RS が出来る前に未定義値は「未定義であることを表す (比較可能な) オブジェクト」にしようみたいな話があったと思うんですが、結局は「何も期待するな」になったのは、やっぱ Scheme 的にそういう依存は好ましくないってことなんでしょうか。
2010/05/19 10:43:38 UTCshiro
#
ああ、そんな議論がありましたね。
#
私の印象では、依存が好ましくないというより、未定義値の使いどころ/位置づけがうまく決まらなかったからじゃないかなあという気がしています。「未定義値」という値として使えるってことになると、それを普通に引数に渡したり戻り値として返したり、とやることになるわけですが、それが出来て嬉しいの/何に使えるの? ってことです。
#
「使っちゃいけない値/当てに出来ない値」を示す値、というのは矛盾していて、値として存在してしまったら使えちゃうわけですから。
2010/05/19 10:51:59 UTC齊藤
#
意味が無いところは意味が無いままな方が自然ってことですね。
2010/05/19 14:41:47 UTCayato
#
偽であるものの定義は言語仕様において、どの空間にもない唯一(出来る限りの最小)の存在であってほしい。()だとか特定のシンボルだとか零元が偽になるなんてのはユーザの定義に任せるべき。そうすると他人のコードが読めなくなりそう
#
だけど、Lispのコードなんて人によって全く別の言語仕様になってるから、そんなのは慣れっこ。
2010/05/19 14:49:03 UTCayato
#
でも、現実には何回 (null? hoge) って書いたんだろう。あぁ...
2010/05/19 17:39:57 UTCshiro
#
(null? hoge)で基底条件判断するのと、(zero? hoge)で基底条件判断するのは同型。前者の方だけが特に気になるとすれば、そういうパースペクティブで見ているってこと。でもmatchで分岐したりfold使ったりしてるとnull?と書く機会はうんと減るよね。
2010/05/19 21:24:53 UTCとおる。
#
たとえば、ECMAScript でいうところの、値としての null と、変数が定義されていないという状態としての undefined の位置づけの話ですよね。
#
Perl だと undef と defined()。Lua はここであえて nil ひとつで済ましています。グローバル変数とかハッシュテーブルの要素に nil を代入すると、その変数(要素)は存在しないもとして扱われます。
#
あと、Lua だと配列の終端を表すのにも nil がつかわれます。リストの終端に () をつかうようなのりでしょうか。この辺はちゃんと理解して使わないとはまりますね。
#
Scheme だと、すくなくとも定義されてない変数を参照することはできないので、「未定義」を使いたい動機がほかの言語に比べてちょっと弱いんじゃないでしょうかね。
2010/05/19 21:35:30 UTCshiro
#
Schemeだと「定義されていない変数」は「未束縛(unbound)」なんですよね。R6RSで話題になったのは「未定義(unspecified)」であって、これはundefinedとも違ってちょっと面倒。
2010/05/19 21:36:45 UTCとおる。
#
(define x) みたいな状態があるってことですか?
2010/05/19 21:36:52 UTCshiro
#
Schemeの場合、プログラム実行前に「世界」が決まっている、という暗黙の前提があります (REPLでは、世界が次々と更新されてゆく感じ)。で、未束縛の変数というものはそもそも世界に存在しないものなので、存在しないものを参照したらどうなるか、というような話が出てきようがない。
#
unspecifiedは「どういう値かを決めてない」ってことですね。R5RS/R6RSでは現状、それは「どんな値であってもいい (なのでその値を当てにするな)」ってことになってます。
#
(define x)となってたら、「xは世界に存在するけど、その値は何になってるかわからない」と解釈する、というのはありだと思います。ただ、Schemeで変数を使うにはその値を取り出すしかないので、どんな値か当てにできない変数というのは使いどころが難しいですね。
#
「とりあえず入れとけや」値があればそれで埋めとけばいいので、色々楽になることは確か。それがCLの場合はnilで、Perlだとundefだったりするわけですが。
#
ああ、世界については別の解釈もあるか。Lua的世界は「あらゆる名前はデフォルトでnilに束縛されている」と解釈することもできるな。
2010/05/19 23:01:10 UTCとおる。
#
あとで set! するのを前提に、変数の宣言だけしたいっていうのはありますよね。C の未初期化変数みたいなものでしょうか?
2010/05/19 23:29:02 UTCshiro
#
そういう場合でも、Schemeだと初期値を決めとけってことになるんでしょうね。関数型脳では初期値の無い変数というのはありえないので。
#
CLOSでスロットが「未束縛」な状態になるっていうのはScheme的感覚とはずれがあるかも。(変数の未束縛と違って、オブジェクトのスロットは既に存在するものだから)