私はコンパイラのクラスにいて、独自の言語をゼロから作成する任務を負っています。現在、私たちのジレンマは、「null」型を含めるかどうかです。null はどのような目的を提供しますか? 私たちのチームの中には、厳密には必要ではないと主張する人もいれば、提供できる追加の柔軟性のためだけにゼロを支持する人もいます.
特にnullに賛成または反対の考えはありますか? null を必要とする機能を作成したことがありますか?
私はコンパイラのクラスにいて、独自の言語をゼロから作成する任務を負っています。現在、私たちのジレンマは、「null」型を含めるかどうかです。null はどのような目的を提供しますか? 私たちのチームの中には、厳密には必要ではないと主張する人もいれば、提供できる追加の柔軟性のためだけにゼロを支持する人もいます.
特にnullに賛成または反対の考えはありますか? null を必要とする機能を作成したことがありますか?
Null: 10 億ドルの間違い。トニー・ホーア:
私はそれを私の10億ドルの間違いと呼んでいます。それが 1965 年のヌル参照の発明でした。その当時、私はオブジェクト指向言語 (ALGOL W) での参照のための最初の包括的な型システムを設計していました。私の目標は、参照のすべての使用が完全に安全であることを保証することであり、チェックはコンパイラによって自動的に実行されます。しかし、実装がとても簡単だったという理由だけで、null 参照を挿入したいという誘惑に抵抗できませんでした。これにより、無数のエラー、脆弱性、およびシステム クラッシュが発生し、過去 40 年間でおそらく 10 億ドルの痛みと損害が発生しました。近年、Microsoft の PREfix や PREfast などの多くのプログラム アナライザーが参照のチェックに使用されており、参照が null でない可能性がある場合は警告が表示されます。 Spec# などの最近のプログラミング言語では、非 null 参照の宣言が導入されています。これは、私が 1965 年に拒否した解決策です。
いやいや、哲学専攻が出てきた気がする……。
NULL の概念は、集合論における空集合の概念に由来します。ほとんどの人は、空集合がゼロに等しくないことに同意します。数学者と哲学者は、集合論の価値について何十年も争ってきました。
プログラミング言語では、メモリ内の何も参照しないオブジェクト参照を理解することは非常に役立つと思います。集合論について Google で検索すると、集合論者が使用する形式的な記号体系 (表記法) と、多くのコンピューター言語で使用される記号との間に類似点があることがわかります。
よろしく、サム
あなたが尋ねるのは何ですか?
良い、
何もない。
私は通常、「メモリ アドレス 0」の C/C++ の側面で「null」を考えます。厳密に必要というわけではありませんが、存在しない場合は、他のものを使用するだけです (myNumber == -1 の場合、または myString == "" の場合)。
私が知っているのは、「null」という単語を入力せずにコーディングに費やした日は考えられないということだけです。
.NET の世界では、MS は最近、以前は null を許容できなかった int や long などの null を許容する型を追加しました。
もし私が言語をデザインしていたら、それを維持するでしょう。ただし、null を持たない言語の使用も避けません。それも少し慣れる必要があります。
言語設計全体の文脈の外で null について話すことは役に立たないと思います。混乱の最初のポイント: null 型は空ですか、それとも単一の識別値 (「nil」と呼ばれることが多い) を含んでいますか? 完全に空の型はあまり役に立ちません。C では空の戻り値の型を使用してvoid、副作用のためにのみ実行されるプロシージャをマークしますが、他の多くの言語では、この目的のためにシングルトン型 (通常は空のタプル) を使用します。
nil 値は、動的型付け言語で最も効果的に使用されることがわかりました。Smalltalk では、値が必要だが情報がない場合に使用される値です。Lua では、これはさらに効果的に使用されます。nil 値は、Lua テーブルでキーまたは値にすることができない唯一の値です。Lua では、欠落しているパラメーターまたは結果の値としても nil が使用されます。
全体として、動的に型付けされた設定では nil値が役立つ可能性があると言えますが、静的に型付けされた設定では、null型は、副作用のために実行される関数 (またはプロシージャまたはメソッド) について話す場合にのみ役立ちます。
C および Java で使用されるNULL ポインターは、何としても避けてください。これらは、ポインターとオブジェクトの実装に固有のアーティファクトであり、適切に設計された言語では許可されるべきではありません。必ず、既存の型を null 値で拡張する方法をユーザーに提供してください。ただし、意図的に明示的に行うようにしてください。(明示的な使用の例として、私は最近、Bentley と Sedgewick の三分探索木を Haskell に実装しました。「文字ではない」ことを意味する 1 つの値を追加して、文字型を拡張する必要がありました。この目的のために、Haskell はMaybe型を提供しています。)
最後に、コンパイラを作成している場合は、言語のコンパイルが最も簡単な部分と、バグの発生が最も少ない部分は、そこにない部分であることを覚えておくとよいでしょう :-)
nullの実際的な例は、yes / noの質問をして、応答がない場合です。回答が非常に重要な状況では質問が回答されなかったことを知ることが重要な場合があるため、デフォルトで「いいえ」に設定することは望ましくありません。
CではNULLは(void*(0))だったので、値(?)を持つ型でした。しかし、それは C++ テンプレートでは機能しなかったため、C++ は NULL 0 を作成し、型を削除して純粋な値になりました。
ただし、特定の NULL 型を使用する方がよいことが判明したため、彼ら (C++ 委員会) は NULL を (C++0x で) 再び型にすることを決定しました。
また、C++ 以外のほぼすべての言語には、型として NULL があるか、0 と同じではない同等の一意の値があります (それと等しいかどうかはわかりませんが、同じ値ではありません)。
したがって、C++ でさえ NULL を型として使用し、基本的にこの問題に関する議論を終了します。
編集:考えてみると、Haskell は NULL 型の別の解決策かもしれませんが、把握や実装は簡単ではありません。
null、nil、Noneなどと呼ぶかどうかにかかわらず、現在何も指していない参照またはポインターを示す方法があると便利なようですリンクされたリストの最後から。
Null はセンチネル値です。これは実際のデータではあり得ない値であり、代わりに使用中の変数に関するメタデータを提供します。
ポインターに割り当てられた null は、ポインターが初期化されていないことを示します。これにより、null 値のポインターの逆参照を検出することで、初期化されていないポインターの誤用を検出できます。ポインターの値をたまたまメモリ内にあるものと同じままにしておくと、プログラムの動作が非常に不規則になり、デバッグがはるかに困難になります。
また、C スタイルの可変長文字列のヌル文字は、文字列の末尾を示すために使用されます。
これらの方法での null の使用、特にポインター値の使用は非常に一般的になったため、「null」センチネル値がまったく異なる方法で実装され、数値 0 とは関係がない場合でも、メタファーが他のシステムにインポートされました。
任意のタイプを、操作のコレクションと共にセットと考えることができます。「通常の」値ではない値を持つと便利な場合が多くあります。たとえば、「EOF」値を考えてみましょう。Cのgetline()。いくつかの方法のいずれかでそれを処理できます: セットの外に NULL 値を持つことができます。特定の値を null として区別することができます (C では、((void *)0)その目的を果たすことができます)。または、新しい型を作成する方法を持つことができます。タイプTの場合、タイプT' =def { T ∪ NULL }を作成します。これは、Haskell が行う方法です (「多分」タイプ)。
どちらが優れているかは、多くの楽しい議論に適しています。
たとえば、C と Java の例を考えてみましょう。C では、null ポインターは数値ゼロであるという規則があります。もちろん、これは実際には慣例にすぎません。言語について、その値を特別なものとして扱うものは何もありません。しかし、Java ではnull、これは実際には悪い参照であり、反対側にあるものを確認するためにそのドアを開けようとするべきではないことを検出して知ることができる明確な概念です。
それでもヌルは何よりも嫌いです。
コメントに基づいた明確化: 私は 0 という事実上の null ポインター値が嫌いnullです。
null への代入を目にするたびに、「ああ、誰かが地雷をコードに埋め込んだ。いつか、関連する実行パスをたどってBOOM ! NullPointerException!」と思います。
私が望むのは、誰かが有用なデフォルトまたは NullObject を指定して、「このパラメーターが有用なものに設定されていない」ことを知らせてくれることです。ハゲのヌル自体は、発生するのを待っているだけの問題です。
とはいえ、生のゼロが自由に歩き回るよりはましです。
null は間違いではありません。Null は「まだわからない」という意味です。
プリミティブの場合、null は実際には必要ありません ((.NET の) 文字列はそれを取得すべきではないと言わざるを得ません)。
しかし、複合エンティティの場合、それは間違いなく目的を果たします。
nullはどのような目的を提供しますか?
ここでは、nullの2つの概念が機能していると思います。
最初の(論理標識がnull)は、プログラムロジックで初期化されていないメモリ参照の実行時表示を提供する従来のプログラム言語メカニズムです。
2番目(null値)は、論理式で論理nullインジケーター(前の定義)を検出し、プログラムコードで論理決定を行うために使用できる基本データ値です。
特にnullに賛成または反対の考えはありますか?
nullは何年にもわたって多くのプログラマーの悩みの種であり、多くのアプリケーション障害の原因でしたが、nullの概念には妥当性があります。あなたとあなたのチームが、参照が初期化されていないために誤用される可能性のあるメモリ参照を使用する言語を作成する場合、その不測の事態を検出するメカニズムが必要になる可能性があります。代替案を作成することは常にオプションですが、nullは広く知られている代替案です。
結論として、それはすべてあなたの言語の目標に依存します:
優先順位リストで堅牢性とプログラムの正当性が高く、プログラムによるメモリ参照を許可する場合は、nullを検討する必要があります。
BB
その決定は、プログラミング言語の目的によって異なります。
誰のためにプログラミング言語を設計していますか? C 派生言語に精通している人向けに設計されていますか? もしそうなら、おそらく null のサポートを追加する必要があります。
一般に、特定の目的に役立つ場合を除き、人々の期待に反することは避けるべきだと思います。
例として、C# のスイッチ ブロックを取り上げます。C# のすべてのケース ラベルには、すべての分岐で明示的な制御フロー式が必要です。つまり、それらはすべて「break」ステートメントまたは明示的な goto で終了する必要があります。つまり、このコードは合法ですが、次のことを意味します。
switch(x)
{
case 1:
case 2:
foo;
break;
}
このコードは合法ではありません:
switch (x)
{
case 1:
foo();
case 2:
bar();
break;
}
ケース 1 からケース 2 への「フォール スルー」を作成するには、次のように goto を挿入する必要があります。
switch (x)
{
case 1:
foo();
goto case 2;
case 2:
bar();
break;
}
これは間違いなく、C# に傾倒している C++ プログラマーの期待に反するものです。ただし、その制限を追加することは目的を果たします。一般的な C++ バグのクラス全体の可能性を排除します。言語の学習曲線がわずかに増えますが、その結果はプログラマーにとって正味の利益になります。
あなたの目標が C++ プログラマーを対象とした言語を設計することである場合、null を削除することはおそらく彼らの期待に反するでしょう。それは混乱を引き起こし、あなたの言語を学ぶのをより困難にします。重要な質問は、「彼らはどのような利益を得るか」です。または、代わりに、「これはどのような不利益を引き起こしますか」。
1学期で実装できる「超小型言語」を設計しようとしているだけなら、話は別です。その場合、目的は人口の特定のセグメントを対象とした有用な言語を構築することではありません。代わりに、コンパイラの作成方法を学ぶだけです。そのシナリオでは、より小さな言語を持つことは大きなメリットとなるため、null を排除する価値があります。
要約すると、次のことを行う必要があります。
通常、これにより、目的の結果がかなり明確になります。
もちろん、デザインの目標を明示的に明確にしない場合、またはそれらが何であるかについて同意できない場合でも、議論は続きます。ただし、その場合、とにかくかなり運命づけられています。
あなたのチームへの私の提案は次のとおりです。あなたの言語で書く必要のあるいくつかのサンプルプログラムを考え出し、あなたnullがそれを含めた場合と比較して、あなたが省略した場合にそれらがどのように見えるかを見てください。
null オブジェクト パターンを使用してください。
言語がオブジェクト指向の場合、UndefinedValueシングルトン インスタンスが 1 つだけ存在するクラスを持たせます。次に、このインスタンスnullを使用します。これには、やnullなどのメッセージに応答するという利点があります。Java のようにヌル ポインター例外が発生することはありません。(もちろん、これには言語が動的に型付けされている必要があります)。#toString#equals
Null は、その変数に値を割り当てることができないことを意味するプレースホルダーです (静的型付き言語の場合は「正しい型」を追加します)。
ここに認知的不協和があります。人間は否定を理解できない、というのはどこかで聞いたことがある。
静的に型付けされた言語を作成している場合、null によってコンパイラがかなり複雑になる可能性があると思います。
動的に型付けされた言語を作成している場合、NULL は非常に便利です。これは、バリエーションのない別の「型」であるためです。
ヌルはオブジェクトに対して、0は数値に対してです。
Null は、プログラムに必要なロジックとドメイン、または本質的に明確で合意された定義のない値を使用することによる将来の保守の意味を完全に考えていないプログラマーに簡単な方法を提供します。
最初は「価値がない」ことを意味していることは明白に思えるかもしれませんが、実際に何を意味するかは文脈によって異なります。たとえば、LastName === null の場合、その人に姓がない、または姓がわからない、またはシステムにまだ入力されていないことを意味しますか? null はそれ自体と等しいか、そうでないか? SQL ではそうではありません。多くの言語ではそうです。しかし、personA.lastName または personB.lastName の値がわからない場合、personA.lastName === personB.lastName をどうやって知ることができるでしょうか? 結果が false の場合、または .. . ヌル?
それはあなたが何をしているかに依存します。そのため、プログラムの他の部分と「null」の意味を正しく解釈するために、外部ライブラリまたはモジュールに実際に依存することはできません。
システム全体のあいまいな null の概念に依存するよりも、lastName の可能な値の DOMAIN を明確に定義し、すべての可能な値が実際に何を意味するかを明確にする方がはるかに優れています。 、使用している言語と何をしようとしているのかによって異なります。実際には、データの操作を開始すると、値がまったく間違った方法で動作する可能性があります。