-- Naive Intuitionistic Proof Search for Propositional Logic -- Adapted to conventions from ex 1 -- 18.01.2018 Simon Wimmer import Data.Maybe import Control.Monad type VName = String -- Logic Formulae data Prop = Atom VName | Imp Prop Prop deriving Eq instance Show Prop where show (Atom x) = x show (Imp (Atom x) b) = x ++ " --> " ++ show b show (Imp a b) = "(" ++ show a ++ ") --> " ++ show b infixr 7 --> (-->) = Imp -- Proof Terms data Term = V VName | Abs VName Term | App Term Term showVar x = "'" ++ x ++ "'" instance Show Term where show (V x) = showVar x show (Abs x t) = "λ" ++ showVar x ++ ". " ++ show t show (App (Abs x t) (V y)) = "(" ++ show (Abs x t) ++ ") " ++ showVar y show (App (Abs x t) t2) = "(" ++ show (Abs x t) ++ ") (" ++ show t2 ++ ")" show (App t1 (V x)) = show t1 ++ " " ++ showVar x show (App t1 t2) = show t1 ++ " (" ++ show t2 ++ ")" infixl 7 \$\$ (\$\$) = App -- Finds the first successful computation in a list -- of computations and returns it. -- Example: firstJust [Nothing, Just 1, Just 3] = Just 1 firstJust :: [Maybe a] -> Maybe a firstJust = foldr mplus Nothing type Goal = ([Prop], Prop) -- Try to solve a goal by assumption assumption :: Goal -> Maybe Term assumption (gamma, prop) = if prop `elem` gamma then Just (V (show prop)) else Nothing -- Try to solve a goal by applying (-> Intro) -- Second argument is the current 'path'. intro :: Goal -> [Goal] -> Maybe Term intro (gamma, Imp a b) hist = do { -- The if-clause is crucial for termination t <- solve (if a `elem` gamma then gamma else a : gamma, b) hist; return (Abs (show a) t) } intro (gamma, _) hist = Nothing -- Try to solve a goal by applying (-> Elim). -- Second argument is the current 'path'. elim :: Goal -> [Goal] -> Maybe Term elim (gamma, prop) hist = if isImp prop then Nothing else firstJust (map (\t -> solveElim (V (show t)) t) (filter isCandidate gamma)) where isCandidate (Imp _ b) = b == prop || isCandidate b isCandidate x = x == prop solveElim l (Imp a b) = do { r <- solve (gamma, a) hist; solveElim (App l r) b } solveElim l b = Just l isImp (Imp _ _) = True isImp _ = False -- Given a goal and a list of previous goals on the current 'path', -- perform a cycle test and try to solve the goal by trying -- assumption, intro and elim. solve :: Goal -> [Goal] -> Maybe Term solve goal hist = if goal `elem` hist then Nothing else firstJust [ intro goal (goal : hist), elim goal (goal : hist) ] -- Wrapping up solve prove :: Prop -> Maybe Term prove prop = solve ([], prop) [] -- Test cases p = Atom "P" q = Atom "Q" r = Atom "R" p1 = p --> q p2 = p --> p p3 = p --> q --> p -- Example below theorem 4.0.6 p4 = (p --> q --> r) --> (p --> q) --> p --> r -- This comment should be irrelevant with the new proof method -- The proof tree we get is slightly different from the lecture. Why? -- (Solution: we are biased towards intro --> try swapping intro and elim in solve) peirce = ((((p --> q) --> p) --> p) --> q) --> q -- This yields the same proof tree as in ex 1 ha3 = ((r --> q) --> q) --> (r --> p) --> (p --> q) --> q -- Same proof tree as in homework 3 examples = [p, q, r, p1, p2, p3, p4, peirce, ha3] -- Example of a changed proof changed = (p --> q) --> p --> q