null参照の考案は10億ドル単位の過ち? 88
でもnullポインタ禁止と言われると面倒くさい? 部門より
あるAnonymous Coward 曰く、
quick sortの開発などで著名なアントニー・ホーア氏が、「null参照の考案は過ちであった」と考えているとの話が本家/.で取り上げられている。
3月11~13日にロンドンで開催されるQCon London 2009における、ホーア氏のプレゼンテーション概要には次のようにある。1965年に考案したnull参照の概念は、10億ドル単位の過ちと呼ぶべきものであろう。当時自分は初のオブジェクト指向言語(ALGOL W)のリファレンスの包括的システムを設計していた。全てのリファレンスの使用が全く問題のないことを確認するため、コンパイラによってチェックを自動化することが目標であった。null参照をこれに含めるのは、非常に簡単であり、やらずにはいられなかった。
しかしこれこそがその後40年に渡り、数え切れないほどのエラーや脆弱性、システムクラッシュの原因となり、10億ドル単位の損害や苦労を引き起こしてきたのである。最近ではMicrosoftのPREfixやPREfastなど、多くのプログラム分析ツールがリファレンスのチェックに採用されてきた。さらにSpec#など最近のプログラミング言語ではnon-null参照の宣言が導入されている。これは私が1965年に却下したソリューションである。
本家/.では「nullポインタのない世界なんて想像できない。ホーア氏に責任があるのではなく、プログラマーに責任がある。でもあえて不満を言うとすればゼロ値のトレースが難しいことだろうか」といった意見など、null参照や逆参照にまつわるコメントの多数寄せられるストーリになっているようです。
がっ!! (スコア:4, おもしろおかしい)
ぬるぽ→がっ!をこの記事に当てはめると (スコア:0)
「しかしこれこそがその後」ウザいだのもういいだのと「数え切れないほどの」荒らしや論争の原因となり、
数百単位の掲示板・スレッドの「損害や苦労を引き起こしてきたのである」。
この記事に従えば、いずれはぬるぽがNGワードな掲示板も現れる・・・?
# 数百単位は言い過ぎかも:p
Re: (スコア:0)
NULLポインタが無くなると (スコア:2, おもしろおかしい)
「ポインタわからん、C言語嫌い!」とか言い出したときに、
「誰もが通る道だ」などと訳知り顔で諭す様なふりしてイジメることが出来なくなるから、困る。
from 三流おっさんプログラマ
Re:NULLポインタが無くなると (スコア:1, すばらしい洞察)
「誰も通らない道だ」に変わる日はすぐ近くです。
from 自称・若手プログラマ
Re:NULLポインタが無くなると (スコア:1)
確かに。ウチの若い者は高校生の頃にC#でプログラミングを覚えたそうだ。
#C++、Javaと移ってきた身にはC#のネーミング規約、特にメソッドが大文字始まりなのに中々慣れない…。
#そのせいでクダンの若い者から「ネーミングがJavaと混じってる」という苦情が時々…。
自分一人が発明したと思っているわけですか... (スコア:2)
まぁ、確かに、Java で null pointer exception を見た時には「おまえ、まだ、そんなことやってるの?」とは思った。
nullの代わりにclass objectのinstanceで初期化して、messageDoesNotUnderstandを投げるって方がエレガントだとは思う。
Curlの例 (スコア:2, 興味深い)
もしかしたら「Spec#」がやっている事と同じ事の紹介かも知れませんが,ちょっと Spec# は詳しくないので,代わりに Curl の例を紹介させてくださいませ.
Curl は HTML の代わりだからプログラミング言語とは違うのでは?とか言われる事がありますが,とんでもない,れっきとした関数型チックでオブジェクト指向的なプログラミング言語であります.
「nullの無い世界」とはちょっと違いますが,Curl には,ある変数が null か否かをコーディングの段階ではっきり意識せざるを得ない仕組みが搭載されています.
もちろん,わけのわからない人が何でもかんでも # をつけて null 可の変数ばかり使ってしまっては,ほとんど意味のない仕組みではありますけども.
Java でも @NonNull アノテーションが提案されていたりするし,C++ でも,スマートポインタをうまいこと改造して同様の仕組みを作り上げる事が出来るはずです (自分なりにはやった事があります.本格的にはやってませんが).
これからの言語は,「null か否かを意識せざるを得ない仕組み」が常識になればいいのになーと思っています.
変数の寿命とオブジェクトの寿命 (スコア:2)
オブジェクトの寿命が変数の寿命よりも短い場合には、
実際には使われないにも関わらず参照が残っているために、
いつまでもGCされずにメモリ上を占有してしまう、
ということが、ままありますね。
特に、クロージャや例外や継続がエスケープしたりしたら、
変数の寿命とオブジェクトの寿命を一致させるのは至難の技でしょう。
なんだかんだ言って、null的な参照はどこかで導入されたと思います。
Re:変数の寿命とオブジェクトの寿命 (スコア:1)
変数とオブジェクトを別に取り扱うことを気持ち悪く感じるのは私だけじゃろか.
Re:変数の寿命とオブジェクトの寿命 (スコア:1)
逃げ場が... (スコア:1)
なんだか逃げ場がない世間の様で、
息が詰まりそうです...
#アレ? ひょっとして俺、疲れてる?
-- LightSpeed-J
Re:逃げ場が... (スコア:1)
ML系言語に逃げちゃいなYo! (スコア:2, 参考になる)
おせっかいモードで補足すると、ML系の言語では、 「決してnullにならない」型と、 「nullになり得る(つまり値が無い場合もある)」型を使い分けることができる、 という話ですね。
Standard MLの option型 [standardml.org]や、 HaskellのMaybe型 [haskell.org]が、 「値が無い場合もある」型として使われます。 コンパイル時の型チェックにより、 「場合分けして、値が有る場合」という場所でしか直接の値を参照できないので、 値が無いのに間違えて値を取り出そうとすることはありません。
# 一応「値があればそれを取り出し、値が無い場合は例外を発生せよ」というライブラリ関数も用意されているので、
# Javaのぬるぽ^H^H^HNullPointerException [sun.co.jp]と同様の動作をさせることも選べます。
ML系言語に馴染みがない人のために、 比較的メジャーな(?)命令型言語から似たものを探すと、 C#の nullable [microsoft.com]型のようなものを連想すると分かりやすいかもしれません。
option型やMaybe型は、参照の際に、 値が無い場合についてのケアをしないと、 必ずコンパイラに怒られるようになっています。 だから安全、というわけですが、 とはいえ、値を使う度に場合分けをするのも面倒です。 そこで例えばC#では、 nullableに対する演算 [microsoft.com]を別途定義することで、 面倒な場合分け無しに、ちょっとしたコードを書ける工夫がしてあります。 Haskellでは、これをもっと一般化させていて、Maybeは Monad [sampou.org]としても使えるために、かなり複雑な計算でも、 驚くほどすっきりと書けたりします(うまくMonadで書ければ、ですが)。
ということで、nullにお悩みなら、ML系言語をどうぞ。:-)
よろしい、ならば (スコア:1, 参考になる)
[null参照の考案は10億ドル単位の過ち?]であるならば謝罪と賠償を要求しよう!
#とか言う冗談を思いついたが、我が身の事を考えたらそんなことできないのでありました
ホーア氏の年齢を考えると…… (スコア:1)
年金生活者の爺さんから、大金をせしめるつもりかい??
鬼か、アンタは??
すっごい滑るよ! (スコア:1, おもしろおかしい)
ナル (スコア:1)
こうか (スコア:1, すばらしい洞察)
nonnull 修飾子 (スコア:1)
nonnull ポインタ変数は初期化必須で、NULL やnonull修飾子無しのポインタからの代入できないくらいで。
0除算エラー (スコア:1)
かつてメジャー言語にポインタがなかった時代、実行時エラーの主役は0除算エラーじゃった。
(オーバーフローはチェックしてない環境も多かったから。)
けど、0除算エラーをなくすために0をなくそうという流れにはならなかったような。
ぬるぽや数値の0ほどじゃないけど長さ0の配列だのフィールドのないクラスだの空文字列だのも存在していることだし
なかったらやっぱり不便だと思う。
nullableの類いを構文に足すのは若干大袈裟な気もするなー。
NULLがない世界 (スコア:0)
# え?NULLがあっても、NULLなんかでは初期化しませんか。そうなんですか。
Re:NULLがない世界 (スコア:2, すばらしい洞察)
Re:NULLがない世界 (スコア:1, 興味深い)
参照はがしてもSEGVらず、数値として評価したら0、文字列として評価したら空文字、代入しても何も起きず、関数としてコールしたらすぐ戻り値0で帰ってきて、パス名とかネットワークアドレスとして開いたら/dev/nullみたいな。
Re:NULLがない世界 (スコア:2, 参考になる)
>あればいいんじゃないかなぁ。つかどうしてないんだろ。
それれが有る言語を「高級言語」と呼びます。
いささか乱暴な言い方ですが、高級とはおおむねそういう方角を向くことを言います。
一方で、そういうモノを見事に動かすには結構な実行時コストがかかるわけで、
そのコストを嫌って「自己責任でやってね」という方針にしてるのが「低級言語」。
まず何が起きるか判ったもんじゃないC言語のレベル。
Javaはもう少し上のレベルで、未定義とまでは言わないものの、すごく扱いづらい挙動をするだけ。
Rubyは更に少し上のレベルで、nilもまたオブジェクトであり、かつ振る舞いが好きなようにカスタマイズできる。method_missingとか使えば「状況ごとに」振る舞いを変えることも出来る。
まあいろいろな高級度にそれぞれの言語が位置しているわけです。
Re:NULLがない世界 (スコア:1, 興味深い)
言語標準として付いて欲しいという話かもしれませんが。理想のnullってのはケースバイケースなので難しいかも。
Re:NULLがない世界 (スコア:1)
野暮御免(Re:NULLがない世界 (スコア:1)
通常、ポインタを取る型の変数に整数の定数(特に負の数)を突っ込んで何某かを表現するのは反則では…後方互換性の問題でやらざるを得ない場合はありますけど、呼び出す関数も含めてユーザサイドで完結する所で下手にそれやると蟲が沸いてくるのでヤバい気がする…
# これが8bitとか8051,PICあたりだったらやりますけどぉ(-.-;)
Re: (スコア:0)
というコード規約を見たことがあります。なんでそんなことをするのか質問したのですが、納得できる答えはありませんでした。
Re:NULLがない世界 (スコア:5, すばらしい洞察)
たぶん数百行あるメソッドの途中で不適当な名前の変数を宣言して、実際にそれを使うのはさらに数百行後な上に例外は握り潰しみたいなコードを書いたアホがプロジェクトにいたからだろうよ。
Re:NULLがない世界 (スコア:1, 興味深い)
・宣言したけど使われないprivate変数
・宣言したけど使われないimport
・一メソッドにつき数百行は少ない方で、千行オーバー当たり前
・例外はExceptionまたはThrowableで逃さずキャッチ
・捕まえた例外はisInstanceでどんな例外かしっかりチェック
・良く分からないエラーは適当な例外クラスでラッピングして投げ飛ばし
どうやらアホは一人だけではなかったようです。私にはもうどうしようもありません。
というか、どう見ても規約の前にJavaの勉強が必要です、本当にありがとうございました。
Re:NULLがない世界 (スコア:1, 興味深い)
コーディング規約やら設計書の雛形やらを作らせていました。
「できる人」は派遣先や社内プロジェクトで忙しいので、
「売れ残り」にそういった仕事をさせていたわけです。
出来上がった規約のレベルは言わずもがな...
Re:NULLがない世界 (スコア:1)
もちろん実体作成しとくのが前提なのですが、最初は初心者ですしうっかりミスもありえます。
#最近使ってないので改良されたかな。もしかしてWarningレベル上げればチェックしてくれてたのだろうか。
Re:NULLがない世界 (スコア:1)
#もちろん単純ミスだったのですぐ直したのですが、その時に「不親切だなぁ」と思ったのでした。
#もしかするとよほど特殊な状況だったのでしょうか。
長年で身についた手癖でしょうね(Re:NULLがない世界 (スコア:1)
単純に、
{
StringData *foo=null;
...
foo=bar();
...
if(!foo) {
fooが設定されてない=函数barでエラー起こしたからあれこれやる
} else {
fooが設定されてる=函数barで正常終了したからあれこれやる
}
}
と言う、C++ではそこそこある(C的な)処理ロジックをやりたかったのでしょう。#1525645 [srad.jp]で既に出されていますが(^^;
Javaでこれを実際にやれば例外で飛ばされる以前にポインタで論理式を表記する事自体が反則なのでなんかすごく酷い邪道に思えてなりませんが…てか、C++と違ってC引きずらないでもいいんだからOOPと言うかカプセル化や継承をうまいこと使ってAPI統一すればいいものを…
# 以下、C++的に書くけど文法極めていい加減
Class functionTemplete {
bool results;
int ERR;
function main();
}
とかなんとかテンプレート作っておいて各実装はこのクラスの下位のクラス内ローカルな関数かこのテンプレを継承したクラスとして実装してねー、このテンプレに載せてある関数名と変数名は予約語扱いでこういう意味持たせておくインタフェース用の変数だよ。的な比較的ルーズなやり口が通用するというのに(あ、排他しないといけない可能性があるから、厳しくやるには変数いじらせる時はこの関数をこう叩けにしなきゃだめな場合も出るか)…そもそもJavaをわざわざ使う最大のメリットって、オブジェクトのポータビリティと同時にこの辺にある気がするのですが(あ、あくまでもいつもはCとアセンブラだけの人の偏見です ^^;)…
まぁ、一度手慣れた手法を崩すのが容易ではないのは分かりますが。
Re: (スコア:0)
Cの後遺症かな
Re:NULLがない世界 (スコア:1)
>不意なガベージコレクションを防ぐ効果があるかも
これが理解できない。
nullってことは、GC対象が無いわけだが・・・・
Re:NULLがない世界 (スコア:5, 参考になる)
そうではなくて、 (non-nullな)参照型が決してnullになり得ない言語仕様にしておけば良かった、 ということでしょう。
といっても、nullに慣れ親しんでしまった人には、 なかなか想像し難いものがあるかもしれません。 例えば、こう考えてみてはどうでしょう。 Javaにそもそもnull定数が無く(nullが予約語でなく、 たとえnullと書いてもコンパイル時に未定義変数エラーになる)、 すべての変数に初期化が必要で、 nullを返すようなライブラリ関数も無かったとしたら? 参照型変数が必ず有効なオブジェクトを参照するように初期化され、 オブジェクトを明示的にfree()/deleteで開放できないとしたら、 ポインタの先を参照した時、そこには必ず値があります。 ポインタをそもそもnullにする方法が用意されていないとしたら、 ポインタがnullかどうかのチェックをする必要もありません。
「えー、でも、それじゃどうやって、値が無いことを示すのさ?」
値が無いかもしれないことを示す必要がある時だけ、 nullableな型を使いましょう、というのが、答のようです。
少なくとも、 ホーア氏のプレゼンテーション概要 [qconlondon.com]にある Spec# [microsoft.com]は、 そのようになっています。
Spec#では、ポインタ型に2種類あって、 nullableなポインタ型か、non nullなポインタ型を宣言できます (C#では値型Tのnullable型をT?と書けますが、それと反対に、Spec#では参照型Tのnon null型をT!と書けます)。 この二つは、コンパイル時の型チェックにより、混ざらないようになっているので、 non nullポインタ型については、 実行時にnullかどうかのチェックをする必要はありません。 参照先に有効な値が存在することは、コンパイル時に保証されているわけです。 non-null型を使うのをサボってnullableなポインタばっかりにしたら、 結局今までと同じじゃん、という気もしますが。 詳しくはDeclaring and Checking Non-null Types in an Object-Oriented Language [microsoft.com](注: PDFです)参照、ということで。
実はこのようにポインタ型を2種類持つ言語はSpec#が最初というわけでもなく、 他にも例えば Ada [adaic.org]の 参照型も、"not null"を指定するかどうかで、2種類の参照型を区別できます。
Spec#は、独自の革新的なアイデアが入っているというわけではないものの、 過去の様々な言語の良いとこ取りを目指しているようですね。 Eiffel風の契約プログラミング [wikipedia.org]向きの 書法があったりして、なかなか面白いです。
件のホーア氏のプレゼンについては、 以前Lambda the Ultimate [lambda-the-ultimate.org]でも取り上げられていました。 そこでもいくつか興味深いコメントが付いていたので、参考にどうぞ。 Adaでもnot null指定ができるというネタはここから頂きました。 「nullableばっかり使ったら同じじゃん問題」に対応した言語については 別のコメント [srad.jp]に書いたのでそちらもどうぞ(長文ばかり(_ _))。
Re:NULLがない世界 (スコア:1, 興味深い)
C++ にも、nullable な (type*) と、not nullable な (type&) がありますよね?
その仕様のおかげで、様々な問題を抱えるに至りました。
どの様な問題が起こったのかといいますと、
例えば、
(type*)->hoge と (type&).hoge が混在して気持ち悪くなりました。
# それはアクセサの問題じゃないか
std::string& hoge() とか意味もわからずに書いてバグらせる奴がでました。
# それはスタックの問題じゃないか
int hoge(const int &hage) とか、何をしたいかわからん事かかれました。
# 結局はバカなのが問題じゃないか
つまり、何がいいたいのかというと、
nullable であるかどうかは、私の問題にはなんら関係がないって事です。
# 違った
Re:NULLがない世界 (スコア:1)
そんな事をしたらアクセス出来るメモリが半分になってしまうか、ポインタを格納するのに2ワード必要になってしまいます。
それよりポインタを作成した時に、中身が自動的にNULLの値になっている様な言語仕様にした方が良いかと。実際、Javaが似た様な事をしてます(Javaにはポインタ自体は無いけどオブジェクトのレファレンスが似た様な格好なので)。
Re:NULLがない世界 (スコア:1)
1bitなんて使わなくても
0 = NULL
1 = 未初期化
それ以外 = 有効なポインタ
みたいなのでいいじゃない。Win32APIにもINVALID_HANDLE_VALUEとかありますし。
ホーア (スコア:0)
ほ、ほーっ、ホアアーッ!! ホアーッ!!
Re:ホーア (スコア:1)
|∀゚).。oO (この人、本当はホーア氏が嫌いなのではないだろうか・・・)
#ほ、ほあーーーっ! ホアアーーーッ!
Re: (スコア:0)
テンションが上がった!
今すぐrm /dev/null (スコア:0)
ぬるぽ (スコア:0)
Re: (スコア:0)
それって『くぱぁ』とかと同列な意味で?
Re:ぬるぽ (スコア:1)
# ふう。これで青少年のピュアな心が守られた。
## ・・・いまどき『くぱぁ』の意味がわからんのは、逆に団塊世代以上だと思うがな。
Re:ぬるぽ (スコア:1)
なんという聖数・・・
心が洗われますね(歌声的な意味で)
#もうidでいいっす(=_=;
眠気と戦う活字中毒者
代わりのバグが増えるだけ (スコア:0)