Home > Net >  Haskell theorem proving tactics as indexed functors and monads
Haskell theorem proving tactics as indexed functors and monads

Time:03-27

I am trying to follow along with this blog post to make a simple intuitionistic theorem proving language in Haskell. Mr. van Bakel suggests using indexed monads for proof state manipulation; here are the building pieces to an indexed monad (equivalent to the definitions from Control.Monad.Indexed):

class IFunctor f where
  imap :: (a -> b) -> f j k a -> f j k b

class IFunctor m => IPointed m where
  ipure :: a -> m i i a

class IPointed m => IApplicative m where
  iap :: m i j (a -> b) -> m j k a -> m i k b

class IApplicative m => IMonad m where
  ibind :: (a -> m j k b) -> m i j a -> m i k b

ijoin :: IMonad m => m i j (m j k a) -> m i k a
ijoin = ibind id

infixr 1 =<<<
infixl 1 >>>=

(>>>=) :: IMonad m => m i j a -> (a -> m j k b) -> m i k b
m >>>= k = ibind k m

(=<<<) :: IMonad m => (a -> m j k b) -> m i j a -> m i k b
(=<<<) = ibind

I am struggling to properly instantiate these classes with the following definition of Tactic:

data Tactic i j a = Tactic ((a -> j) -> i)

Starting with IFunctor:

instance IFunctor Tactic where
  imap f (Tactic g) = Tactic (\ h -> g (h . f))

-- f :: a -> b
-- g :: (a -> j) -> i
-- h :: b -> j

And now to make it pointed:

instance IPointed Tactic where
  ipure a = Tactic (\ h -> h a)

Simple enough. I cannot wrap my head around constructing the applicative and monadic instances, however. My guess for the monad is

instance IMonad Tactic where
  ibind f (Tactic g) = Tactic (\ h -> imap g (imap h . f))

-- f :: a -> Tactic ((b -> k) -> j)
-- g :: (a -> j) -> i
-- h :: b -> k
-- RHS :: Tactic ((b -> k) -> i)

since the signatures seem to check out. I am completely stumped by the applicative instance though.

instance IApplicative Tactic where
  iap (Tactic f) (Tactic g) = Tactic (\ h -> ???)

-- f :: ((a -> b) -> j) -> i
-- g :: (a -> k) -> j
-- h :: b -> k
-- RHS :: Tactic ((b -> k) -> i)

What would you suggest?


Edit: I got the applicative instance to work via

instance IApplicative Tactic where
  iap (Tactic f) (Tactic g) = Tactic (\ h -> f (\ x -> g (h . x)))

-- f :: ((a -> b) -> j) -> i
-- g :: (a -> k) -> j
-- h :: b -> k
-- RHS :: Tactic ((b -> k) -> i)
-- x :: a -> b, h . x :: a -> k

thanks to Li-Yao's hint on the typo in g signature, but I am still stuck on the bind definition.

CodePudding user response:

Hints:

  • There's a typo in the type of g in the comment (EDIT: now fixed)
  • What's the type of the hole ???? (see more details below)
  • Another way is to implement iap using imap and ibind, the same way you can implement (<*>) using fmap and (>>=)
  • Tactic is a more indexed version of the continuation monad type Cont r a = (a -> r) -> r, so if you're familiar with that, the implementation is the same.

You can do type-driven programming by putting a hole _ and by looking at the error message from the compiler.

instance IMonad Tactic where
  ibind f (Tactic g) = Tactic _

-- error: Found hole: _ :: (b -> k) -> i

When the hole has a function type, it's always safe to start with a lambda:

instance IMonad Tactic where
  ibind f (Tactic g) = Tactic (\h -> _)

-- error: Found hole: _ :: i
-- Relevant bindings include
--        h :: b -> k
--        g :: (a -> j) -> i
--        f :: a -> Tactic j k b

The only way to produce an i is to apply g to some argument.

instance IMonad Tactic where
  ibind f (Tactic g) = Tactic (\h -> g _)

-- error: Found hole: _ :: a -> j

Again a function.

instance IMonad Tactic where
  ibind f (Tactic g) = Tactic (\h -> g (\a -> _))

-- error: Found hole: _ :: j
-- Relevant bindings include
--        a :: a
--        h :: b -> k
--        g :: (a -> j) -> i
--        f :: a -> Tactic j k b

There is no obvious way to produce a j, but there is a way to use the a which we just introduced, with f :: a -> Tactic j k b (and upon closer inspection we see that it will in fact yield a j eventually). We can then pattern-match on the resulting Tactic to get some more data:

instance IMonad Tactic where
  ibind f (Tactic g) = Tactic (\h -> g (\a ->
    let Tactic p = f a in _))

-- error: Found hole: _ :: j
-- Relevant bindings include
--        p :: (b -> k) -> j
--        a :: a
--        h :: b -> k
--        g :: (a -> j) -> i
--        f :: a -> Tactic j k b

The final step is left as an exercise for the reader.

  • Related