From 8c8539415b1a31f7bba6da4f0795a1fe4e92360f Mon Sep 17 00:00:00 2001
From: Sean Gillespie <sean@mistersg.net>
Date: Fri, 6 Jan 2017 21:44:08 -0500
Subject: [PATCH] Add a small typechecker

---
 lambda-calculator.cabal                |  8 +++--
 src/Language/SystemF/Expression.hs     |  1 -
 src/Language/SystemF/TypeCheck.hs      | 41 ++++++++++++++++++++++++++
 test/Language/SystemF/TypeCheckSpec.hs | 22 ++++++++++++++
 4 files changed, 69 insertions(+), 3 deletions(-)
 create mode 100644 src/Language/SystemF/TypeCheck.hs
 create mode 100644 test/Language/SystemF/TypeCheckSpec.hs

diff --git a/lambda-calculator.cabal b/lambda-calculator.cabal
index d0071b0..9fd17c5 100644
--- a/lambda-calculator.cabal
+++ b/lambda-calculator.cabal
@@ -24,8 +24,10 @@ library
 
                        Language.SystemF,
                        Language.SystemF.Expression,
-                       Language.SystemF.Parser
+                       Language.SystemF.Parser,
+                       Language.SystemF.TypeCheck
   build-depends:       base <= 5,
+                       containers,
                        parsec
   default-language:    Haskell2010
 
@@ -58,9 +60,11 @@ test-suite lambda-calculus-test
 
                        Language.SystemFSpec,
                        Language.SystemF.ExpressionSpec,
-                       Language.SystemF.ParserSpec
+                       Language.SystemF.ParserSpec,
+                       Language.SystemF.TypeCheckSpec
   build-depends:       base <= 5,
                        lambda-calculator,
+                       containers,
                        hspec,
                        HUnit
   ghc-options:         -threaded -rtsopts -with-rtsopts=-N
diff --git a/src/Language/SystemF/Expression.hs b/src/Language/SystemF/Expression.hs
index ece7e15..0bfa167 100644
--- a/src/Language/SystemF/Expression.hs
+++ b/src/Language/SystemF/Expression.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE FlexibleInstances #-}
 module Language.SystemF.Expression where
 
 import Data.Monoid
diff --git a/src/Language/SystemF/TypeCheck.hs b/src/Language/SystemF/TypeCheck.hs
new file mode 100644
index 0000000..76616f9
--- /dev/null
+++ b/src/Language/SystemF/TypeCheck.hs
@@ -0,0 +1,41 @@
+module Language.SystemF.TypeCheck where
+
+import Data.Map
+import Prelude hiding (lookup)
+
+import Language.SystemF.Expression
+
+type UniqueSupply n = [n]
+type Context n t = Map n t
+
+typecheck :: (Ord n, Eq t)
+          => UniqueSupply t 
+          -> Context n (Ty t)
+          -> SystemFExpr n t 
+          -> Either String (Ty t)
+typecheck uniqs ctx (Var v)        = tcVar uniqs ctx v
+typecheck uniqs ctx (Abs n t body) = tcAbs uniqs ctx n t body
+typecheck _     _   _       = undefined
+
+tcVar :: (Ord n, Eq t)
+      => UniqueSupply t
+      -> Context n (Ty t)
+      -> n
+      -> Either String (Ty t)
+tcVar uniqs ctx var = maybe (TyVar <$> unique uniqs) return (lookup var ctx)
+
+tcAbs :: (Ord n, Eq t)
+      => UniqueSupply t
+      -> Context n (Ty t)
+      -> n
+      -> Ty t
+      -> SystemFExpr n t
+      -> Either String (Ty t)
+tcAbs uniqs ctx name ty body = TyArrow ty <$> typecheck uniqs ctx' body
+  where ctx' = insert name ty ctx
+
+-- Utilities
+unique :: UniqueSupply t
+       -> Either String t
+unique (u:_) = return u
+unique _     = fail "Unique supply ran out"
diff --git a/test/Language/SystemF/TypeCheckSpec.hs b/test/Language/SystemF/TypeCheckSpec.hs
new file mode 100644
index 0000000..2776892
--- /dev/null
+++ b/test/Language/SystemF/TypeCheckSpec.hs
@@ -0,0 +1,22 @@
+module Language.SystemF.TypeCheckSpec (spec) where
+
+import Data.Map
+
+import Test.Hspec
+
+import Language.SystemF.Expression
+import Language.SystemF.TypeCheck
+
+tc uniqs ctx = typecheck uniqs (fromList ctx)
+
+spec :: Spec
+spec = describe "typecheck" $ do
+  it "typechecks simple variables in context" $
+    tc [] [("x", TyVar "X")] (Var "x") `shouldBe` Right (TyVar "X")
+
+  it "typechecks simple variables not in context" $ 
+    tc ["A"] [] (Var "x") `shouldBe` Right (TyVar "A")
+
+  it "typechecks simple abstractions" $
+    tc [] [] (Abs "x" (TyVar "A") (Var "x")) 
+      `shouldBe` Right (TyArrow (TyVar "A") (TyVar "A"))
-- 
GitLab