1 {-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
2 {-# LANGUAGE PatternSynonyms #-}
6 -- Copyright : Tomáš Musil 2014
9 -- Maintainer : tomik.musil@gmail.com
10 -- Stability : experimental
12 -- This is a toy λ-calculus implementation.
27 import Data.Attoparsec.Text
28 import Control.Applicative
29 import Control.Monad.State
32 -- >>> import Test.QuickCheck
33 -- >>> import Control.Applicative
34 -- >>> let aTerm 0 = pure $ Var "x"
35 -- >>> let aTerm n = oneof [pure (Var "x"), liftA (Lambda "x") $ aTerm (n - 1), liftA2 App (aTerm (n `div` 2)) (aTerm (n `div` 2))]
36 -- >>> instance Arbitrary Term where arbitrary = sized aTerm
41 -- >>> print $ Lambda "x" (Var "x")
44 data Term = Var VarName | Lambda VarName Term | App Term Term deriving (Eq)
46 pattern RedEx x t s = App (Lambda x t) s
47 pattern AppApp a b c = App a (App b c)
48 pattern EmLambda x y t = Lambda x (Lambda y t)
51 instance Show Term where
53 show (EmLambda x y t) = show (Lambda (x ++ " " ++ y) t)
54 show (Lambda x t) = "(λ" ++ x ++ "." ++ show t ++ ")"
55 show (AppApp a b c) = show a ++ " " ++ braced (App b c)
56 show (App t r) = show t ++ " " ++ show r
58 braced :: Term -> String
59 braced t = "(" ++ show t ++ ")"
62 -- prop> t == tRead (show (t :: Term))
64 tRead :: String -> Term
65 tRead s = case parseOnly (parseTerm <* endOfInput) (T.pack s) of
69 parseVar :: Parser Term
74 parseLambda :: Parser Term
76 char '\\' <|> char 'λ'
77 vars <- sepBy1 parseVar (char ' ')
80 return $! createLambda vars t
82 createLambda :: [Term] -> Term -> Term
83 createLambda (Var x : vs) t = Lambda x $ createLambda vs t
85 createLambda _ _ = error "createLambda failed"
87 parseApp :: Parser Term
89 aps <- sepBy1 (parseBraces <|> parseLambda <|> parseVar) (char ' ')
90 return $! createApp aps
92 createApp :: [Term] -> Term
94 createApp (t:ts:tss) = createApp (App t ts : tss)
95 createApp [] = error "empty createApp"
97 parseBraces :: Parser Term
104 parseTerm :: Parser Term
105 parseTerm = parseApp <|>
110 -------------------------------------------------
112 isFreeIn :: VarName -> Term -> Bool
113 isFreeIn x (Var v) = x == v
114 isFreeIn x (App t u) = x `isFreeIn` t || x `isFreeIn` u
115 isFreeIn x (Lambda v t) = x /= v && x `isFreeIn` t
117 rename :: Term -> Term
118 rename (Lambda x t) = Lambda n (substitute x (Var n) t)
120 rnm v = if (v ++ "r") `isFreeIn` t then rnm (v ++ "r") else v ++ "r"
121 rename _ = error "TODO vymyslet reprezentaci, kde pujde udelat fce, ktera bere jen Lambdy"
123 substitute :: VarName -> Term -> Term -> Term
124 substitute a b (Var x) = if x == a then b else Var x
125 substitute a b (Lambda x t)
126 | x == a = Lambda x t
127 | x `isFreeIn` b = substitute a b $ rename (Lambda x t)
128 | otherwise = Lambda x (substitute a b t)
129 substitute a b (App t u) = App (substitute a b t) (substitute a b u)
133 -- >>> reduce $ tRead "(\\x.x x) (g f)"
136 reduce :: Term -> Term
137 reduce (Var x) = Var x
138 reduce (Lambda x t) = Lambda x (reduce t)
139 reduce (App t u) = app (reduce t) u
140 where app (Lambda x v) w = reduce $ substitute x w v
141 app a b = App a (reduce b)
143 data Strategy = Eager | Lazy
145 reduceStep :: (Monad m) => Term -> m Term
146 reduceStep (RedEx x s t) = return $ substitute x t s
147 reduceStep t = return $ t
149 traversPost :: (Monad m) => (Term -> m Term) -> Term -> m Term
150 traversPost f (App t u) = do
151 nt <- traversPost f t
152 nu <- traversPost f u
154 traversPost f (Lambda x t) = f . Lambda x =<< traversPost f t
155 traversPost f (Var x) = f (Var x)
157 printT :: Term -> IO Term
162 toNormalForm :: Strategy -> Int -> Term -> Maybe Term
163 toNormalForm Eager n = flip evalStateT 0 . traversPost (short n >=> cnt >=> reduceStep)
165 cnt :: (Monad m) => Term -> StateT Int m Term
166 cnt t@(RedEx _ _ _) = do
171 short :: Int -> Term -> StateT Int Maybe Term