binder-0.2: test/Binder3Spec.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Binder3Spec where
import Control.Lens
import Control.Monad ((<=<))
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Trans.State.Strict (evalStateT, get, modify, StateT)
import Data.List (intersperse)
-- import qualified Data.Map.Lazy as M
import Data.Text (Text)
import qualified Data.Text as T (pack)
import GHC.Generics hiding (S, to)
import Test.Hspec
import Data.Binder
newtype S a = S { runS :: StateT Int IO a }
deriving
( Generic
, Generic1
, Functor
, Applicative
, Monad
, MonadIO
)
instance MonadNumbering S where
type Numbering S = Int
numbering = do
i <- S $ get
S $ modify succ
return i
-- This example is stolen from one example of bindlib.
-- https://github.com/rlepigre/ocaml-bindlib/blob/master/examples/pred2.ml
data Symbol = Symbol Text Int
deriving (Eq, Show)
data Term
= Term'Var (Var S Term)
| Term'Fun Symbol [Term]
data Form
= Form'Imply Form Form
| Form'Univ1 (Binder Term S Form)
| Form'Univ2 Int (Binder Pred S Form)
| Form'FVari (Var S Pred) [Term]
newtype Pred = Pred { unPred :: BinderList Term S Form }
data Proof
= Proof'ImplyI Form (Binder Proof S Proof)
| Proof'ImplyE Proof Proof
| Proof'Univ1I (Binder Term S Proof)
| Proof'Univ1E Proof Term
| Proof'Univ2I Int (Binder Pred S Proof)
| Proof'Univ2E Proof Pred
| Proof'Axiom Form (Var S Proof)
pred'Arity :: Getter Pred Int
pred'Arity = to unPred . binderList'Arity
pred'makeRaw :: Box S (BinderList Term S Form) -> Box S Pred
pred'makeRaw = fmap Pred
pred'make :: VarList S Term -> Box S Form -> Box S Pred
pred'make xs t = pred'makeRaw $ bindList xs t
term'mkFree :: Var S Term -> S Term
term'mkFree = return . Term'Var
pred'mkFree :: Int -> Var S Pred -> S Pred
pred'mkFree n vp = do
let names = if n == 1 then ["x"]
else flip map [1..n] $ \i -> "x" <> T.pack (show i)
xs <- newVarList names term'mkFree
let ts = boxList $ map term'Var xs
p = Form'FVari vp <$> ts
fmap Pred $ unbox $ bindList xs p
proof'mkFree :: Form -> Var S Proof -> S Proof
proof'mkFree f = return . Proof'Axiom f
term'Var :: Var S Term -> Box S Term
term'Var = boxVar
term'Fun :: Symbol -> [Box S Term] -> Box S Term
term'Fun s ts = Term'Fun s <$> boxList ts
form'Imply :: Box S Form -> Box S Form -> Box S Form
form'Imply f g = Form'Imply <$> f <*> g
form'Univ1Raw :: Box S (Binder Term S Form) -> Box S Form
form'Univ1Raw = fmap Form'Univ1
form'Univ1 :: Var S Term -> Box S Form -> Box S Form
form'Univ1 x t = form'Univ1Raw $ bind x t
form'Univ2Raw :: Int -> Box S (Binder Pred S Form) -> Box S Form
form'Univ2Raw arity = fmap $ Form'Univ2 arity
form'Univ2 :: Int -> Var S Pred -> Box S Form -> Box S Form
form'Univ2 arity x t = form'Univ2Raw arity $ bind x t
form'FVari :: Var S Pred -> [Box S Term] -> Box S Form
form'FVari x ts = Form'FVari x <$> boxList ts
proof'ImplyIRaw :: Box S Form -> Box S (Binder Proof S Proof) -> Box S Proof
proof'ImplyIRaw p b = Proof'ImplyI <$> p <*> b
proof'ImplyI :: Box S Form -> Var S Proof -> Box S Proof -> Box S Proof
proof'ImplyI p x t = proof'ImplyIRaw p $ bind x t
proof'ImplyE :: Box S Proof -> Box S Proof -> Box S Proof
proof'ImplyE p q = Proof'ImplyE <$> p <*> q
proof'Univ1IRaw :: Box S (Binder Term S Proof) -> Box S Proof
proof'Univ1IRaw = fmap Proof'Univ1I
proof'Univ1I :: Var S Term -> Box S Proof -> Box S Proof
proof'Univ1I x t = proof'Univ1IRaw $ bind x t
proof'Univ1E :: Box S Proof -> Box S Term -> Box S Proof
proof'Univ1E p q = Proof'Univ1E <$> p <*> q
proof'Univ2IRaw :: Int -> Box S (Binder Pred S Proof) -> Box S Proof
proof'Univ2IRaw arity = fmap $ Proof'Univ2I arity
proof'Univ2I :: Int -> Var S Pred -> Box S Proof -> Box S Proof
proof'Univ2I arity x t = proof'Univ2IRaw arity $ bind x t
proof'Univ2E :: Box S Proof -> Box S Pred -> Box S Proof
proof'Univ2E p q = Proof'Univ2E <$> p <*> q
proof'Axiom :: Box S Form -> Var S Proof -> Box S Proof
proof'Axiom f v = (\g -> Proof'Axiom g v) <$> f
boxTerm :: Term -> S (Box S Term)
boxTerm (Term'Var x) = return $ term'Var x
boxTerm (Term'Fun s ts) = fmap (term'Fun s) $ sequenceA $ boxTerm <$> ts
boxForm :: Form -> S (Box S Form)
boxForm (Form'Imply a b) = form'Imply <$> boxForm a <*> boxForm b
boxForm (Form'Univ1 b) = form'Univ1Raw <$> boxBinder boxForm b
boxForm (Form'Univ2 a b) = form'Univ2Raw a <$> boxBinder boxForm b
boxForm (Form'FVari x ts) = do
let arg1 = unPred <$> boxVar x
arg2 <- fmap boxList $ sequenceA $ boxTerm <$> ts
boxJoin $ substList <$> arg1 <*> arg2
showTerm :: Term -> Text
showTerm (Term'Var x) = nameOf x
showTerm (Term'Fun (Symbol s _) ts) =
s <> "(" <> mconcat (intersperse ", " $ map showTerm ts) <> ")"
showForm :: Form -> S Text
showForm (Form'Imply a b) = do
sha <- showForm a
shb <- showForm b
return $ "(" <> sha <> ") => (" <> shb <> ")"
showForm (Form'Univ1 b) = do
(x, t) <- unbind b
sht <- showForm t
return $ "forall_1 " <> nameOf x <> ".(" <> sht <> ")"
showForm (Form'Univ2 _ b) = do
(x, t) <- unbind b
sht <- showForm t
return $ "forall_2 " <> nameOf x <> ".(" <> sht <> ")"
showForm (Form'FVari x ts) = do
return $ nameOf x <> "(" <> mconcat (intersperse ", " $ map showTerm ts) <> ")"
eqTerm :: Term -> Term -> Bool
eqTerm (Term'Var x) (Term'Var y) = x == y
eqTerm (Term'Fun s1 ts1) (Term'Fun s2 ts2) =
s1 == s2 && and (map (uncurry eqTerm) $ zip ts1 ts2)
eqTerm _ _ = False
eqForm :: Form -> Form -> S Bool
eqForm (Form'Imply a1 b1) (Form'Imply a2 b2) = do
ca <- eqForm a1 a2
cb <- eqForm b1 b2
return $ ca && cb
eqForm (Form'Univ1 b1) (Form'Univ1 b2) = eqBinder eqForm b1 b2
eqForm (Form'Univ2 a1 b1) (Form'Univ2 a2 b2) = do
c <- eqBinder eqForm b1 b2
return $ a1 == a2 && c
eqForm (Form'FVari x1 ts1) (Form'FVari x2 ts2) =
return $ x1 == x2 && and (map (uncurry eqTerm) $ zip ts1 ts2)
eqForm _ _ = return False
data BadProof
= BadProof'Imply
| BadProof'ImplyDifferForm Form Form
| BadProof'Univ1
| BadProof'Univ2
showBadProof :: BadProof -> S Text
showBadProof BadProof'Imply = return "BadProof'Imply"
showBadProof (BadProof'ImplyDifferForm a b) = do
sha <- showForm a
shb <- showForm b
return $ "BadProof'ImplyDifferForm (" <> sha <> ") (" <> shb <> ")"
showBadProof BadProof'Univ1 = return "BadProof'Univ1"
showBadProof BadProof'Univ2 = return "BadProof'Univ2"
typeInfer :: Proof -> S (Either BadProof Form)
typeInfer = unbox <=< fn
where
fn :: Proof -> S (Box S (Either BadProof Form))
fn (Proof'ImplyI f p) = do
ax <- newVar (p ^. binder'Name) $ proof'mkFree f
tax <- proof'mkFree f ax
pr <- subst p tax
ber <- fn pr
er <- unbox ber
case er of
Right r -> do
bf <- boxForm f
br <- boxForm r
return $ Right <$> form'Imply bf br
Left err -> return $ pure $ Left err
fn (Proof'ImplyE p1 p2) = do
mf1' <- unbox =<< fn p2
mf2' <- unbox =<< fn p1
case (mf1', mf2') of
(Right f1', Right (Form'Imply f1 f2)) -> do
b <- eqForm f1 f1'
if b then fmap Right <$> boxForm f2
else return $ pure $ Left $ BadProof'ImplyDifferForm f1 f1'
_ -> return $ pure $ Left BadProof'Imply
fn (Proof'Univ1I p) = do
t <- newVar (p ^. binder'Name) term'mkFree
te <- term'mkFree t
pr <- subst p te
ef <- unbox =<< fn pr
case ef of
Right f -> do
bf <- boxForm f
return $ Right <$> form'Univ1 t bf
Left err -> return $ pure $ Left err
fn (Proof'Univ1E p t) = do
mf <- unbox =<< fn p
case mf of
Right (Form'Univ1 b) -> do
f <- subst b t
fmap Right <$> boxForm f
Right _ -> return $ pure $ Left BadProof'Univ1
Left err -> return $ pure $ Left err
fn (Proof'Univ2I arity f) = do
t <- newVar (f ^. binder'Name) $ pred'mkFree arity
pr <- pred'mkFree arity t
eg <- unbox =<< fn =<< subst f pr
case eg of
Right g -> do
bg <- boxForm g
return $ fmap Right $ form'Univ2 arity t bg
Left err -> return $ pure $ Left err
fn (Proof'Univ2E p p0) = do
mf <- unbox =<< fn p
case mf of
Right (Form'Univ2 arity b) -> if arity == p0 ^. pred'Arity
then do
f <- subst b p0
fmap Right <$> boxForm f
else return $ pure $ Left BadProof'Univ2
Right _ -> return $ pure $ Left BadProof'Univ2
Left err -> return $ pure $ Left err
fn (Proof'Axiom f _) = fmap Right <$> boxForm f
typeCheck :: Proof -> Form -> S Bool
typeCheck p f0 = do
ef <- typeInfer p
case ef of
Right f -> eqForm f0 f
_ -> return False
leq :: S (Box S Pred)
leq = do
u <- newVar "u" term'mkFree
v <- newVar "v" term'mkFree
let bu = term'Var u
bv = term'Var v
x <- newVar "X" $ pred'mkFree 1
let bl = unPred <$> boxVar x
p1 <- bindListApply bl $ boxList [bu]
p2 <- bindListApply bl $ boxList [bv]
return $ fmap Pred $ bindList [u, v] $ form'Univ2 1 x $ form'Imply p1 p2
equalTransitive :: S Form
equalTransitive = do
q <- leq
x <- newVar "x" term'mkFree
y <- newVar "y" term'mkFree
z <- newVar "z" term'mkFree
let bx = term'Var x
by = term'Var y
bz = term'Var z
bl = fmap unPred q
p1 <- bindListApply bl $ boxList [bx, by]
p2 <- bindListApply bl $ boxList [by, bz]
p3 <- bindListApply bl $ boxList [bx, bz]
unbox $ form'Univ1 x $ form'Univ1 y $ form'Univ1 z $
form'Imply p1 $ form'Imply p2 p3
equalTransitiveProof :: S Proof
equalTransitiveProof = do
q <- leq
x <- newVar "x" term'mkFree
y <- newVar "y" term'mkFree
z <- newVar "z" term'mkFree
let bx = term'Var x
by = term'Var y
bz = term'Var z
bl = fmap unPred q
f <- bindListApply bl $ boxList [bx, by]
uf <- unbox f
h1 <- newVar "h1" $ proof'mkFree uf
g <- bindListApply bl $ boxList [by, bz]
ug <- unbox g
h2 <- newVar "h2" $ proof'mkFree ug
px <- newVar "X" $ pred'mkFree 1
p <- bindListApply (unPred <$> boxVar px) $ boxList [bx]
up <- unbox p
h3 <- newVar "h3" $ proof'mkFree up
unbox $ proof'Univ1I x $ proof'Univ1I y $ proof'Univ1I z $
proof'ImplyI f h1 $ proof'ImplyI g h2 $
proof'Univ2I 1 px $ proof'ImplyI p h3 $
proof'ImplyE (proof'Univ2E (boxVar h2) (boxVar px)) $
proof'ImplyE (proof'Univ2E (boxVar h1) (boxVar px)) (boxVar h3)
spec :: Spec
spec = describe "leq" $ do
it "forms correctly" $ do
let r = "forall_1 x.(forall_1 y.(forall_1 z.((forall_2 X.((X(x)) => (X(y)))) => ((forall_2 X.((X(y)) => (X(z)))) => (forall_2 X.((X(x)) => (X(z))))))))"
flip shouldReturn r $ flip evalStateT 0 $ runS $ do
f <- equalTransitive
showForm f
it "infers type soundly" $ do
let r = "forall_1 x.(forall_1 y.(forall_1 z.((forall_2 X.((X(x)) => (X(y)))) => ((forall_2 X.((X(y)) => (X(z)))) => (forall_2 X.((X(x)) => (X(z))))))))"
flip shouldReturn r $ flip evalStateT 0 $ runS $ do
ef <- typeInfer =<< equalTransitiveProof
case ef of
Right f -> showForm f
Left bp -> showBadProof bp
it "checks correctly" $ do
let r = True
flip shouldReturn r $ flip evalStateT 0 $ runS $ do
p <- equalTransitiveProof
f <- equalTransitive
typeCheck p f