SMLの空レコード型とunit型注釈を付けた時の処理系毎の振る舞い

SML#で使用出来る多相レコードは、SMLのレコード間のサブタイプ関係を推論すると考えると良さそうです。
例えば fn {x:int,...}=> hogehoge という関数は x という int 型のフィールドを持つ任意のレコードを引数に取りますが、これを {x:int} のサブタイプを何でも受け付けると考えます。
すると {x:int, y:string} や {z:'a, x:int, y:'a list} は {x:int} のサブタイプとなります。


ここで以下の関数 e を考えます。

fun e {...} = "unit";

({...} はどんなフィールドも含んでいる必要の無いレコードパターン。)


上の議論から考えれば、レコードに何も(特定のフィールドを)含んで無くてよい型を引数にとるので、e はあらゆるレコード型を受け付けるはずです。つまりあらゆるレコード型のスーパータイプですね。
各実装で評価すると以下のようになります。

型注釈無し

まずは SML# 。

(* SML# 2.0.0 *)
# fun e {...} = "unit";
val e = fn : ['a#{}. 'a -> string]
# e {x=3.14};
val it = "unit" : string
# e (42, "foo");
val it = "unit" : string

妥当な振る舞いに見えます。


次に SML/NJ 。

(* SML/NJ *)
- fun e {...} = "unit";
stdIn:1.2-1.22 Error: unresolved flex record (need to know the names of ALL the fields
 in this context)
  type: {'Z}

おなじみの(?) flex record エラー。何のフィールドにも触れてないけどダメっぽい。さもありなん。


更に MLton 。

(* MLton *)
$ cat e.sml
fun e {...} = "unit"
$ mlton e.sml
Error: e.sml 1.7.
  Unresolved ... in record pattern.
    in: {...}
compilation aborted: parseAndElaborate reported errors

こちらもそれっぽいエラー。


最後に Poly/ML 。

(* Poly/ML *)
$ poly --use e.sml
Poly/ML 5.5.1 Release
Error- in 'e.sml', line 1.
Can't find a fixed record type. Found near {...}
Error trying to use the file: 'e.sml'

これも妥当に見えます。

型注釈あり

ところでSMLのレコードは一般的に {f1=v1, f2=v2, ..., fn=vn} という形で書きます。
空の場合は {} と書き、これは ():unit と同じ値を指します。

- {}=();
val it = true : bool

{...} は任意のレコードを受け取り、{}は空のレコードであり、()と等しい。
では先ほどのレコードパターン {...} に unit 型の注釈を加えるとどうなるでしょうか?


SML# から。

(* SML# 2.0.0 *)
# fun e ({...}:unit) = "unit";
(interactive):2.8-2.17 Error:
  (type inference 048) type and type annotation don't agree
    inferred type: 'R#{}
  type annotation: unit

unit型はレコードではないのでエラーになっているようです。まぁ妥当に見えます。


で、SML/NJ。

(* SML/NJ *)
- fun e ({...}:unit) = "unit";
val e = fn : unit -> string

え? あれ…?


MLton では…

(* MLton *)
$ cat e.sml
fun e ({...}:unit) = "unit"
$ mlton e.sml
$ (* エラー無し *)

こっちも通ります…。


でも Poly/ML だと

$ poly --use s.sml
Poly/ML 5.5.1 Release
Exception- InternalError: mkArgTuple raised while compiling

Error trying to use the file: 's.sml'

通らない。

まとめ

まとめると以下のようになります。
(* 14/10/19 HaMLetでの結果を追記しました。やはりPoly/MLとSML#が怪しいようですね。 *)

型\処理系 SML# SML/NJ MLton Poly/ML HaMLet
{...} pass fail fail fail fail
({...}:unit) fail pass pass fail pass
({...}:{x:int}) pass pass pass pass pass

最後の行のパターンも試してみました。適当なレコード型のアノテーションを付けるとどの処理系でも通ります。(推論されるはずの型の)サブタイプを指定していることになりますね。


注釈無しの場合については、そもそも多相レコードに対応してない処理系では {...} を書く(大抵の)場合は注釈が要るはずですし、どれも正しい動作に見えます。


注釈ありの場合は………どうなんですか(@_@)?
{...} が {} にマッチするのが自然だと考えられるので、どちらかと言うと SML# と Poly/ML が間違っているような気がしますが…。