nullが悪い場合、なぜ現代の言語はそれを実装するのですか?[閉まっている]


82

JavaやC#のような言語のデザイナーは、null参照の存在に関連する問題を知っていたはずですnull参照は本当に悪いことですか?を参照)。また、オプションタイプの実装は、null参照ほど複雑ではありません。

とにかくそれを含めることにしたのはなぜですか?null参照の欠如は、言語の作成者とユーザーの両方から、より良い品質のコード(特により優れたライブラリ設計)を奨励(または強制)するだろうと確信しています。

それは単に保守主義のせいなのでしょうか-「他の言語にもあります、我々もそれを持たなければなりません...」?


99
nullは素晴らしいです。私はそれが大好きで、毎日使っています。
ピーターB

17
@PieterBしかし、大部分の参照に使用しますか、それともほとんどの参照をnullにしたくないのですか?引数は、nullを許可するデータがあってはならないということではなく、明示的でオプトインする必要があるということだけです。

11
@PieterBしかし、過半数をNULL可能にすべきではない場合、デフォルトではなくNULL可能性を例外にすることは意味がありませんか?オプションタイプの通常の設計は、不在とアンパックの明示的なチェックを強制することですが、オプトインのNULL可能参照の既知のJava / C#/ ...セマンティクスを持つこともできることに注意してくださいnullの場合)。少なくともいくつかのバグを防止し、nullチェックの欠落について文句を言う静的分析をより実用的にします。

20
WTFは皆さんと一緒ですか?ソフトウェアで問題が発生する可能性があることと実際に問題が発生することのうち、nullを逆参照しようとしても問題はありません。常にAV / segfaultが生成されるため、修正されます。あなたはこれを心配しなければならないほどのバグ不足がありますか?もしそうなら、私はたくさんの予備があり、それらのどれもnull参照/ポインタの問題を引き起こしません。
マーティンジェームズ

13
@MartinJames「常にAV /セグメンテーション違反を生成し、修正される」-いいえ、いいえ。
14年

回答:


97

免責事項:私は個人的に言語デザイナーを知らないので、私があなたに与えるどんな答えも推測になります。

トニー・ホア自身から:

私はそれを10億ドルの間違いと呼んでいます。それは1965年のnull参照の発明でした。当時、私はオブジェクト指向言語(ALGOL W)での参照のための最初の包括的な型システムを設計していました。私の目標は、コンパイラによって自動的に実行されるチェックを使用して、参照のすべての使用が絶対に安全であることを保証することでした。しかし、実装が非常に簡単だったという理由だけで、null参照を挿入する誘惑に抵抗することはできませんでした。これにより、無数のエラー、脆弱性、およびシステムクラッシュが発生し、過去40年間で数十億ドルの痛みと損害をもたらした可能性があります。

強調鉱山。

当然、それは彼にとって悪い考えのようには思われませんでした。同じ理由で部分的に永続化されている可能性があります-チューリング賞を受賞したクイックソートの発明者にとって良いアイデアのように思えても、多くの人々がそれがなぜ悪なのか理解しいないことは驚くことではありません。それは、マーケティングと学習曲線の両方の理由から、新しい言語が古い言語に似ていると便利だからです。適例:

「私たちはC ++プログラマーの後を追いました。私たちはそれらの多くをLispに半分ほど引きずり込むことができました。」 -Guy Steele、Java仕様の共著者

(出典:http : //www.paulgraham.com/icad.html

そしてもちろん、Cにはnullがあるため、C ++にはnullがあり、Cの歴史的な影響を検討する必要はありません。C#はMicrosoftのJava実装であるJ ++に取って代わり、Windows開発用の言語としてC ++にも取って代わったため、どちらからでも入手できました。

編集ここには、検討する価値があるホアからの別の引用があります:

プログラミング言語は、全体として非常に複雑です。オブジェクト指向、継承、およびその他の機能は、一貫性のある科学的に十分な根拠のある規律または正しさの理論の観点からまだ考慮されていません。 。私の生涯にわたって科学者として追求してきた私の最初の仮定は、まともなプログラミング言語の設計に収束する手段として、正確さの基準を使用していることです。プログラムのさまざまなコンポーネントがその仕様のさまざまなコンポーネントに明確に対応しているので、それについて構成的に推論できます。[...]コンパイラを含むツールは、正しいプログラムを作成するという意味の理論に基づいている必要があります。 -2002年7月17日、イギリス、ケンブリッジのフィリップL.フラナによる口頭履歴インタビュー。ミネソタ大学チャールズバベッジ研究所。[ http://www.cbi.umn.edu/oh/display.phtml?id=343]

繰り返しますが、私のものを強調します。サン/オラクルとマイクロソフトは企業であり、どの企業でも収益はお金です。彼らにとっての利点nullは短所を上回ったかもしれないし、単に彼らが問題を十分に検討するには締め切りが厳しかったかもしれない。締め切りが原因で発生した可能性のある別の言語ミスの例として:

Cloneableが壊れているのは残念ですが、それは起こります。オリジナルのJava APIは、市場の終盤を迎えるために、厳しい締め切りの下で非常に迅速に行われました。元のJavaチームは素晴らしい仕事をしましたが、すべてのAPIが完璧というわけではありません。Cloneableは弱点であり、人々はその制限に注意する必要があると思います。-ジョシュ・ブロック

(出典:http : //www.artima.com/intv/bloch13.html


32
親愛なるダウンボーター:どうすれば答えを改善できますか?
ドーバル14年

6
あなたは実際に質問に答えませんでした。あなたは、いくつかの事後の意見についての引用と、「コスト」についての余分な手を振るだけを提供しました。(nullが10億ドルの間違いである場合、MSとJavaを実装することでその負債を削減して、その負債を減らすべきではないでしょうか?)
DougM

29
@DougM過去50年間のすべての言語デザイナーを見つけて、なぜ彼nullが自分の言語で実装したのかを尋ねてください。この質問に対する答えは、言語設計者からのものでない限り投機的です。Eric Lippert以外にこのサイトに頻繁にアクセスすることは知りません。最後の部分は、さまざまな理由でニシンです。MSおよびJavaのAPIの上に記述されたサードパーティのコードの量は、明らかにAPI自体のコードの量を上回ります。顧客が望むならnull、あなたはそれらを与えますnull。また、彼らが受け入れたことnullは彼らにお金がかかっていると思います。
ドーバル14年

3
あなたが与えることができる唯一の答えが投機的である場合、最初の段落でそれを明確に述べてください。(答えを改善する方法を尋ねられたので、私は答えました。括弧は、単に自由に無視できるコメントです。結局、それは英語の括弧です。)
DougM

7
この答えは合理的です。私はいくつかの考慮事項を追加しました。ICloneable.NETでも同様に壊れていることに注意してください。残念ながら、これはJavaの欠点が時間から学ばなかった1つの場所です。
エリックリッパー

121

JavaやC#などの言語のデザイナーは、null参照の存在に関連する問題を知っていたと確信しています。

もちろん。

また、オプションタイプの実装は、null参照ほど複雑ではありません。

失礼ですが同意できません!C#2のnull値を許容する値の型に関する設計上の考慮事項は、複雑で議論の余地があり、困難でした。彼らは言語とランタイムの両方の設計チームに何ヶ月もの議論、プロトタイプの実装などを行いました。実際、nullable boxingのセマンティクスはC#2.0の出荷に非常に近く変更されました。

とにかくそれを含めることにしたのはなぜですか?

すべての設計は、多くの微妙かつ著しく互換性のない目標の中から選択するプロセスです。考慮される要因のほんのいくつかの簡単なスケッチを与えることができます。

  • 言語機能の直交性は一般に良いことと考えられています。C#には、null許容値型、null許容値型、null許容参照型があります。NULL不可の参照型は存在しないため、型システムは非直交になります。

  • C、C ++、およびJavaの既存のユーザーに対する知識が重要です。

  • COMとの容易な相互運用性が重要です。

  • 他のすべての.NET言語との容易な相互運用性が重要です。

  • データベースとの容易な相互運用性が重要です。

  • セマンティクスの一貫性が重要です。TheKingOfFranceがnullに等しい参照を持っている場合、それは常に「現在、フランスの王がいない」ことを意味しますか、それとも「フランスの王が間違いなくあるか、私はちょうどそれが今誰なのかわかりません」という意味ですか?または、「フランスに王を持つという概念は無意味であるため、質問すらしないでください!」という意味ですか?Nullは、C#ではこれらすべてのことを意味し、これらの概念はすべて有用です。

  • パフォーマンスコストは重要です。

  • 静的解析を受け入れることが重要です。

  • 型システムの一貫性が重要です。私たちはすることができます常に null非許容参照がないことを知っている決して下に任意の無効であることが観察状況?参照型のnull不可フィールドを持つオブジェクトのコンストラクターはどうですか?このようなオブジェクトのファイナライザでは、参照を埋めることが想定されていたコードが例外をスローしたため、オブジェクトはファイナライズされます。その保証についてあなたに嘘をついている型システムは危険です。

  • そして、セマンティクスの一貫性はどうですか?null 値は使用されると伝播しますが、null 参照は使用されると例外をスローします。それは矛盾しています。その矛盾は何らかの利益によって正当化されますか?

  • 他の機能を壊さずに機能を実装できますか?この機能は、他にどのような将来可能な機能を排除しますか?

  • あなたが望む軍隊ではなく、あなたが持っている軍隊と戦争に行きます。C#1.0にはジェネリックが含まれていなかったことを忘れないでください。そのためMaybe<T>、代替手段として説明するのは完全な非スターターです。ランタイムチームがnull参照を排除するためだけにジェネリックを追加している間に、.NETは2年間スリップするはずでしたか?

  • 型システムの一貫性はどうですか?Nullable<T>任意の値型について言うことができます-いいえ、待って、それは嘘です。言うことはできませんNullable<Nullable<T>>。できるはずですか?もしそうなら、その望ましいセマンティクスは何ですか?型システム全体にこの機能のためだけに特別なケースを持たせることは価値がありますか?

等々。これらの決定は複雑です。


12
特にジェネリック医薬品の導入以外はすべて+1。JavaとC#の歴史の両方に、ジェネリックが存在しなかった期間があったことを忘れがちです。
ドーバル14年

2
たぶん愚かな質問(私はちょうどIT学部)-しかし、使用する前に「has-value」チェックを必要とする通常のnull可能な参照として、構文レベルでオプションタイプを実装できませんでした(CLRはそれについて何も知らない)コード?オプションのタイプは、実行時にチェックを必要としないと思います。
mrpyo 14年

2
@mrpyo:確かに、それは可能な実装の選択肢です。他の設計上の選択肢はなくならないため、その実装の選択肢には多くの長所と短所があります。
エリックリッパー

1
@mrpyo「has-value」チェックを強制することはお勧めできません。理論的には非常に良いアイデアですが、実際にはIMOはコンパイラを満足させるためにあらゆる種類の空のチェックをもたらします-Javaのチェック例外や、それをだましている人catchesは何もしません。無効な状態で操作を継続するよりも、システムを爆破させる方が良いと思います。
何も不可能

2
@voo:null不可の参照型の配列は、多くの理由で困難です。多くの可能な解決策があり、それらはすべて異なる操作にコストを課します。Supercatの提案は、要素が割り当てられる前に法的に読み取り可能かどうかを追跡することであり、コストがかかります。配列が表示される前に各要素でイニシャライザが実行されるようにすることで、異なるコストがかかります。だからここにこだわる:これらのテクニックのどれを選んだとしても、誰かが彼らのペットのシナリオに対して効率的でないと不満を言うだろう。これは機能に対する重大なポイントです。
エリックリッパー

28

Nullは、価値の欠如を表すという非常に有効な目的に役立ちます。

私はヌルの乱用と、特に自由に使用した場合に生じる可能性のあるすべての頭痛と苦痛について、私が知っている最も声の強い人だと言います。

私の個人的なスタンスは、人々がヌルを使用することができるのは、それが必要かつ適切であると正当化できる場合のみです。

nullを正当化する例:

死亡日は通常、null許容フィールドです。死亡日には3つの状況が考えられます。人が死亡して日付がわかっているか、人が死亡して日付が不明であるか、人が死んでいないために死の日付が存在しないかのいずれかです。

死亡日もDateTimeフィールドであり、「不明」または「空」の値はありません。使用する言語に応じて異なる新しい日時を作成するときに表示されるデフォルトの日付がありますが、技術的にはその時点で実際に死亡した可能性があり、「空の値」としてフラグを立てる可能性がありますデフォルトの日付を使用します。

データは状況を適切に表す必要があります。

人は死の日付が知られています(3/9/1984)

シンプル、 '3/9/1984'

人は死んでいます死亡日は不明です

それでは、何がベストですか?Null、 '0/0/0000'、または'01 / 01/1869 '(またはデフォルト値は何ですか?)

人が死んでいない死の日付は適用されません

それでは、何がベストですか?Null、 '0/0/0000'、または'01 / 01/1869 '(またはデフォルト値は何ですか?)

それでは、それぞれの価値を考えてみましょう...

  • Null、それはあなたが警戒する必要がある意味と懸念を持っています、例えばそれが最初にnullでないことを確認せずに誤って操作しようとすると例外がスローされますが、実際の状況を最もよく表します...人が死んでいない場合死の日付は存在しません...それは何もない...それはnullです...
  • 0/0/0000、これは一部の言語では問題ない可能性があり、日付なしの適切な表現である可能性さえあります。残念なことに、一部の言語と検証では、これを無効な日時として拒否するため、多くの場合、手間がかかりません。
  • 1/1/1869(またはデフォルトのdatetime値が何であれ)、ここでの問題は処理が難しいことです。あなたは価値の価値の欠如としてそれを使用することができますが、私が死の日付を持っていないすべての私の記録を除外したい場合はどうなりますか?データ整合性の問題を引き起こす可能性のある、その日に実際に死亡した人々を簡単に除外できました。

実際には、時にはあなたがされています何を表していないする必要があると必ず、時々変数の型は、そのためにうまく動作しますが、多くの場合、変数の型は何を表していないことができるようにする必要があります。

リンゴがない場合、リンゴは0個ありますが、リンゴの数がわからない場合はどうなりますか?

ヌルはすべての手段で乱用され、潜在的に危険ですが、時には必要です。多くの場合、これはデフォルトです。なぜなら、値を提供するまで、値の欠如とそれを表現するために何かが必要だからです。(ヌル)


37
Null serves a very valid purpose of representing a lack of value.OptionまたはMaybe型が型システムをバイパスすることなく、この非常に有効な目的を果たします。
ドーバル14年

34
値不足の値があってはならないと主張する人はいません。すべての値が潜在的に欠落しているのではなく、欠落している可能性のある値を明示的にマークする必要があると主張しています。

2
RualStorgeはSQLに関連して話していたと思います。なぜなら、すべての列をとしてマークする必要があると述べるキャンプがあるからNOT NULLです。私の質問は...しかしRDBMSに関連していなかった
mrpyo

5
「値なし」と「不明な値」を区別するための+1
David

2
人の状態を区別する方が理にかなっていますか?すなわちPersonタイプがありstateタイプのフィールドState判別の労働組合である、AliveとしますDead(dateOfDeath : Date)
ジョンハンソン

10

「他の言語が持っている、私たちも持っている必要があります...」までは、ジョーンズに追いつくようなものではありません。新しい言語の重要な機能は、他の言語の既存のライブラリと相互運用できることです(Cを参照)。Cにはヌルポインターがあるため、相互運用性レイヤーには必然的にヌルの概念が必要です(または、使用時に爆発する他の「存在しない」同等物)。

言語設計者は、オプションタイプを使用し、ヌルになる可能性のあるすべての場所でヌルパスを処理するように選択することもできます。そして、それはほぼ確実にバグを減らすことにつながります。

しかし、(特にJavaとC#の場合、導入のタイミングと対象読者のために)この相互運用性レイヤーにオプションタイプを使用すると、採用が魚雷にならないとしても害を及ぼす可能性があります。90年代半ばから後半のC ++プログラマーの悩みの種であるオプションタイプが最後まで渡されるか、90年代半ばから後半のC ++プログラマーの地獄を悩ませる相互運用性層がヌルを検出すると例外がスローされます。 ..


3
最初の段落は私には意味がありません。Javaには、あなたが提案する形のC相互運用機能がありません(JNIがありますが、参照に関連するすべてのことで既に数十のフープを飛び越えます。さらに、実際にはほとんど使用されません)。

@delnan-申し訳ありませんが、私はこの種の相互運用性を備えたC#に精通しています。むしろ、基礎となるJavaライブラリの多くは、下部にあるJNIも使用していると思いました。
テラスティン14年

6
nullを許可することについて適切な議論をしますが nullを奨励せずに許可することはできます。Scalaはその良い例です。nullを使用するJava APIとシームレスに相互運用できますが、OptionScala内で使用するためにラップすることをお勧めしますval x = Option(possiblyNullReference)。実際には、人々がの利点を見るのにそれほど時間はかかりませんOption
カールビーレフェルト

1
オプションタイプは、(静的に検証された)パターンマッチングと密接に関連していますが、残念ながらC#にはありません。F#はそうですが、素晴らしいです。
スティーブン・エヴァース

1
@SteveEversプライベートコンストラクター、シールされた内部クラス、およびMatch引数としてデリゲートを受け取るメソッドを含む抽象基本クラスを使用して、偽造することができます。次に、ラムダ式をMatch(名前付き引数を使用するためのボーナスポイント)に渡しMatch、正しいものを呼び出します。
ドーバル14年

7

まず第一に、無効の概念が必要であるという点で私たち全員が同意できると思います。情報がないことを表す必要がある状況がいくつかあります。

null参照(およびポインター)を許可することは、この概念の実装の1つにすぎず、おそらく最も一般的なものですが、問題があることはわかっていますが、C、Java、Python、Ruby、PHP、JavaScriptなどはすべて同様のものを使用しますnull

どうして ?さて、代替手段は何ですか?

Haskellなどの関数型言語では、 Option or Maybe型があります。ただし、これらは次のものに基づいています。

  • パラメトリック型
  • 代数データ型

さて、元のC、Java、Python、RubyまたはPHPはこれらの機能のいずれかをサポートしていましたか?いいえ。Javaの欠陥ジェネリックは言語の歴史の中で最近のものであり、他のそれらを実装することすらまったく疑っています。

そこにあります。null簡単で、パラメトリック代数データ型はより困難です。人々は最も単純な代替手段を求めました。


「nullは簡単で、パラメトリック代数データ型はより困難です」に対する+1 しかし、パラメトリックタイピングやADTの問題はそれほど問題ではなかったと思います。必要だと認識されていないだけです。一方、Javaがオブジェクトシステムなしで出荷されていた場合、それは失敗していました。OOPは「ショーストップ」機能でした。それがなければ、誰も興味を持っていませんでした。
ドーバル

@Doval:まあ、OOPはJavaに必要だったかもしれませんが、Cには必要ありませんでした:)しかし、Javaがシンプルであることを目指していたことは事実です。残念なことに、人々は単純な言語が単純なプログラムにつながると思っているようですが、それはちょっと奇妙です(Brainfuckは非常に単純な言語です...)が、複雑な言語(C ++ ...)も万能薬ではないことは確かですそれらは非常に便利です。
マチューM.

1
@MatthieuM .:実システムは複雑です。複雑さがモデル化されている実世界のシステムと一致する適切に設計された言語により、複雑なシステムを単純なコードでモデル化できます。言語を過度に単純化しようとすると、その複雑さを使用しているプログラマーに押し付けるだけです。
supercat

@supercat:私はこれ以上同意できませんでした。または、アインシュタインが言い換えているように、「すべてをできるだけシンプルにしますが、シンプルではありません。」
マチューM.

@MatthieuM .:アインシュタインは多くの点で賢明でした。「すべてがオブジェクトであり、参照が保存される可能性がある」と想定しようとする言語は、Object実用的なアプリケーションが非共有の可変オブジェクトと共有可能な不変オブジェクト(どちらも値のように振る舞う必要がある)および共有可能および共有不可能であることを認識できませんエンティティ。Objectすべてに単一のタイプを使用しても、そのような区別の必要性がなくなるわけではありません。それらを正しく使用するのが難しくなるだけです。
supercat

5

Null / nil / none自体は悪ではありません。

彼の誤解を招く名前の有名なスピーチ「The Billion dollar Mistake」を見た場合、トニー・ホアは変数がヌルを保持できるようにすることは大きな間違いだったと語っています。選択肢-オプションを使用する-は実際にはnull参照を取り除きませ。代わりに、nullを保持できる変数とそうでない変数を指定できます。

実際のところ、適切な例外処理を実装する最新の言語では、null参照解除エラーは他の例外と何の違いもありません-あなたはそれを見つけて、それを修正します。null参照のいくつかの代替手段(たとえば、Null Objectパターン)はエラーを隠し、ずっと後まで静かに失敗します。私の意見では、速く失敗するのがはるかに良いです

質問は、言語がオプションの実装に失敗するのはなぜですか?実際問題として、C ++のほとんどで最も人気のある言語には、割り当てられないオブジェクト変数を定義する機能がありますNULL。これは、トニー・ホアが演説で言及した「ヌル問題」の解決策です。次に人気のある型付き言語であるJavaにそれがないのはなぜですか?一般に、特にその型システムに多くの欠陥がある理由を尋ねる人がいるかもしれません。言語が体系的にこの間違いを犯していると本当に言えるとは思いません。ある人はそう、そうでない人もいます。


1
実装の観点から見たJavaの最大の強みの1つですが、言語の観点から見た弱点は、非プリミティブ型が1つしかないことです。それは、無差別オブジェクト参照です。これにより、ランタイムが大幅に簡素化され、非常に軽量なJVM実装が可能になります。ただし、その設計では、すべてのタイプにデフォルト値が必要であり、無差別オブジェクト参照の場合、可能なデフォルトはのみですnull
supercat

まあ、とにかく1つのルート非プリミティブ型。なぜこれが言語の観点からの弱点なのですか?この事実がすべてのタイプにデフォルト値を持つことを必要とする理由(または逆に複数のルートタイプがタイプにデフォルト値を持たせない理由)も、それが弱点なのか理解できません。
BT

フィールドまたは配列要素が保持できる他の種類の非プリミティブ?弱点は、IDをカプセル化するために使用される参照と、それによって識別されるオブジェクト内に含まれる値をカプセル化するために使用される参照があることです。識別情報をカプセル化するために使用される参照型変数の場合null、唯一の実用的なデフォルトです。ただし、値をカプセル化するために使用される参照は、型が賢明なデフォルトインスタンスを持つか、構築できる場合に、賢明なデフォルト動作を行うことができます。参照がどのように振る舞うべきかの多くの側面がかどうか、およびそれらがどのように値をカプセル化によって異なりますが、...
supercat

... Java型システムにはそれを表現する方法がありません。場合はfoo保持への参照のみint[]含む{1,2,3}とコードが欲しいfooへの参照を保持するためにint[]含む{2,2,3}ことがインクリメントするだろう達成するために、最速の方法をfoo[0]。コードがfoo保持することをメソッドに知らせたい場合{1,2,3}、他のメソッドは配列を変更せず、変更しfooたいポイントを超えて参照を永続化しません。これを達成する最も速い方法は、配列に参照を渡すことです。Javaは、「一時的な読み取り専用の参照」そしてタイプ、...持っていた場合
supercatを

...配列は一時参照として安全に渡され、その値を保持したいメソッドはそれをコピーする必要があることを知っています。このような型が存在しない場合、配列の内容を安全に公開する唯一の方法は、配列のコピーを作成するか、その目的のために作成されたオブジェクトにカプセル化することです。
supercat

4

プログラミング言語は一般に、技術的に正しいというよりは、実用的に役立つように設計されているためです。事実は、null状態は、不良または欠落したデータ、またはまだ決定されていない状態による一般的な発生であるということです。技術的に優れたソリューションは、単にヌル状態を許可し、プログラマーがミスを犯すという事実を吸い上げるよりも扱いにくいです。

たとえば、ファイルを操作する簡単なスクリプトを作成する場合、次のような擬似コードを作成できます。

file = openfile("joebloggs.txt")

for line in file
{
  print(line)
}

また、joebloggs.txtが存在しない場合は失敗します。問題は、おそらく大丈夫な単純なスクリプトの場合と、より複雑なコードの多くの状況では、それが存在し、障害が発生しないことがわかっているため、チェックを強制することで時間を浪費することです。より安全な代替手段は、潜在的な障害状態に適切に対処することを強制することで安全性を達成しますが、多くの場合、私はそれをしたくないので、ただ乗りたいです。


13
そして、ここでは、nullの正確な問題の例を示しました。適切に実装された「openfile」関数は、何が起こったのかを正確に説明してすぐに実行を停止する例外(ファイルがない場合)をスローする必要があります。代わりに、nullを返す場合、さらに伝播(to for line in file)し、無意味なnull参照例外をスローします。nullが存在しなければ、「openfile」の設計者はこの間違いを犯すことはできません。
mrpyo 14年

2
+1「プログラミング言語は一般に、技術的に正しいというよりも実際に役立つように設計されているため」
マーティンBa

2
私が知っているすべてのオプションの種類では、1回の短い追加メソッド呼び出しで、nullの失敗を実行できます(例:)let file = something(...).unwrap()。POVに応じて、エラーまたはnullが発生しないという簡潔なアサーションを処理しない簡単な方法です。無駄な時間は最小限であり、他の場所で時間を節約できます。何かヌルになる可能性があるかどうかを把握する必要がないからです。もう1つの利点(それだけで追加の呼び出しに値する場合があります)は、エラーケースを明示的に無視することです。失敗した場合、何が問題で、どこに修正が必要かはほとんど疑いありません。

4
@mrpyoすべての言語が例外や例外処理(またはtry / catch)をサポートしているわけではありません。また、例外も悪用される可能性があります。「フロー制御としての例外」は、一般的なアンチパターンです。このシナリオ-ファイルは存在しません-は、そのアンチパターンの最も頻繁に引用される例です。ある悪い習慣を別の習慣に置き換えているように見えます。
デビッド

8
@mrpyo if file exists { open file }は競合状態に苦しんでいます。ファイルを開くことが成功するかどうかを確認する唯一の信頼できる方法は、ファイルを開くことです。

4

そこの明確な、実用的な用途はあるNULL(またはnil、またはNil、またはnull、またはNothingポインタがまたはそれが希望する言語で呼ばれているものは何でも)。

例外システムを持たない言語(Cなど)では、ポインターを返す必要がある場合、エラーのマークとしてNULLポインターを使用できます。例えば:

char *buf = malloc(20);
if (!buf)
{
    perror("memory allocation failed");
    exit(1);
}

ここでNULLは、からの戻り値malloc(3)が失敗のマーカーとして使用されます。

メソッド/関数の引数で使用する場合、引数のデフォルトの使用を示すか、出力引数を無視できます。以下の例。

例外メカニズムを備えた言語であっても、特に例外処理が高価な場合(Objective-Cなど)には、ソフトエラー(つまり、回復可能なエラー)の表示としてNULLポインターを使用できます。

NSError *err = nil;
NSString *content = [NSString stringWithContentsOfURL:sourceFile
                                         usedEncoding:NULL // This output is ignored
                                                error:&err];
if (!content) // If the object is null, we have a soft error to recover from
{
    fprintf(stderr, "error: %s\n", [[err localizedDescription] UTF8String]);
    if (!error) // Check if the parent method ignored the error argument
        *error = err;
    return nil; // Go back to parent layer, with another soft error.
}

ここで、ソフトエラーは、プログラムがキャッチされない場合でもクラッシュすることはありません。これにより、Javaのようなクレイジーなtry-catchがなくなり、ソフトエラーが中断されないため、プログラムフローをより適切に制御できます(そして、残りのいくつかのハード例外は通常、回復できず、キャッチされません)


5
問題は、決して含むべきでない変数を、あるべき変数と区別する方法がないことnullです。たとえば、Javaで5つの値を含む新しい型が必要な場合、enumを使用できますが、6つの値を保持できる型(必要な5 + null)を取得できます。型システムの欠陥です。
ドーバル14年

@Dovalそれが状況にNULLを割り当てる場合(またはデフォルトがある場合、デフォルト値の同義語として扱う)、またはソフトエラーのマーカーとしてNULL(最初に表示されるべきではない)を使用する(つまり、エラーですが、少なくともまだクラッシュしていません)
Maxthon Chan

1
@MaxtonChanにNullは、型の値にデータがない場合(enum値など)にのみ意味を割り当てることができます。値がより複雑なもの(構造体など)にnullなるとすぐに、その型に意味のある意味を割り当てることはできません。をnull構造体またはリストとして使用する方法はありません。また、nullエラー信号として使用する場合の問題は、何がnullを返すか、nullを受け入れる可能性があるかを判断できないことです。プログラム内の変数は、使用する前にすべての変数nullをチェックすることが非常に細心の注意を払っていない限り、可能性がありますnull
ドーバル14年

1
@Doval:不変の参照型をnull使用可能なデフォルト値と見なすことに固有の困難はありません(たとえば、デフォルト値がstring空の文字列として振る舞う、前述の共通オブジェクトモデルのように)。必要だったのは、非仮​​想メンバーを呼び出すときcallではなく、言語で使用することだけcallvirtでした。
supercat

@supercatこれは良い点ですが、不変型と不変型を区別するためのサポートを追加する必要はありませんか?それが言語にどれほど些細なことを加えるのか、私にはわかりません。
ドーバル

4

2つの関連する問題がありますが、わずかに異なります。

  1. nullまったく存在すべきですか?またはMaybe<T>、nullが便利な場所で常に使用する必要がありますか?
  2. すべての参照はNULL可能にすべきですか?そうでない場合、どちらがデフォルトになりますか?

    null可能な参照型を明示的に宣言することstring?で、nullプログラマーが慣れているものとあまり違わずに、問題の原因のほとんど(すべてではない)を回避できます。

少なくとも、すべての参照がNULL可能であるべきではないことに同意します。しかし、nullを回避することには複雑さが伴います:

.NETはdefault<T>、マネージコードが最初にアクセスする前にすべてのフィールドを初期化します。これは、必要な参照型nullまたは同等のものが必要な場合、コードを実行せずに値の型をゼロに初期化できることを意味します。これらの両方に重大な欠点がありdefaultますが、初期化の単純さがこれらの欠点を上回っていた可能性があります。

  • たとえば、フィールドthisをマネージコードに公開する前にフィールドの初期化を要求することで、この問題を回避できます。Spec#は、C#と比較してコンストラクターチェーンとは異なる構文を使用して、このルートに進みました。

  • 以下のために静的フィールドあなたは単に隠すことができないので、フィールド初期化子で実行することができるコードの種類に強い制約ポーズをしない限り、これを確実に、困難であるthisポインタを。

  • 参照型の配列を初期化する方法は?List<T>容量が長さよりも大きい配列によってサポートされているを検討してください。残りの要素には何らかの値が必要です。

もう一つの問題は、それがのようなメソッドを許可しないということであるbool TryGetValue<T>(key, out T value)返すdefault(T)ようvalue、彼らは何かを見つけるしない場合。この場合、最初はoutパラメーターの設計が悪いと主張するのは簡単です。このメソッドは、判別ユニオンまたは多分代わりに返される必要があります。

これらの問題はすべて解決できますが、「nullを禁止してすべてがうまくいく」ほど簡単ではありません。


List<T>それは、すべてのことのいずれかが必要となるので、私見最高の一例であるTバッキングストア内のすべての項目があることを、デフォルト値を持っているMaybe<T>場合でも、余分な「のisValid」フィールドでTあるMaybe<U>、またはのためのコードというList<T>振る舞いが異なっによってTそれ自体がNULL入力可能な型であるかどうか。T[]要素をデフォルト値に初期化することは、これらの選択肢の中で最も悪ではないと考えますが、もちろん、要素にデフォルト値必要であることを意味します。
supercat

さびはポイント1に続きます。ヌルはまったくありません。Ceylonはポイント2に続きます-デフォルトでは非ヌルです。nullの可能性がある参照は、参照またはnullのいずれかを含むユニオン型で明示的に宣言されますが、nullがプレーンな参照の値になることはありません。結果として、言語は完全に安全であり、セマンティック上不可能なのでNullPointerExceptionはありません。
ジムバルター

2

最も有用なプログラミング言語では、データ項目を任意のシーケンスで読み書きできるため、プログラムが実行される前に読み書きの順序を静的に決定できないことがよくあります。実際には、コードが読み取る前に実際にすべてのスロットに有用なデータを保存する場合が多くありますが、それを証明することは困難です。したがって、少なくとも理論的にはコードがまだ有用な値で書かれていないものを読み取ろうとするプログラムを実行する必要があります。コードがそうすることが合法であるかどうかにかかわらず、コードが試行を行うのを止める一般的な方法はありません。唯一の質問は、それが発生したときに何をすべきかです。

言語とシステムが異なれば、アプローチも異なります。

  • 1つのアプローチは、書き込まれていないものを読み取ろうとすると、すぐにエラーが発生するということです。

  • 2番目のアプローチは、格納された値が意味的に有用である方法がなくても、コードがすべての場所でそれを読み取る前に何らかの値を提供することを要求することです。

  • 3番目のアプローチは、単純に問題を無視し、「自然に」起こることは何でも起こるようにすることです。

  • 4番目のアプローチは、すべてのタイプにデフォルト値が必要であり、他に何も書き込まれていないスロットはすべてデフォルト値になるということです。

アプローチ#4はアプローチ#3よりもはるかに安全であり、一般にアプローチ#1および#2よりも安価です。その場合、参照型のデフォルト値はどうあるべきかという疑問が残ります。不変の参照型の場合、多くの場合、デフォルトのインスタンスを定義し、その型の変数のデフォルトはそのインスタンスへの参照であると言うのが理にかなっています。ただし、可変参照型の場合、これはあまり役に立ちません。書き込み可能な参照型を記述する前に使用しようとすると、通常、使用しようとした時点でトラップする以外の安全なアクションはありません。

意味的に言えば、customerstypeの配列がありCustomer[20]、にCustomer[4].GiveMoney(23)何も保存せずに試行したCustomer[4]場合、実行はトラップする必要があります。読み取りのCustomer[4]試行はコードが試行するまで待つのではなく、すぐにトラップする必要があると主張することができGiveMoneyますが、スロットを読み取り、値を保持していないことがわかり、それを利用するのに役立つ十分なケースがあります読み取り試行自体が失敗するという情報は、しばしば大きな迷惑になります。

一部の言語では、特定の変数にnullを含めないように指定できます。また、nullを保存しようとすると、直ちにトラップがトリガーされます。これは便利な機能です。ただし、一般に、プログラマーが参照の配列を作成できるようにする言語は、null配列要素の可能性を考慮に入れるか、配列要素の初期化を意味のないデータに強制する必要があります。


ないでしょうMaybe/ Optionあなたはあなたの参照のために値を持っていない場合ので、タイプは、#2の問題を解決するため、まだ将来的には1を持って、あなただけ保存することができますNothingMaybe <Ref type>
ドーバル

@Doval:いいえ、それは問題を解決しません-少なくとも、再びnull参照を導入することなしではありません。「何もない」タイプのメンバーのように振る舞うべきですか?もしそうなら、どれ?または、例外をスローする必要がありますか?どちらの場合、単純にnull正しく/賢明に使用するよりも良い方法はありますか?
cHao

@Doval:のバッキングタイプはList<T>a T[]またはa Maybe<T>ですか?のバッキングタイプはList<Maybe<T>>どうですか?
supercat

@supercat 単一の値を保持しているので、バッキングタイプがどのようMaybeな意味を持つかわかりません。という意味ですか?ListMaybeMaybe<T>[]
ドーバル

@cHao Nothingはtypeの値にのみ割り当てることができるMaybeため、を割り当てるようなものではありませんnullMaybe<T>Tは、2つの異なるタイプです。
ドーバル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.