SML/NJのvalue restrictionが怪しい気がする
SML/NJに怪しげな振る舞いを見つけました。
コードとエラーメッセージから推察するに value restriction(値多相性制約) 絡みだと思います。
それを踏まえて問題のコード。
structure S :> sig val reset : unit -> unit val register_reset : (unit -> unit) -> unit end = struct fun id x = x val reset_fun = ref id fun register_reset f = reset_fun := (f o !reset_fun) fun reset() = !reset_fun () end
これをnjに渡すと…
Standard ML of New Jersey v110.76 [built: Thu Nov 20 22:50:17 2014] [opening test.sml] test.sml:8.7-8.25 Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...) test.sml:10.17-10.30 Error: operator and operand don't agree [tycon mismatch] operator domain: ?.X1 operand: unit in expression: (! reset_fun) () test.sml:2.1-11.4 Error: value type in structure doesn't match signature spec name: register_reset spec: (unit -> unit) -> unit actual: (?.X1 -> ?.X1) -> unit /home/eldesh/lcl/bin/sml: Fatal error -- Uncaught exception Error with 0 raised at ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
S.reset_fun で出ている警告に value restriction という単語が含まれていますね :)
どうやら S.register_reset で、型 unit と処理系が導入した型 ?.X1 がマッチしないと言っているようです。
この ?.X1 とはnjが導入した型変数の表示で、「具体的には分からないけども多相でない型」を表します。
これと同じ型の付く式に具体型が出てくれば、その型に決まります。
処理系毎の振る舞い
このコードは mlton,mlkit,sml#,polyml は受け付けますが、sml/njのみが弾いてしまいます。
処理系 | MLton | MLKit | SML# | Poly/ML | SML/NJ |
---|---|---|---|---|---|
accept? | pass | pass | pass | pass | fail |
よってnjがあやしいです*2
ただ恐らく、作った人たちはこの手の動作は把握してる気がするので報告しても直る気がしない…。
work around
上のコードでは関数 reset_fun が多相である必要は無いので、それをアノテーションしてあげればエラーは回避できます。
(* 修正前 *) val reset_fun = ref id (* 修正後 *) val reset_fun : (unit -> unit) ref = ref id