7-4. 関数に対する型検査

型検査の対象となる式の構文に関数を追加しよう。
type exp =
  ...
  | Fun of string * exp   (* fun x -> e *)
  | App of exp * exp      (* function application i.e. e e *)
これに対応して「式の型」をあらわす型も拡張する必要がある。 関数の型は、(int -> bool) のように矢印を使って表現する。 これを、TArrow(TInt, TBool) と表現することにしよう。 ただ、関数の定義域や値域がさらに関数型であることもある。 たとえば、 (int -> (int -> bool)) や ((int -> bool) -> int) といった ものも型である。(このような「高階関数」が使えることが、関数型言語の 特徴の1つである。) そこで、拡張された ty型は以下のように定義される。
  type ty = TInt | TBool | TArrow of ty * ty
この定義を使えば、 (int -> (int -> bool)) に対応する内部表現は, TArrow(TInt, TArrow(TInt, TBool)) となる.

関数の型の検査

上記の拡張に対する型検査は,以下のように行えばよい. 以上の考え方で,関数に対する型検査器 tcheck3 を実装する.
(* tcheck3 : tyenv -> exp -> ty *)
let rec tcheck3 te e =
  match e with
  ...
  | Fun(x, e1) ->
        let t1 = lookup x te in
        let t2 = tcheck3 te e1 in 
           TArrow(t1,t2)
  | App(e1,e2) ->
        let t1 = tcheck3 te e1 in
        let t2 = tcheck3 te e2 in 
          begin
           match t1 with
	   | TArrow(t10,t11) -> if t2=t10 then t11
                             else failwith "type error in App"
	   | _ -> failwith "type error in App"
          end
  | _ -> failwith "unknown expression"

課題 7-4.

発展課題 (オプショナル)


補足: tcheck3 では,関数の引数となる変数の型も, 型環境で与えなければいけない.そのため,異なる関数の引数がx という同じ 名前であったとき,それらの型も同じでなければいけない,という(OCamlにはない) 制約がある. たとえば (fun x-> x) (fun x -> x + 1) という式は、OCaml では型がつく が、tcheck3 では型がつかない。なぜなら、2つの束縛変数x は OCaml では 異なるものであるのに、tcheck3 は区別せずに同じ型を持つ変数と見なしている からである。

この問題を克服するには、 型環境をつかうのではなく,関数の仮引数(変数)ごとに型を記述するスタイルにすれば よい.この場合,「式」の構文の中に,「型」を含む表記を許すよう 拡張しなければいけないので,本実験では,この方向は深追いしないが、 もし興味がある人がいたら、拡張してみるとよい。 たとえば、以下のようにするとよい。

type exp = 
    ...
  | Fun of string * ty * exp   (* fun (x:t) -> e *)
  | ...
この場合、Fun の構文を変更するので、今まで実装した Fun を扱う部分も全て 変更する必要がある。 なお、後に述べる型推論の考え方を使えば、この問題点も自然に解消する.
トップ, 前へ. 次へ.

亀山 幸義