型システム
名前的(nominal)と構造的(structural)は、静的/動的型付けでのプログラム要素の型を識別(identify)する際の性質である。
名前的型付けとは、システムが保持しているプログラム要素の参照情報上の型の名前を見て、データ値の型を識別するスタイルである。
構造的型付けとは、参照情報上の型の名前を見ないで、データ値本体の分析でデータ値の型を識別するスタイルである。ただし、データ集合を分解して行き着くリテラルの型識別は事実上の名前的型付けになる。例えば構造体では、フィールド構造(各フィールドの型の種類と順序)が一致しているならばフィールド名が違っても同じ型と判定される。構造的型付けは、部分型の判定にも多用される。例えばオブジェクトでは、特定の関数/手続きの構造を双方が所持しているならば、その部分型での等価と見なされる。特定のフィールド構造を双方が所持している場合も同様である。
Benjamin C. Pierce (2005). Advanced Topics in Types and Programming Languages. MIT Press. ISBN 0-262-16228-8. http://www.cis.upenn.edu/~bcpierce/attapl/main.html
Type System Terminology
Meijer, Erik. “Static Typing Where Possible, Dynamic Typing When Needed: The End of the Cold War Between Programming Languages” (PDF). Microsoft Corporation. 2007年2月16日時点のオリジナルよりアーカイブ。2007年3月8日閲覧。
Pierce, Benjamin C. 著、遠藤侑介・ほか共 訳『型システム入門 : プログラミング言語と型の理論』オーム社、2013年3月。ISBN 9784274069116。
静的な型システムの言語では型宣言を必要とし、基本的にプログラマはすべての変数に特定の型を明示的に関連付けなければならない。しかし、変数の初期化時の右辺値や変数の使われ方など、プログラマが型を明示せずともコードの文脈から型を自動的に決定する型推論(type inference)の機能を持つ言語もある。例として、Haskellにおいて変数 x と y を加算する関数 f を定義してみる。
ここで、x と y の型を特に明示していないが、+による加算は数値のみに定義されているので、コンパイラは x と y は共に数値型であると推論できる。ゆえにプログラム中で f の引数として数値でない型(文字列やリストなど)の値を渡して呼び出すとエラーを報告する。
型推論の目的は、単にコードの記述効率を向上したり、型のミスマッチに起因するエラーを軽減したりすることだけではない。ラムダ式や匿名型など、コンパイラが内部で型を自動生成し、プログラマが具体的な型の名前をコード上で書き下すことができない場合もあり、そういった機能に対応しなければならないという目的もあって型推論を導入した言語もある(C++やC#など)。
プログラムにおけるエラーはさまざまだが、型に基づく一連のエラーがある。単純な例としては、浮動小数点数を表現しているワードを(一般的なコンピュータのハードウェアでは、メモリ上のワードとしては区別がつかないため)整数型として扱ってしまう、といったようなものである。この例では 0 と +0.0 のような特別な場合を除いてたいていの場合は得られる結果は無意味であり、より複雑な構造を持った値の場合は構造を壊して不正にしてしまうかもしれない。このような異常をプログラムが起こさないことを検査するのが型検査(英: type checking)である。
前述のように型安全性が具体的にどのようなものであるかはプログラミング言語や文脈に依存する。
一般的にコンピュータでは整数と浮動小数点数のように異なる型の値同士の演算はできず、いったん同じ型に揃えて演算する必要がある。プログラマがコード上で型変換(キャスト)を明示する必要のある厳しい言語もあれば、コンパイラによってある程度の暗黙の型変換や型昇格がなされるようなゆるい言語もある。さらにCにおいては、キャスト(型変換)構文を使うと、互換性のない型同士でも強制変換できてしまう。例えば整数とポインタとの間で相互変換することや、整数へのポインタを浮動小数点数へのポインタに無理やり変換することもできてしまい、簡単に型安全性が破壊される。誤った型へのポインタ経由で領域にアクセスした場合の動作は未定義である。型検査によって型安全性を確保するために、互換性のない型同士の変換は明示的・暗黙的問わず一切できないようになっている言語や、間違った型変換をすると実行時に例外をスローする言語もある。
型安全でない例としては、C/C++において、可変長引数の関数を利用する場面が挙げられる。代表的なものがprintfやscanfで、これらの関数は任意の数のあらゆる型のデータ(オブジェクト)を可変長引数として統一的に渡せるように、いったん引数オブジェクトの型情報を消去し、別途入力として与えられた書式文字列をもとに型情報を関数内部で復元する仕組みになっている。