Obj.magic を使う [OCaml]
printf の謎を解くために OCaml の Printf.printf のソースを眺めてみた。長くて追うのを投げ出したけど Obj.magic という見慣れない関数が怪しげに使われているということが分かった。
さて、これは何か。Obj モジュールのマニュアル [1] は "Not for the casual user." という警告(!)と型が書いてあるだけだ。magic 関数の型は val magic : 'a -> 'b だが、どうやらこれはある種の identity function で、にもかかわらず引数の型と不一致な文脈においてもコンパイラに怒られないという特徴を持つようだ。
これを使うと printf のサブセットみたいなものが以下のように書ける。
# exception Invalid_format;; exception Invalid_format # let myprintf fmt v = match fmt with | "%s" -> print_string (Obj.magic v) | "%d" -> print_int (Obj.magic v) | _ -> raise Invalid_format ;; val myprintf : string -> 'a -> unit = <fun> # myprintf "%s" "hello!";; hello!- : unit = () # myprintf "%d" 123;; 123- : unit = ()本来これと同じようなことを Obj.magic 無しでやろうとすると型チェックに引っかかる。引数 v の型が一貫していないからだ。
# let myprintf fmt v = match fmt with | "%s" -> print_string v | "%d" -> print_int v | _ -> raise Invalid_format ;; Characters 81-82: | "%d" -> print_int v ^ This expression has type string but is here used with type intところで上記の myprintf 関数は C 風である。 C 風であるというのは (1) コンパイル時に v の型チェックがなされず、 (2) 書式 fmt と v の型が不一致の場合でも実行時例外などで補足されない、ということを意味する。ためしてみよう。
# myprintf "%s" 123;; F:\>Windows の「問題が発生したため、ocamlrun.exe を終了します。 ご不便をおかけして申し訳ありません。」というダイアログが出て OCaml が落ちてしまった。 Printf.printf はこのようなことにはならず、型チェックがちゃんとなされる。
# Printf.printf "%s" 123;; Characters 19-22: Printf.printf "%s" 123;; ^^^ This expression has type int but is here used with type stringなのでもっと賢いことをしているのだ。続く。
[1] http://caml.inria.fr/pub/docs/manual-ocaml/libref/Obj.html
コメント 0