プログラマが知るべき97のこと/プリミティブ型よりドメイン固有の型を

1999年9月23日、火星探査機「マーズ・クライメイト・オーピター(MCO)」は火星を周回する軌道への突入に失敗し、燃え尽きました。3億2,730万ドルが失われた原因はソフトウェアのエラーでした。そのエラーは、具体的には「単位の混在」でした。同じ数値の単位を、地上のソフトウェアではポンドとしていたのに対し、宇宙船ではニュートンとしていたのです。その結果地上では、宇宙船のスラスタ推力を実際の約4.45分の1とみなしてしまうことになりました。

データの型付けがもっと強ければ、あるいはドメイン固有の型が使われていれば問題の発生を防げたという事例は数多くありますが、MCOの事故もその1つと言えるでしょう。プログラミング言語Adaには、これを根拠とする機能が多数組み込まれています。Adaは、安全性が特に重要視される組み込みソフトウェアの実装を目的として設計された言語だからです。Adaは型付けが強く、プリミティブ型とユーザ定義型の両方について、以下のような静的チェックをするという特徴があります。

type Velocity In_Knots is new Float range 0.0 .. 500.00;
type Distance In_Nautical_Miles is new Float range 0.0 .. 3000.00;
Velocity: Velocity_In_Nots;
Distance: Ditance_In_Nautical_Miles;
Some_Number: Float;
Some_Numver:= Distance + Velocity; -- コンパイラが型エラーを検出

さほど要求の厳しくないドメインでも、文字列や浮動小数点数といった言語(あるいはそのライブラリ)の提供するプリミティブ型を使うより、ドメイン固有の型を使う方が好ましいという場合は珍しくありません。最近ではJava、C++、Pythonなど、抽象データ型をクラスとして表現する言語も増えています。たとえば、​Velocity_In_Knots​(速度のデータを扱う型。単位は常に「ノット」) 、​Distance_In_Nautical_Miles​(距障のデータを扱う型。単位は常に「海里」)といった型を、クラスとして表現することができるのです。こうした型を定義すれば、コードの品質を大きく向上させることができるでしょう。それは具体的には次のような利点があるためです。

  • コードが読みやすくなる。ドメインの重要な概念を表す言葉がそのまま型の名前になっているので、Float、Stringといった型名が使われるより、意味がわかりやすい。
  • テストがしやすくなる。機能一つ一つがカプセル化され、他から独立するため、1つ1つのテストがしやすくなる。
  • コードの再利用が容易。同じコードを複数のアプリケーションやシステムに簡単に再利用できる。

この種のアプローチは、静的な型付けの言語でも、動的な型付けの言語でも同様に有効です。両者の違いは、静的な型付けの言語では、コンパイラが型のチェックをしてくれるのに対し、動的な型付けの言語では、ユニットテストで型のチェックをせざるを得ないということくらいでしょう。ただ、型チェックの手段は違っても、ドメイン固有の型を使う目的や、その使い方は基本的に同じです。

マーズ・クライメイト・オーピターの事故を貴重な教訓とし、ドメイン固有の型を積極的に使ってソフトウェアの品質を高めるべきです。