packages feed

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"]