summaryrefslogtreecommitdiff
path: root/lambda-calcul/haskell/test/Minilang/Lambda
diff options
context:
space:
mode:
Diffstat (limited to 'lambda-calcul/haskell/test/Minilang/Lambda')
-rw-r--r--lambda-calcul/haskell/test/Minilang/Lambda/EvalSpec.hs40
-rw-r--r--lambda-calcul/haskell/test/Minilang/Lambda/InferSpec.hs47
2 files changed, 87 insertions, 0 deletions
diff --git a/lambda-calcul/haskell/test/Minilang/Lambda/EvalSpec.hs b/lambda-calcul/haskell/test/Minilang/Lambda/EvalSpec.hs
new file mode 100644
index 0000000..df933a4
--- /dev/null
+++ b/lambda-calcul/haskell/test/Minilang/Lambda/EvalSpec.hs
@@ -0,0 +1,40 @@
+module Minilang.Lambda.EvalSpec (spec) where
+
+import Control.Concurrent (threadDelay)
+import Control.Concurrent.Async (race_)
+import Minilang.Lambda.Eval (Term (..), eval, evalNeed)
+import Test.Hspec (Spec, describe, it, shouldBe)
+
+spec :: Spec
+spec = do
+ let fix = Lam "x" (App (Var "x") (Var "x"))
+ let omega = App fix fix
+ let term = App (App (Lam "x" (Lam "y" (Var "y"))) omega) (Var "z")
+
+ describe "call-by-value eval" $ do
+ it "evaluates 'x' as itself" $ do
+ let term = Var "x"
+ eval term [] `shouldBe` Var "x"
+ it "evaluates '((λ x. x) y)' as 'y'" $ do
+ let term = App (Lam "x" (Var "x")) (Var "y")
+ eval term [] `shouldBe` Var "y"
+ it "evaluates '((λ x y. y) y z t)' as '(z t)'" $ do
+ let snd' = Lam "x" (Lam "y" (Var "y"))
+ let term = App (App (App snd' (Var "y")) (Var "z")) (Var "t")
+ eval term [] `shouldBe` App (Var "z") (Var "t")
+ it "diverges on evaluating argument bound to ω" $
+ shouldDiverge $ eval term []
+
+ describe "call-by-need eval" $ do
+ it "evaluates 'x' as itself" $ do
+ evalNeed (Var "x") [] `shouldBe` Var "x"
+ it "evaluates '(λ x y . y) ⊥ z' as 'z'" $ do
+ evalNeed term [] `shouldBe` Var "z"
+ it "diverges on evaluating ω" $
+ shouldDiverge $ evalNeed omega []
+
+shouldDiverge :: Term -> IO ()
+shouldDiverge t =
+ race_
+ (t `seq` fail "Did not diverge")
+ (threadDelay 1000000)
diff --git a/lambda-calcul/haskell/test/Minilang/Lambda/InferSpec.hs b/lambda-calcul/haskell/test/Minilang/Lambda/InferSpec.hs
new file mode 100644
index 0000000..dac76ec
--- /dev/null
+++ b/lambda-calcul/haskell/test/Minilang/Lambda/InferSpec.hs
@@ -0,0 +1,47 @@
+module Minilang.Lambda.InferSpec (spec) where
+
+import Minilang.Lambda.Eval (Term (..))
+import Minilang.Lambda.Infer (TypeError (..), annotate)
+import qualified Minilang.Lambda.Infer as Ann
+import Minilang.Lambda.Unify (Type (..), UnifyError (..))
+import Test.Hspec (Spec, describe, it, shouldBe)
+
+spec :: Spec
+spec = do
+ describe "annotate" $ do
+ it "annotates unknown variable with fresh type variable" $ do
+ let term = Var "x"
+ annotate term `shouldBe` Right (Ann.Var "x" (TyVar "a"))
+ it "annotates bound λ variable with fresh type variable" $ do
+ let term = Lam "x" (Var "x")
+ annotate term `shouldBe` Right (Ann.Lam "x" (Ann.Var "x" (TyVar "a")) (TyVar "a" :-> TyVar "a"))
+ it "annotates application with fresh type variables" $ do
+ let term = App (Var "x") (Var "y")
+ annotate term `shouldBe` Right (Ann.App (Ann.Var "x" (TyVar "a")) (Ann.Var "y" (TyVar "b")) (TyVar "c"))
+ it "reuse free variables types" $ do
+ let term = App (Var "x") (Var "x")
+ annotate term `shouldBe` Right (Ann.App (Ann.Var "x" (TyVar "a")) (Ann.Var "x" (TyVar "a")) (TyVar "b"))
+ describe "infer" $ do
+ it "infers correctly some expressions" $ do
+ let id' = Lam "x" (Var "x")
+ let fst' = Lam "x" (Lam "y" (Var "x"))
+ let snd' = Lam "x" (Lam "y" (Var "y"))
+ let comp = Lam "f" (Lam "g" (Lam "x" (App (Var "f") (App (Var "g") (Var "x")))))
+ let k = Lam "x" (Lam "y" (Lam "z" (App (App (Var "x") (Var "z")) (App (Var "y") (Var "z")))))
+
+ Ann.infer id' `shouldBe` Right (TyVar "a" :-> TyVar "a")
+ Ann.infer fst' `shouldBe` Right (TyVar "a" :-> (TyVar "b" :-> TyVar "a"))
+ Ann.infer snd' `shouldBe` Right (TyVar "a" :-> (TyVar "b" :-> TyVar "b"))
+ Ann.infer comp `shouldBe` Right ((TyVar "d" :-> TyVar "e") :-> ((TyVar "c" :-> TyVar "d") :-> (TyVar "c" :-> TyVar "e")))
+ Ann.infer k
+ `shouldBe` Right
+ ( (TyVar "c" :-> TyVar "e" :-> TyVar "f")
+ :-> (TyVar "c" :-> TyVar "e")
+ :-> TyVar "c"
+ :-> TyVar "f"
+ )
+
+ it "rejects circular references" $ do
+ let circ = Lam "f" (App (Lam "x" (App (App (Var "f") (Var "x")) (Var "x"))) (Lam "y" (App (App (Var "f") (Var "y")) (Var "y"))))
+
+ Ann.infer circ `shouldBe` Left (UnifyError $ Circularity "e" (TyVar "e" :-> TyVar "g"))