packages feed

smartcheck-0.1: examples/LambdaCalc.hs

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}

-- Copied from <http://augustss.blogspot.com/2007/10/simpler-easier-in-recent-paper-simply.html>

module LambdaCalc where

import Data.List
import Data.Typeable

import Control.Monad
import GHC.Generics

import Test.QuickCheck

import Test.SmartCheck

type Sym = String

data Expr
        = Var Sym
        | App Expr Expr
        | Lam Sym Expr
        deriving (Eq, Read, Show, Typeable, Generic)

freeVars :: Expr -> [Sym]
freeVars (Var v) = [v]
freeVars (App f a) = freeVars f `union` freeVars a
freeVars (Lam i e) = freeVars e \\ [i]

subst :: Sym -> Expr -> Expr -> Expr
subst v x b = sub b
  where sub e@(Var i) = if i == v then x else e
        sub (App f a) = App (sub f) (sub a)
        sub (Lam i e) =
            if v == i then
                Lam i e
            else if i `elem` fvx then
                let i' = cloneSym e i
                    e' = substVar i i' e
                in  Lam i' (sub e')
            else
                Lam i (sub e)
        fvx = freeVars x
        cloneSym e i = loop i
           where loop i' = if i' `elem` vs then loop (i ++ "'") else i'
                 vs = fvx ++ freeVars e

substVar :: Sym -> Sym -> Expr -> Expr
substVar v v' e = subst v (Var v') e

alphaEq :: Expr -> Expr -> Bool
alphaEq (Var v)   (Var v')    = v == v'
alphaEq (App f a) (App f' a') = alphaEq f f' && alphaEq a a'
alphaEq (Lam v e) (Lam v' e') = alphaEq e (substVar v' v e')
alphaEq _ _ = False

nf :: Expr -> Expr
nf ee = spine ee []
  where spine (App f a) as = spine f (a:as)
        spine (Lam v e) [] = Lam v (nf e)
        spine (Lam v e) (a:as) = spine (subst v a e) as
        spine f as = app f as
        app f as = foldl App f (map nf as)

betaEq :: Expr -> Expr -> Bool
betaEq e1 e2 = alphaEq (nf e1) (nf e2)

z,s,m,n :: Expr
[z,s,m,n] = map (Var . (:[])) "zsmn"
app2 :: Expr -> Expr -> Expr -> Expr
app2 f x y = App (App f x) y
zero, one, two, three, plus :: Expr
zero  = Lam "s" $ Lam "z" z
one   = Lam "s" $ Lam "z" $ App s z
two   = Lam "s" $ Lam "z" $ App s $ App s z
three = Lam "s" $ Lam "z" $ App s $ App s $ App s z
plus  = Lam "m" $ Lam "n" $ Lam "s" $ Lam "z" $ app2 m s (app2 n s z)

test0 :: Bool
test0 = betaEq (app2 plus one two) three

---------------------------------------------------------------------------------

instance SubTypes Expr
instance SubTypes Pr

---------------------------------------------------------------------------------

data Pr = Pr Expr Expr
  deriving (Read, Show, Typeable, Generic)

instance Arbitrary Expr where
  arbitrary = sized mkE
    where
    mkE 0 = liftM Var vars
    mkE x = oneof [ liftM2 App (liftM2 Lam vars mkE') mkE'
                  , liftM2 Lam vars mkE'
                  ]
      where
      mkE' = mkE =<< choose (0, x-1)

vars :: Gen [Char]
vars = oneof $ map return ["x", "y", "z"]

instance Arbitrary Pr where
  arbitrary = do expr  <- arbitrary
                 return $ Pr expr expr

---------------------------------------------------------------------------------

-- prop0 :: Pr -> Property
-- prop0 (Pr (e0, e1)) = alphaEq e0 e1 ==> betaEq e0 e1

-- if you do a beta reduction to nf
-- prop1 :: Pr -> ScProperty
-- prop1 (Pr e0 e1) = -- Timeout due to possible non-termination
--   within 1000 $ alphaEq e0 e1 --> betaEq e0 (substVar "x" "y" e1)

-- lambdaTest :: IO ()
-- lambdaTest = smartCheck args prop1
--   where args = scStdArgs { qcArgs = stdArgs { maxSuccess = 100
--                                             , maxSize    = 100
--                                             }
--                          }

---------------------------------------------------------------------------------
-- Cruft

{-
nonDet = App x x
  where
  x = Lam "x" (App (Var "x") (Var "x"))


xx = (App (Lam "`" (App (Lam "\SI" (Var "f")) 
                        (App (Lam "" (Var "O\172")) 
                             (Var "3UC")))))

aa (Pr a b) = alphaEq a b
-}