SMLでtype erasureできた

型に厳しいことで有名な言語であるStandard ML
C++でよく使われる,type erasure(型消去)パターンを実現する方法を紹介します.

(* 斉藤さんのコメントによる指摘を受けて追記 *)
fun op $ (f,x) = f x; infixr 1 $;

(* コード中の行頭に - の付いた行はインタプリタの出力です *)

準備

まず下準備から…(というかコレが一番のキモなんですけど)

signature ANY =
sig
  type dyn
  val newdyn : unit -> ('a->dyn) * (dyn->'a)
end

まずANYインターフェースを定義します.

で, 次にその実装.

structure Any :> ANY =
struct
  exception Dynamic
  type dyn = exn    (* exn => 例外 *)
  fun newdyn () =
    let exception E of 'a (* 型構築子Eを定義 *)
    in (E, fn (E a)=>a | _=> raise Dynamic)
    end
end

えー. こんなんアリなんですかね?(汗
呼び出すたびに多相な例外型のコンストラクタと値の取得関数を返します.
SML黒魔術ですね.


次に型注釈を書いて**runtime用の型を表す値**を定義.

type 'a Any = ('a->Any.dyn)*(Any.dyn->'a)
(* コメントの指摘を受けてtypo修正 *)
val int_type    = newdyn () : int Any
val string_type = newdyn () : string Any
val is_type     = newdyn () : (int*string) Any

使ってみる

先に定義した3つの型に対応したジェネリックtoStringを定義.

fun IStoString (x,y) = String.concat ["(",Int.toString x,",",y,")"]

fun gToString v =    Int.toString $ snd    int_type v
    handle Dynamic =>               snd string_type v (* !? *)
    handle Dynamic => IStoString $ snd     is_type v
- val it = fn : Any.dyn -> string

………汗


実際に適用すると….

val xs = [fst    int_type 314
         ,fst string_type "foobar"
         ,fst     is_type (98, "hoge")]
- val xs = [-,-,-] : Any.dyn list (* 怪しげなリスト *)
val ss = List.map gToString xs
- val ss = ["314","foobar","(98,hoge)"] : string list

やりました\(^o^)/