MokkosuでHaskellの型システムを作る (その4)

# はじめに

Simon Peyton Jones 論文 Typing Haskell in Haskell を参考に Haskell の型システムの実装に挑戦しています。実装には関数型言語の [Mokkosu](http://lambdataro.sakura.ne.jp/mokkosu/) を使っています。

* [Mark P Jones: Typing Haskell in Haskell](http://web.cecs.pdx.edu/~mpj/thih/)
* [MokkosuでHaskellの型システムを作る (その1)](http://qiita.com/lambdataro/items/1e4d795d3dd97a94c8f3)
* [MokkosuでHaskellの型システムを作る (その2)](http://qiita.com/lambdataro/items/c19e0d6d637e0dbb52ca)
* [MokkosuでHaskellの型システムを作る (その3)](http://qiita.com/lambdataro/items/681cabee9c307b0d5e3e)

# 型推論
```
type Literal = LitInt(Int)
             | LitChar(Char)
             | LitRat(Double)
             | LitStr(String)
;

let ti_lit = {
  ~LitChar(_) -> ([], t_char);
  ~LitInt(_) ->
    let v = new_tvar Star in
    ([IsIn(Id "Num", v)], v);
  ~LitStr(_) -> ([], t_string);
  ~LitRat(_) ->
    let v = new_tvar Star in
    ([IsIn(Id "Fractional", v)], v)
};

type Pat = PVar(Id)
         | PWildcard
         | PAs(Id, Pat)
         | PLit(Literal)
         | PNpk(Id, Int)
         | PCon(Assump, [Pat]);

fun ti_pat = {
  ~PVar(i) ->
    let v = new_tvar Star in
    ([], [Assump(i, to_scheme v)], v);
  ~PWildcard ->
    let v = new_tvar Star in
    ([], [], v);
  ~PAs(i, p) ->
    let (ps, a, t) = ti_pat p in
    (ps, Assump(i, to_scheme t) :: a, t);
  ~PLit(l) ->
    let (ps, t) = ti_lit l in
    (ps, [], t);
  ~PNpk(i,k) ->
    let t = new_tvar Star in
    ([IsIn(Id "Integral", t)], [Assump(i, to_scheme t)], t);
  ~PCon(~Assump(i, sc), pats) ->
    let (ps, a, ts) = ti_pats pats in
    let t2 = new_tvar Star in
    let ~Qual(qs,t) = fresh_inst sc in
    do unify t (foldr fn t2 ts) in
    (ps ++ qs, a, t2)
}
and ti_pats pats =
  let psasts = map ti_pat pats in
  let ps = concat (for (qs2, _, _) <- psasts in qs2) in
  let a  = concat (for (_, a2, _)  <- psasts in a2 ) in
  let ts = for (_, _, t) <- psasts in t in
  (ps, a, ts)
;

type Expr = Var(Id)
          | Lit(Literal)
          | Const(Assump)
          | Ap(Expr, Expr)
          | Let(BindGroup, Expr)

and Alt = Alt([Pat], Expr)

and Impl = Impl(Id, [Alt])

and BindGroup = BindGroup([Expl], [[Impl]])

and Ambiguity = Ambiguity(Tyvar, [Pred])

and Expl = Expl(Id, Scheme, [Alt]);

fun diff_pred_list ps1 ps2 =
  match ps1 {
    [] -> [];
    p :: ps ->
      if mem_pred_list p ps2 ->
        diff_pred_list ps ps2
      else
        p :: diff_pred_list ps ps2
  };

fun diff_tyvar_list tvs1 tvs2 =
  match tvs1 {
    [] -> [];
    tv :: tvs ->
      if mem_tyvar_list tv tvs2 ->
        diff_tyvar_list tvs tvs2
      else
        tv :: diff_tyvar_list tvs tvs2
  };

let num_classes = [
  Id "Num", Id "Integral", Id "Floating", Id "Fractional",
  Id "Real", Id "RealFloat", Id "RealFrac"
];

let std_classes = [
  Id "Eq", Id "Ord", Id "Show", Id "Read", Id "Bounded", Id "Enum",
  Id "Ix", Id "Functor", Id "Monad", Id "MonadPlus"
] ++ num_classes;

let apply_assump_list s =
  map (apply_assump s);

let tv_assump_list a =
  nub_tyvar_list <| concat_map tv_assump a;

let foldl1 f lis = foldl f (head lis) (tail lis);

fun ti_expr ce a = {
  ~Var(i) ->
    let sc = find i a in
    let ~Qual(ps,t) = fresh_inst sc in
    (ps, t);
  ~Const(~Assump(i,sc)) ->
    let ~Qual(ps,t) = fresh_inst sc in
    (ps, t);
  ~Lit(l) ->
    ti_lit l;
  ~Ap(e,f) ->
    let (ps, te) = ti_expr ce a e in
    let (qs, tf) = ti_expr ce a f in
    let t = new_tvar Star in
    do unify (tf `fn` t) te in
    (ps ++ qs, t);
  ~Let(bg,e) ->
    let (ps, a2) = ti_bind_group ce a bg in
    let (qs, t) = ti_expr ce (a2 ++ a) e in
    (ps ++ qs, t)
}

and ti_alt ce a ~Alt(pats,e) =
  let (ps,a2,ts) = ti_pats pats in
  let (qs,t) = ti_expr ce (a2 ++ a) e in
  (ps ++ qs, foldl fn t ts)

and ti_alts ce a alts t =
  let psts = map (ti_alt ce a) alts in
  do iter (unify t) (map snd psts) in
  concat (map fst psts)

and split ce fs gs ps =
  let ps2 = reduce ce ps in
  let (ds, rs) = partition (all (flip elem fs) << tv_pred) ps2 in
  let rs2 = defaulted_preds ce (fs ++ gs) rs in
  (ds, diff_pred_list rs rs2)

and ambiguities ce vs ps =
  for v <- tv_pred_list ps `diff_tyvar_list` vs in
    Ambiguity (v, filter (elem v << tv_pred) ps)

and candidates ce ~Ambiguity(v, qs) =
  let is = for ~IsIn(i,t) <- qs in i in
  let ts = for ~IsIn(i,t) <- qs in t in
  for if all (type_equal (TVar v)) ts;
      if any (flip elem num_classes) is;
      if all (flip elem std_classes) is;
      t2 <- defaults ce;
      if all (entail ce []) (for i <- is in IsIn(i,t2))
  in t2

and with_defaults f ce vs ps =
  let vps = ambiguities ce vs ps in
  let tss = map (candidates ce) vps in
  if any is_nil tss -> error "cannot resolve ambiguity"
  else f vps (map head tss)

and defaulted_preds ce =
  with_defaults (\vps ts -> concat_map (\~Ambiguity(_,x) -> x) vps) ce

and with_defaults2 f ce vs ps =
  let vps = ambiguities ce vs ps in
  let tss = map (candidates ce) vps in
  if any is_nil tss -> error "cannot resolve ambiguity"
  else f vps (map head tss)

and default_subst ce tvs ps =
  with_defaults2 (\vps ts -> zip (map (\~Ambiguity(x,_) -> x) vps) ts)
    ce tvs ps |> Subst

and ti_expl ce a ~Expl(i,sc,alts) =
  let ~Qual(qs,t) = fresh_inst sc in
  let ps = ti_alts ce a alts t in
  let s = get_subst () in
  let qs2 = apply_pred_list s qs in
  let t2 = apply_type s t in
  let fs = tv_assump_list (apply_assump_list s a) in
  let gs = tv_type t2 `diff_tyvar_list` fs in
  let sc2 = quantify gs (Qual (qs2, t2)) in
  let ps2 = filter (not << entail ce qs2) (apply_pred_list s ps) in
  let (ds, rs) = split ce fs gs ps2 in
  if not (scheme_equal sc sc2) ->
    error "signature too general"
  else if not (is_nil rs) ->
    error "context too weak"
  else
    ds

and restricted bs =
  let simple ~Impl(i,alts) = any (\~Alt(x,_) -> is_nil x) alts in
  any simple bs

and ti_impls ce a bs =
  let ts = map (\_ -> new_tvar Star) bs in
  let is = map (\~Impl(x,_) -> x) bs in
  let scs = map to_scheme ts in
  let a2 = zip_with (curry Assump) is scs ++ a in
  let altss = map (\~Impl(_,y) -> y) bs in
  let pss = zip_with (ti_alts ce a2) altss ts in
  let s = get_subst () in
  let ps2 = apply_pred_list s (concat pss) in
  let ts2 = apply_type_list s ts in
  let fs = tv_assump_list (apply_assump_list s a) in
  let vss = map tv_type ts2 in
  let gs = foldl1 union_tyvar_list vss `diff_tyvar_list` fs in
  let (ds,rs) = split ce fs (foldl1 intersect_tyvar_list vss) ps2 in
  if restricted bs ->
    let gs2 = gs `diff_tyvar_list` tv_pred_list rs in
    let scs2 = map (\x -> quantify gs2 (Qual([], x))) ts2 in
    (ds ++ rs, zip_with (curry Assump) is scs2)
  else
    let scs2 = map (\x -> quantify gs (Qual (rs, x))) ts2 in
    (ds, zip_with (curry Assump) is scs2)

and ti_bind_group ce a ~BindGroup(es,iss) =
  let a2 = for ~Expl(v,sc,alts) <- es in Assump(v,sc) in
  let (ps, a3) = ti_seq ti_impls ce (a2 ++ a) iss in
  let qss = map (ti_expl ce (a3 ++ a2 ++ a)) es in
  (ps ++ concat qss, a3 ++ a2)

and ti_seq ti ce a = {
  [] -> ([], []);
  bs :: bss ->
    let (ps, a2) = ti ce a bs in
    let (qs, a3) = ti_seq ti ce (a2 ++ a) bss in
    (ps ++ qs, a3 ++ a2)
}

and ti_seq2 ti ce a = {
  [] -> ([], []);
  bs :: bss ->
    let (ps, a2) = ti ce a bs in
    let (qs, a3) = ti_seq2 ti ce (a2 ++ a) bss in
    (ps ++ qs, a3 ++ a2)
};


type Program = Program([BindGroup]);

let ti_program ce (a : [Assump]) ~Program(bgs) =
  do init_state () in
  let (ps, a2) = ti_seq2 ti_bind_group ce a bgs in
  let s = get_subst () in
  let rs = reduce ce (apply_pred_list s ps) in
  let s2 = default_subst ce [] rs in
  apply_assump_list (s2 `append_subst` s) a2;
```

# ここまでの感想
* プログラムは作り終えた。型も通った。意味は分かっていない。
* 次回は何かサンプルプログラム的なものを動かしてみたいと思う。
* 論文には実行例は載っていないので自力で作る必要がある。

つづく