Quick actions

cmd+k|ctrl+k

Navigation

Languages

Type checker for the simply typed lambda calculus using cofree

Snippet info

Language

Scala

Visibility

public

Author

rightfold

Created

2016-08-25T14:53:55Z

Updated

2016-08-25T15:10:43Z

case class Cofree[F[_], A](head: A, tail: F[Cofree[F, A]])

sealed abstract class Exp[A]
case class Var[A](name: String) extends Exp[A]
case class Abs[A](param: String, paramT: Type, body: A) extends Exp[A]
case class App[A](func: A, arg: A) extends Exp[A]

sealed abstract class Type
case object BoolT extends Type
case object StringT extends Type
case class FuncT(from: Type, to: Type) extends Type

object TypeCheck {
    type Env = Map[String, Type]

    def typeCheck[A](env: Env)(exp: Cofree[Exp, A]): Option[Cofree[Exp, Type]] =
        exp.tail match {
            case Var(name) =>
                env.get(name).map(t => Cofree(t, Var(name)))
            case Abs(param, paramT, body) =>
                val env2 = env + (param -> paramT)
                typeCheck(env2)(body).map(body2 =>
                    Cofree(FuncT(paramT, body2.head), Abs(param, paramT, body2)))
            case App(func, arg) =>
                for {
                    func2 <- typeCheck(env)(func)
                    arg2 <- typeCheck(env)(arg)
                    r <- func2.head match {
                        case FuncT(from, to) if from == arg2.head =>
                            Some(Cofree(to, App(func2, arg2)))
                        case _ => None
                    }
                } yield r
        }
}

object Main {
    import TypeCheck._

    def mkVar(name: String) = Cofree((), Var[Cofree[Exp, Unit]](name))
    def mkAbs(param: String, paramT: Type, body: Cofree[Exp, Unit]) =
        Cofree((), Abs(param, paramT, body))
    def mkApp(func: Cofree[Exp, Unit], arg: Cofree[Exp, Unit]) =
        Cofree((), App(func, arg))

    def test(env: Env, exp: Cofree[Exp, Unit]) =
        println(exp.toString + " : " + typeCheck(env)(exp).toString)

    def main(args: Array[String]): Unit = {
        test(
            Map(),
            mkVar("x")
        )
        test(
            Map("x" -> BoolT),
            mkVar("x")
        )
        test(
            Map("x" -> BoolT, "not" -> FuncT(BoolT, BoolT)),
            mkApp(mkVar("not"), mkVar("x"))
        )
        test(
            Map("x" -> StringT, "not" -> FuncT(BoolT, BoolT)),
            mkAbs("x", BoolT, mkApp(mkVar("not"), mkVar("x")))
        )
    }
}
INFO