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 が間違っているような気がしますが…。