部分的型付け
多相型推論をする言語を作りたいのですが、その前に部分的型付けを勉強しておきたい所です。TAPLのrcdsubbot*1から型付け部分だけ抜き出して名前をオレオレに変換してScalaで書いてみました。TopはScalaでいうとAny型で、どんな型でもTopの子になります。Botは最小型というどの型の子にでもなり得る、IPS細胞みたいな型です。サンプルコード*2の変数定義以外が動きます。
このプログラムで実現しているサブタイピングは構造的なサブタイピングといいます。C++等のオブジェクト指向の継承は、更に順番も必要になるでしょうが、サブタイピングの規則を書き換える事でいろいろなサブタイピングが出来るようになります。
package rcdsubbot sealed trait T case object TTop extends T // 最大の型 case object TBot extends T // 最小の型 case class TFun(t1: T, t2: T) extends T // 関数の型 case class TRecord(els: List[(String, T)]) extends T // レコードの型 sealed trait E case class EVar(i: String) extends E // 変数 case class EFun(v: String, y: T, t: E) extends E // λ抽象 case class EApp(t1: E, t2: E) extends E // 関数適用 case class ERecord(fields: List[(String, E)]) extends E // レコード case class EProj(t: E, proj: String) extends E // レコードのフィールド参照 object main extends App { // lambda x:Top.x println(typing(EFun("x",TTop,EVar("x")))) //((lambda x: Top -> Top. x) (lambda x: Top. x)): Top -> Top; println(typing( EApp( EFun("x",TFun(TTop,TTop),EVar("x")), EFun("x",TTop,EVar("x"))) )) /* ((lambda r: {x: Top -> Top}. r.x (r.x)) {x=lambda z: Top. z, y=lambda z: Top. z}): Top; */ println(typing( EApp( EFun("r",TRecord(List("x"->TFun(TTop,TTop))), EApp(EProj(EVar("r"), "x"),EProj(EVar("r"), "x"))), ERecord(List( "x"->EFun("z",TTop,EVar("z")), "y"->EFun("z",TTop,EVar("z")) ))) )) //(lambda x: Bot. x): Bot -> Bot; println(typing(EFun("x",TBot,EVar("x")))) //(lambda x: Bot. x x): Bot -> Bot; println(typing(EFun("x",TBot,EApp(EVar("x"),EVar("x"))))) } object typing { def apply(e:E):T = { visit(Map(), e) } def subtype(cT:T, pT:T): Boolean = { cT == pT || // ((cT, pT) match { case (_, TTop) => true // SA-Top case (TBot, _) => true // SA-Bot case (TFun(ct1,ct2), TFun(pt1,pt2)) => // SA-Arrow subtype(ct1, pt1) && subtype(ct2, pt2) case (TRecord(css), TRecord(pts)) => // SA-Rcd pts.forall { case (s,pt) => css.find{case (s1,_) => s1 == s} match { case Some((_,ct)) => subtype(ct, pt) case None => false } } case _ => false }) } def visit(env:Map[String,T], e:E):T = { e match { case EVar(v) => env(v) // TA-Var case EFun(v, t, e) => // TA-Abs val t2 = visit(env+(v->t), e) TFun(t, t2) case EApp(e1, e2) => // TA-App val t1 = visit(env, e1) val t2 = visit(env, e2) t1 match { case TFun(t11, t12) => if (!subtype(t2, t11)) throw new Exception("type error "+t2 + "!="+t12) t12 case TBot => TBot // TA-AppBot case _ => throw new Exception("type error") } case ERecord(fs) => // TA-Rcd val fts = fs.map { case(s, e) => (s, visit(env, e)) } TRecord(fts) case EProj(e1, s) => // TA-Proj visit(env, e1) match { case TRecord(fts) => fts.find{case (s1, _) => s1 == s} match { case Some((_,t)) => t case None => throw new Exception("type error") } case TBot => TBot // TA-ProjBot case _ => throw new Exception("type error") } } } }