add (custom) de bruijn-y conversion and alpha equivalence check
This commit is contained in:
parent
99a18df155
commit
dde7cfc7a4
5 changed files with 164 additions and 3 deletions
|
|
@ -1,7 +1,7 @@
|
|||
module EvaluationSpec (spec) where
|
||||
|
||||
import Data.Set (Set, empty, fromList)
|
||||
import Evaluation (beta, bind, freeIn, freeVars, freshName, subst)
|
||||
import Evaluation (alphaEquiv, beta, bind, freeIn, freeVars, freshName, subst)
|
||||
import Parser (Expr (Abstraction, Application, Variable), parse)
|
||||
import Test.Hspec
|
||||
|
||||
|
|
@ -106,6 +106,98 @@ spec = do
|
|||
it "bind with abstraction argument substitutes correctly" $
|
||||
-- (λx. x) applied to (λi. i) → (λi. i)
|
||||
bind absI absI `shouldBe` Just absI
|
||||
describe "alphaEquiv" $ do
|
||||
|
||||
-- Reflexivity
|
||||
it "a variable is alpha-equivalent to itself" $
|
||||
alphaEquiv (Variable "x") (Variable "x") `shouldBe` True
|
||||
it "an abstraction is alpha-equivalent to itself" $
|
||||
alphaEquiv (Abstraction "x" (Variable "x")) (Abstraction "x" (Variable "x")) `shouldBe` True
|
||||
it "a nested term is alpha-equivalent to itself" $
|
||||
alphaEquiv (Abstraction "x" (Abstraction "y" (Application (Variable "x") (Variable "y"))))
|
||||
(Abstraction "x" (Abstraction "y" (Application (Variable "x") (Variable "y"))))
|
||||
`shouldBe` True
|
||||
|
||||
-- Renaming bound variables (True)
|
||||
it "identity with different binder names: λx. x ≡ λy. y" $
|
||||
alphaEquiv (Abstraction "x" (Variable "x"))
|
||||
(Abstraction "y" (Variable "y"))
|
||||
`shouldBe` True
|
||||
it "constant body with different binder names: λx. z ≡ λy. z" $
|
||||
alphaEquiv (Abstraction "x" (Variable "z"))
|
||||
(Abstraction "y" (Variable "z"))
|
||||
`shouldBe` True
|
||||
it "nested: λx. λy. x ≡ λa. λb. a (outer reference)" $
|
||||
alphaEquiv (Abstraction "x" (Abstraction "y" (Variable "x")))
|
||||
(Abstraction "a" (Abstraction "b" (Variable "a")))
|
||||
`shouldBe` True
|
||||
it "nested: λx. λy. y ≡ λa. λb. b (inner reference)" $
|
||||
alphaEquiv (Abstraction "x" (Abstraction "y" (Variable "y")))
|
||||
(Abstraction "a" (Abstraction "b" (Variable "b")))
|
||||
`shouldBe` True
|
||||
it "nested application body: λx. λy. x y ≡ λa. λb. a b" $
|
||||
alphaEquiv (Abstraction "x" (Abstraction "y" (Application (Variable "x") (Variable "y"))))
|
||||
(Abstraction "a" (Abstraction "b" (Application (Variable "a") (Variable "b"))))
|
||||
`shouldBe` True
|
||||
it "self-application body: λx. x x ≡ λy. y y" $
|
||||
alphaEquiv (Abstraction "x" (Application (Variable "x") (Variable "x")))
|
||||
(Abstraction "y" (Application (Variable "y") (Variable "y")))
|
||||
`shouldBe` True
|
||||
|
||||
-- Symmetry
|
||||
it "alpha equivalence is symmetric" $
|
||||
alphaEquiv (Abstraction "x" (Variable "x"))
|
||||
(Abstraction "y" (Variable "y"))
|
||||
`shouldBe`
|
||||
alphaEquiv (Abstraction "y" (Variable "y"))
|
||||
(Abstraction "x" (Variable "x"))
|
||||
|
||||
-- Free variables are name-sensitive (False)
|
||||
it "distinct free variables are not equivalent" $
|
||||
alphaEquiv (Variable "x") (Variable "y") `shouldBe` False
|
||||
it "different free variables in abstraction body: λx. y ≢ λx. z" $
|
||||
alphaEquiv (Abstraction "x" (Variable "y"))
|
||||
(Abstraction "x" (Variable "z"))
|
||||
`shouldBe` False
|
||||
it "bound body vs free body: λx. x ≢ λx. y" $
|
||||
alphaEquiv (Abstraction "x" (Variable "x"))
|
||||
(Abstraction "x" (Variable "y"))
|
||||
`shouldBe` False
|
||||
|
||||
-- Structurally different terms (False)
|
||||
it "abstraction vs bare variable are not equivalent" $
|
||||
alphaEquiv (Variable "x")
|
||||
(Abstraction "x" (Variable "x"))
|
||||
`shouldBe` False
|
||||
it "different depths: λx. x ≢ λx. λy. y" $
|
||||
alphaEquiv (Abstraction "x" (Variable "x"))
|
||||
(Abstraction "x" (Abstraction "y" (Variable "y")))
|
||||
`shouldBe` False
|
||||
it "argument order matters in application: x y ≢ y x" $
|
||||
alphaEquiv (Application (Variable "x") (Variable "y"))
|
||||
(Application (Variable "y") (Variable "x"))
|
||||
`shouldBe` False
|
||||
|
||||
-- Shadowing / name reuse
|
||||
it "consistent inner shadowing: λx. λx. x ≡ λy. λy. y" $
|
||||
alphaEquiv (Abstraction "x" (Abstraction "x" (Variable "x")))
|
||||
(Abstraction "y" (Abstraction "y" (Variable "y")))
|
||||
`shouldBe` True
|
||||
it "inner shadow differs from outer reference: λx. λx. x ≢ λx. λy. x" $
|
||||
alphaEquiv (Abstraction "x" (Abstraction "x" (Variable "x")))
|
||||
(Abstraction "x" (Abstraction "y" (Variable "x")))
|
||||
`shouldBe` False
|
||||
|
||||
-- Free variables unchanged across binder rename
|
||||
it "free variable in body is preserved: λx. y ≡ λz. y" $
|
||||
alphaEquiv (Abstraction "x" (Variable "y"))
|
||||
(Abstraction "z" (Variable "y"))
|
||||
`shouldBe` True
|
||||
it "free vs bound distinction preserved: λy. y ≢ λy. z" $
|
||||
alphaEquiv (Abstraction "y" (Variable "y"))
|
||||
(Abstraction "y" (Variable "z"))
|
||||
`shouldBe` False
|
||||
|
||||
describe "beta reduction" $ do
|
||||
-- Normal forms (no redex)
|
||||
it "returns Nothing for a bare variable" $
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue