lambda-calculator-3.1.0.0: test/Language/Lambda/SystemF/EvalSpec.hs
module Language.Lambda.SystemF.EvalSpec (spec) where
import RIO
import RIO.Map (fromList)
import Test.Hspec
import Language.Lambda.Shared.Errors
import Language.Lambda.Shared.UniqueSupply (defaultUniques, defaultTyUniques)
import Language.Lambda.SystemF.Expression
import Language.Lambda.SystemF.Eval
import Language.Lambda.SystemF.HspecUtils
import Language.Lambda.SystemF.State
spec :: Spec
spec = do
let evalExpr' expr = execTypecheck (evalExpr expr) $
mkTypecheckState defaultUniques defaultTyUniques
describe "evalExpr" $ do
it "Does not reduce normal form" $ do
"x" `shouldEvalTo` "x"
it "beta reduces" $ do
"(\\x:T. x) y:T" `shouldEvalTo` "y:T"
"(\\f:(T->T) x:T. f x) (g:T->T) (y:T)" `shouldEvalTo` "g:(T->T) y:T"
"\\x:T. (\\y:T. y) x" `shouldEvalTo` "\\x:T. x"
"(\\f:(T->T) x:T. f x) (\\f:T. x:T)" `shouldEvalTo` "\\z:T. x:T"
it "reduces let bodies" $ do
"let x = (\\y:Y. y) z:Y" `shouldEvalTo` "let x = z:Y"
it "nested let expressions fail" $ do
eval "let x = let y = z" `shouldFailWith` isLambdaException
it "reduces type abstractions to A normal form" $ do
"\\T. (\\y:T. y) x:T" `shouldEvalTo` "\\T. x:T"
it "reduces type applications" $ do
"(\\T. x:T) [X]" `shouldEvalTo` "x:X"
"(\\x:(forall T. T). x) [X]" `shouldEvalTo` "\\x:X. x"
"x:(forall T. T) [X]" `shouldEvalTo` "x:X"
"(\\x:(forall T. T). x) (\\X. y:X)" `shouldEvalTo` "\\X. y:X"
describe "subGlobals" $ do
let subGlobals' :: SystemFExpr Text -> SystemFExpr Text
subGlobals' expr = unsafeExecTypecheck (subGlobals expr) state
state = TypecheckState globals' defaultUniques defaultTyUniques
globals' = fromList [("w", TypedExpr (Var "x") (TyVar "X"))]
it "subs simple variables" $ do
subGlobals' (Var "w") `shouldBe` Var "x"
subGlobals' (VarAnn "w" (TyVar "X")) `shouldBe` Var "x"
it "does not sub shadowed bindings" $ do
let expr = Abs "w" (TyVar "W") $ Var "w"
subGlobals' expr `shouldBe` expr
xit "does not capture globals" $ do
let expr = Abs "x" (TyVar "X") $ Var "w"
subGlobals' expr `shouldBe` Abs "a" (TyVar "X") (Var "x")
describe "betaReduce" $ do
let betaReduce' :: SystemFExpr Text -> SystemFExpr Text -> SystemFExpr Text
betaReduce' e1 e2 = unsafeExecTypecheck (betaReduce e1 e2) $
mkTypecheckState defaultUniques defaultTyUniques
it "reduces simple applications" $ do
let e1 = Abs "x" (TyVar "T") (Var "x")
e2 = Var "y"
betaReduce' e1 e2 `shouldBe` e2
it "reduces nested abstractions" $ do
let e1 = Abs "x" (TyVar "T") (Abs "y" (TyVar "U") (Var "x"))
e2 = Var "z"
betaReduce' e1 e2 `shouldBe` Abs "y" (TyVar "U") (Var "z")
it "reduces inner applications" $ do
let e1 = Abs "f" (TyArrow (TyVar "T") (TyVar "T")) $
App (Var "f") (VarAnn "x" (TyVar "T"))
e2 = Var "g"
betaReduce' e1 e2 `shouldBe` App (Var "g") (VarAnn "x" (TyVar "T"))
it "does not reduce unreducible expressions" $ do
let e2 = Var "y"
betaReduce' (Var "x") e2
`shouldBe` App (Var "x") (Var "y")
betaReduce' (VarAnn "x" (TyVar "T")) e2
`shouldBe` App (VarAnn "x" (TyVar "T")) (Var "y")
betaReduce' (TyAbs "X" (Var "x")) e2
`shouldBe` App (TyAbs "X" (Var "x")) e2
betaReduce' (TyApp (Var "x") (TyVar "X")) e2
`shouldBe` App (TyApp (Var "x") (TyVar "X")) e2
it "does not reduce irreducible chained applications" $ do
let e1 = App (Var "x") (Var "y")
e2 = Var "z"
betaReduce' e1 e2 `shouldBe` App (App (Var "x") (Var "y")) (Var "z")
it "does not sub shadowed bindings" $ do
let e1 = Abs "x" (TyVar "T") (Abs "x" (TyVar "U") (Var "x"))
e2 = Var "z"
betaReduce' e1 e2 `shouldBe` Abs "x" (TyVar "U") (Var "x")
it "fails to reduce Let" $ do
let e1 = Let "x" (Var "x")
e2 = Var "z"
evaluate (betaReduce' e1 e2) `shouldThrow` isImpossibleError
it "avoids capture" $ do
let beta :: SystemFExpr Text -> SystemFExpr Text -> SystemFExpr Text
beta e1 e2 = unsafeExecTypecheck (betaReduce e1 e2) $
mkTypecheckState ["z"] defaultTyUniques
let e1 = Abs "f" (TyArrow (TyVar "T") (TyVar "U")) $
Abs "x" (TyVar "U") $
App (Var "f") (Var "x")
e2 = Abs "f" (TyVar "T") $ Var "x"
beta e1 e2 `shouldBe` Abs "z" (TyVar "U") (Var "x")
describe "evalTyApp" $ do
it "reduces simple type applications" $ do
let expr = TyApp
(TyAbs "T" (VarAnn "x" (TyVar "T")))
(TyVar "X")
evalExpr' expr `shouldBeRight` VarAnn "x" (TyVar "X")
it "reduces type applications with abstractions" $ do
let expr = TyApp
(TyAbs "T" (Abs "x" (TyVar "T") (Var "x")))
(TyVar "X")
evalExpr' expr `shouldBeRight` Abs "x" (TyVar "X") (Var "x")
it "does not reduce irreducible expressions" $ do
let tyApp inner = TyApp (TyAbs "T" inner) (TyVar "X")
evalExpr' (tyApp (Var "x")) `shouldBeRight` Var "x"
evalExpr' (tyApp (VarAnn "x" (TyVar "Z"))) `shouldBeRight` VarAnn "x" (TyVar "Z")
evalExpr' (tyApp (Abs "x" (TyVar "Z") (Var "x")))
`shouldBeRight` Abs "x" (TyVar "Z") (Var "x")
it "fails on let" $ do
let expr = TyApp (Let "x" (VarAnn "y" (TyVar "T"))) (TyVar "X")
evalExpr' expr `shouldSatisfy` either isLetError (const False)
it "reduces nested expressions" $ do
let tyApp inner = TyApp (TyAbs "T" inner) (TyVar "X")
let e1 = App (Var "f") (VarAnn "x" (TyVar "T"))
evalExpr' (tyApp e1) `shouldBeRight` App (Var "f") (VarAnn "x" $ TyVar "X")
let e2 = Abs "x" (TyVar "U") (VarAnn "t" $ TyVar "T")
evalExpr' (tyApp e2) `shouldBeRight` Abs "x" (TyVar "U") (VarAnn "t" $ TyVar "X")
let e3 = TyAbs "U" $ VarAnn "x" (TyVar "T")
evalExpr' (tyApp e3) `shouldBeRight` TyAbs "U" (VarAnn "x" $ TyVar "X")
let e4 = TyApp (VarAnn "x" (TyVar "T")) (TyVar "U")
evalExpr' (tyApp e4) `shouldBeRight` TyApp (VarAnn "x" $ TyVar "X") (TyVar "U")
let e5 = TyApp
(TyAbs "U" $ VarAnn "x" (TyVar "U"))
(TyVar "T")
evalExpr' (tyApp e5) `shouldBeRight` VarAnn "x" (TyVar "X")
it "reduces in nested types" $ do
let tyApp inner = TyApp (TyAbs "T" inner) (TyVar "X")
let e1 = VarAnn "f" $ TyArrow (TyVar "T") (TyVar "U")
evalExpr' (tyApp e1) `shouldBeRight` VarAnn "f" (TyArrow (TyVar "X") (TyVar "U"))
let e2 = VarAnn "f" $ TyForAll "T" (TyVar "T")
evalExpr' (tyApp e2) `shouldBeRight` e2
let e3 = VarAnn "f" $ TyForAll "U" (TyVar "T")
evalExpr' (tyApp e3) `shouldBeRight` VarAnn "f" (TyForAll "U" (TyVar "X"))
describe "alphaConvert" $ do
let alphaConvert' :: [Text] -> [Text] -> SystemFExpr Text -> SystemFExpr Text
alphaConvert' uniques' fvs expr = unsafeExecTypecheck (alphaConvert fvs expr) $
mkTypecheckState uniques' defaultTyUniques
it "alpha converts simple expressions" $ do
let freeVars = ["x"] :: [Text]
expr = Abs "x" (TyVar "T") (Var "x")
uniques' = ["y"]
alphaConvert' uniques' freeVars expr `shouldBe` Abs "y" (TyVar "T") (Var "y")
it "avoids captures" $ do
let freeVars = ["x"]
expr = Abs "x" (TyVar "T") (Var "x")
uniques' = ["x", "y"]
alphaConvert' uniques' freeVars expr `shouldBe` Abs "y" (TyVar "T") (Var "y")
describe "etaConvert" $ do
it "eta converts simple expressions" $ do
let expr :: SystemFExpr Text
expr = Abs "x" (TyVar "T") $ App (Var "f") (Var "x")
etaConvert expr `shouldBe` Var "f"
it "eta converts nested applications" $ do
let expr1 :: SystemFExpr Text
expr1 = Abs "y" (TyVar "T") $ App (App (Var "f") (Var "x")) (Var "y")
etaConvert expr1 `shouldBe` App (Var "f") (Var "x")
let expr2 :: SystemFExpr Text
expr2 = Abs "x" (TyArrow (TyVar "T") (TyVar "T")) $
Abs "y" (TyVar "T") $
App (App (Var "f") (Var "x")) (Var "y")
etaConvert expr2 `shouldBe` Var "f"
let expr3 :: SystemFExpr Text
expr3 = Abs "x" (TyVar "T") $
Abs "y" (TyArrow (TyVar "T") (TyVar "T")) $
App (Var "y") (Var "x")
etaConvert expr3 `shouldBe` expr3
let expr4 :: SystemFExpr Text
expr4 = Abs "f" (TyVar "T") $
Abs "x" (TyVar "T") (Var "x")
etaConvert expr4 `shouldBe` expr4
it "ignores non-eta convertable expressions" $ do
let expr :: SystemFExpr Text
expr = Abs "x" (TyVar "T") $ Var "x"
etaConvert expr `shouldBe` expr
describe "freeVarsOf" $ do
let freeVarsOf' :: SystemFExpr Text -> [Text]
freeVarsOf' = freeVarsOf
it "Returns simple vars" $ do
freeVarsOf' (Var "x") `shouldBe` ["x"]
freeVarsOf' (VarAnn "x" (TyVar "T")) `shouldBe` ["x"]
it "Does not return bound vars" $
freeVarsOf' (Abs "x" (TyVar "T") (Var "x")) `shouldBe` []
it "Returns nested simple vars" $
freeVarsOf' (Abs "x" (TyVar "T") (Var "y")) `shouldBe` ["y"]
it "Returns applied simple vars" $
freeVarsOf' (App (Var "x") (Var "y")) `shouldBe` ["x", "y"]