「Erlangの型記法(Type Notation)」において、関数の型仕様(type spec)記述に関して、僕が誤解していた点があるので訂正します。間違っていたところは、次のような書き方の解釈です。
僕は、イコール記号の使用を慣例的濫用<らんよう>だと思っていました。そうじゃなかったんですよ。
find_char(Str, Ch) -> integer()
Str = string()
Ch = char()
誤解の背景
まず、僕の間違った解釈の背景を説明しましょう。
僕は、「Erlangの型仕様記述は、Cの関数プロトタイプ宣言と同じだろう」と理解しました。
/* C */
int find_char(string str, char ch);% Erlang
find_char(Str::string(), Ch::char()) -> integer()
ねっ、同じでしょう。もっとも、Erlangの型仕様は言語に含まれるものではなくて、単なる規約ですけどね。
さて、昔(K & R)のCでは、関数ヘッダを次のように書きました。
この書き方をErlangに適用すると、
int find_char(str, ch)
string str;
char ch;
なんらかの理由で「::」が「=」にすり替わってしまうと、
find_char(Str, Ch) -> integer()
Str :: string()
Ch :: char()
これが、今使われている記法だな、っと。←間違い!
find_char(Str, Ch) -> integer()
Str = string()
Ch = char()
イコールは型変数への代入
上記の解釈が勘違いであることは、EDocのマニュアル(http://www.erlang.org/doc/pdf/edoc.pdf)の"1.1.13 Type specifications"を見て気が付きました。
find_char(Str, Ch) のStrやChは引数変数ではなかったのです。StrやChは型変数(type variable)であり、型表現の代理として置かれています。つまり、Str = string() と Ch = char() を一種の代入文だと思って、find_char(Str, Ch) は、find_char(string(), char()) と具体化して解釈すればいいのです。実に単純だね。
型変数を使うのは、型表現が複雑でうっとうしくなるときや、説明的な型名を使いたいときです。型表現を書ける場所なら、どこにでも型変数を書いてかまいません。そして、型変数の具体化を、イコール記号を使った代入文もどきで行うわけです。
引数変数と型変数の両方を使うこともできます。
型変数を説明的名前にしておけば、引数変数を付ける必要はあまりないでしょう。が、次の例では引数変数が“引数の役割”を示唆しています。
find_char(S::Str, C::Ch) -> Index
Str = string()
Ch = char()
Index = integer()
find_substr(Sub::Str, Total::Str) -> Index
Str = string()
Index = integer()
意外と整合的な型システム
Erlangの型記法(type notation)は、言語仕様の外にある規約に過ぎません。しかし、型変数や型構成子が自由に使えるとなると、形式的に割ときれいな型システムを作れそうです。面白いからやってみようと思いますが、それは別エントリーにします。