letを変える
letは変数に値を束縛し次の式で使えるように出来る素晴らしい式です。
しかし、作ろうとしている言語では、Scalaのvarとvalとblockが必要です。
そこで、構文木の定義にblockとreturn, varを追加します。
blockは式のリストで、型は一番最後の式の型です。ただし、リターン式があればリターン式の型も合わせる必要があります。
リターン式は式の途中に記述出来る式で、リターン式があれば、そこで関数の計算は終了します。
package hm sealed trait SyntaxNode case class Lambda(v: String, body: SyntaxNode) extends SyntaxNode case class Ident(name: String) extends SyntaxNode case class Apply(fn: SyntaxNode, arg: SyntaxNode) extends SyntaxNode //case class Let(v: String, defn: SyntaxNode, body: SyntaxNode) extends SyntaxNode case class Var(v: String, defn: SyntaxNode) extends SyntaxNode case class Block(body:List[SyntaxNode]) extends SyntaxNode case class Return(arg:SyntaxNode) extends SyntaxNode
letでは変数の定義も出来るのでvarを作ります。varは再代入可能な変数を定義できる式です。
def infer(ast:SyntaxNode, env:Map[String, Type], nongen:Set[Var]):Type = { ast match { case Ident(name) => gettype(name, env, nongen) case Apply(fn, arg) => val funtype = infer(fn, env, nongen) val argtype = infer(arg, env, nongen) val resulttype = newVar() unify(Function(argtype, resulttype), funtype) resulttype case Lambda(arg, body) => val argtype = newVar() val resulttype = infer(body, env + (arg -> argtype), nongen + argtype) Function(argtype, resulttype) /* case Let(v, defn, body) => val defntype = infer(defn, env, nongen) val newenv = env + (v -> defntype) infer(body, newenv, nongen)*/ case Var(v, defn) => val defntype = infer(defn, env, nongen) env = env + (v -> defntype) defntype case Block(x::List()) => try { infer(x, env, nongen) } catch { case ReturnTypeException(t) => t } case Block(x::xs) => infer(x, env, nongen) infer(Block(xs), env, nongen) case Return(x) => throw ReturnTypeException(infer(x, env, nongen)) } }
と書いてみましたが、returnで例外投げちゃ駄目ですね。うーむ。