module Term (
Id,
Term(..),
vars, fvs,
freshName,
subst
) where
import Data.List
import Data.Maybe (fromJust)
type Id = String
data Term = Var Id | App Term Term | Abs Id Term
vars :: Term -> [Id]
vars (Var x) = [x]
vars (App t u) = vars t `union` vars u
vars (Abs x t) = [x] `union` vars t
fvs :: Term -> [Id]
fvs (Var x) = [x]
fvs (App t u) = fvs t `union` fvs u
fvs (Abs x t) = fvs t \\ [x]
variants :: String -> [String]
variants [] = [[]]
variants [x] = [[y] | y <- [x .. 'z']] ++ map (x:) (variants "a")
variants (x:xs) = map (x:) $ variants xs
rename :: (Id -> Id) -> Term -> Term
rename r (Var y) = Var (r y)
rename r (App t u) = App (rename r t) (rename r u)
rename r (Abs x t) = Abs (r x) (rename r t)
freshName :: Id -> [Id] -> (Id, (Term -> Term))
freshName x ys = (x', ren)
where
x' = head $ dropWhile (`elem` ys) $ variants x
ren = rename (\v -> if v == x then x' else v)
subst :: Id -> Term -> Term -> Term
subst x s (Var y) = if x == y then s else Var y
subst x s (App t u) = App (subst x s t) (subst x s u)
subst x s u@(Abs y t) = if x `elem` fvs u then Abs y' (subst x s (ren t)) else u
where
taboo = [x] `union` (fvs s `union` (vars t \\ [y]))
(y', ren) = freshName y taboo
instance Show Term where
showsPrec d t | isChurchNum t = shows $ fromJust $ churchNum t
showsPrec d (Var x) = showString x
showsPrec d (App t u) = showParen (d > app_prec) $
showsPrec app_prec t .
showString " " .
showsPrec (app_prec + 1) u
where
app_prec = 5
showsPrec d (Abs x t) = showParen (d > abs_prec) $ showString "\\" . showsAbs x t
where
abs_prec = 4
showsAbs x (Abs y t) = showString x . showString " " . showsAbs y t
showsAbs x t = showString x . showString ". " . showsPrec abs_prec t
churchNum :: Term -> Maybe Int
churchNum (Abs f (Abs x t)) = power f x t
where
power _ x (Var y) = if x == y then Just 0 else Nothing
power f x (App (Var g) t) =
if f == g then case power f x t of
Nothing -> Nothing
Just n -> Just $ n + 1
else Nothing
power _ _ _ = Nothing
churchNum _ = Nothing
isChurchNum t = churchNum t /= Nothing