void型とunit型に関して、だいぶ前に話題にしたことがあります。
void型は、直感的には「値がない」ことを示します。unit型(シングルトン型ともいいます)は、常に特定・単一の値を返すことを示します。
多くの場合、void型はunit型だと解釈できますが、それとは対極的な解釈として「void型はany型だ」というのもあります。戻り値がvoid型だと宣言された関数は、どんな値を返すかわからない。どんな値でも返す可能性がある。だから、そんな当てにならない値を使ってはいけない、捨てろ! という解釈ですね。
いずれにしても、voidには「実は値がある」という解釈です。でも、ほんとに値がないケースが2種類あります。1つ目は常に例外を起こして正常な戻り値を返さない関数*1です。
void error_exit(string message);
絶対に値を返さないことを明示したいなら、voidよりneverとでも書いた方がいいでしょう。(下の宣言の構文はテケトー。)
never error_exit(string message) throws SomeRuntimeException;
絶対に値を返さない別なケースは一方向のリモート呼び出しです。
oneway void notifyWeather(weather w);
oneway void という書き方は実際にIDL(Interface Definition/Description Language)で使われます。リモート呼び出しのときに戻り値があるなら、呼び出し要求を発行した後で戻り値の到着を待ちます。しかし、oneway void が指定されると送りっぱなしにします。呼び出しというよりは単なる送出で、「関数/手続き呼び出し」よりは「イベント配送」のセマンティクスになります。
さて、void型戻り値の解釈として、unit型、any型、oneway void型がありますが、これらの違いを理解するには状態遷移で考えた方がいいでしょう。関数呼び出しの戻り値を入れるレジスタがあって、そのレジスタ値が呼び出し後にどう変化するかを考えてみます。
- いつでも特定の値にセットされるなら、unit型
- どんな値に書き換えられるかまったく予測できないなら、any型
- 直前の値のまま(そもそも書き換えが起きない)なら、oneway void型
*1:「そんなのは関数と呼ばない」って意見もあるでしょうが、割とみんな関数と呼んでるようですから、、、