{-# LANGUAGE TemplateHaskell #-}
-- | Types
module Induction.Structural.Types
(
-- * Obligations
Obligation(..), TaggedObligation,
Predicate,
Hypothesis,
-- ** Terms
Term(..),
-- * Typing environment
TyEnv,
-- ** Arguments
Arg(..),
-- * Tagged (fresh) variables
Tagged(..), tag,
-- ** Removing tagged variables
unTag, unTagM, unTagMapM
) where
import Data.Generics.Genifunctors
import Control.Monad.Identity
import Control.Monad.State
import Control.Applicative
import Data.Function (on)
import Data.Traversable (traverse)
import Data.Map (Map)
import qualified Data.Map as M
-- | The simple term language only includes variables, constructors and functions.
data Term c v
= Var v
| Con c [Term c v]
| Fun v [Term c v]
-- ^ Induction on exponential data types yield assumptions with functions
deriving (Eq,Ord)
-- Typed variables are represented as (v,t)
-- | A list of terms.
--
-- Example: @[tm1,tm2]@ corresponds to the formula /P(tm1,tm2)/
type Predicate c v = [Term c v]
-- | Quantifier lists are represented as tuples of variables and their type.
--
-- Example:
--
--
-- >Obligation
-- > { implicit = [(x,t1),(y,t2)]
-- > , hypotheses = [([],[htm1,htm2])
-- > ,([(z,t3)],[htm3,htm4])
-- > ]
-- > , conclusion = [tm1,tm2]
-- > }
--
-- Corresponds to the formula:
--
-- /forall (x : t1) (y : t2) . (P(htm1,htm2) & (forall (z : t3) . P(htm3,htm4)) => P(tm1,tm2))/
--
-- The implicit variables (/x/ and /y/) can be viewed as skolemised, and use
-- these three formulae instead:
--
-- /P(htm1,htm2)./
--
-- /forall (z : t3) . P(htm3,htm4)./
--
-- /~ P(tm1,tm2)./
--
data Obligation c v t = Obligation
{ implicit :: [(v,t)]
-- ^ Implicitly quantified variables (skolemised)
, hypotheses :: [Hypothesis c v t]
-- ^ Hypotheses, with explicitly quantified variables
, conclusion :: Predicate c v
-- ^ The induction conclusion
}
-- | Quantifier lists are represented as tuples of variables and their type.
--
-- Example:
--
-- @([(x,t1),(y,t2)],[tm1,tm2])@
--
-- corresponds to the formula
--
-- /forall (x : t1) (y : t2) . P(tm1,tm2)/
type Hypothesis c v t = ([(v,t)],Predicate c v)
-- | An argument to a constructor can be recursive (`Rec`) or non-recursive
-- (`NonRec`). Induction hypotheses will be asserted for `Rec` arguments.
--
-- For instance, when doing induction on @[a]@, then @(:)@ has two arguments,
-- @NonRec a@ and @Rec [a]@. On the other hand, if doing induction on @[Nat]@,
-- then @(:)@ has @NonRec Nat@ and @Rec [Nat]@.
--
-- Data types can also be exponential. Consider
--
-- @data Ord = Zero | Succ Ord | Lim (Nat -> Ord)@
--
-- Here, the @Lim@ constructor is exponential. If we describe types and
-- constructors with strings, the constructors for this data type is:
--
-- >[ ("Zero",[])
-- >, ("Succ",[Rec "Ord"])
-- >, ("Lim",[Exp ("Nat -> Ord") ["Nat"])
-- >]
--
-- The first argument to `Exp` is the type of the function, and the second
-- argument are the arguments to the function.
data Arg t
= Rec t
| NonRec t
| Exp t [t]
-- | Given a type, return either that you cannot do induction on is type
-- (`Nothing`), or `Just` the constructors and a description of their arguments
-- (see `Arg`).
--
-- The function /should instantiate type variables/. For instance, if you look
-- up the type @[Nat]@, you should return the cons constructor with arguments
-- @Nat@ and @[Nat]@ (see `Arg`).
--
-- Examples of types not possible to do induction on are function spaces and
-- type variables. For these, return `Nothing`.
type TyEnv c t = t -> Maybe [(c,[Arg t])]
-- | Cheap way of introducing fresh variables. The `Eq` and `Ord` instances
-- only uses the `Integer` tag.
data Tagged v = v :~ Integer
-- | The `Integer` tag
tag :: Tagged v -> Integer
tag (_ :~ t) = t
instance Eq (Tagged v) where
(==) = (==) `on` tag
instance Ord (Tagged v) where
compare = compare `on` tag
-- | Obligations with tagged variables (see `Tagged` and `unTag`)
type TaggedObligation c v t = Obligation c (Tagged v) t
-- | Tri-traverse of Obligation
trObligation :: Applicative f => (c -> f c') -> (v -> f v') -> (t -> f t') -> Obligation c v t -> f (Obligation c' v' t')
trObligation = $(genTraverse ''Obligation)
-- | Removing tagged (fresh) variables in a monad.
-- The remove function is exectued at /every occurence/ of a tagged variable.
-- This is useful if you want to sync it with your own name supply monad.
unTagM :: Applicative m => (Tagged v -> m v') -> [TaggedObligation c v t] -> m [Obligation c v' t]
unTagM f = traverse (trObligation pure f pure)
-- | Removing tagged (fresh) variables
unTag :: (Tagged v -> v') -> [TaggedObligation c v t] -> [Obligation c v' t]
unTag f = runIdentity . unTagM (return . f)
-- | Remove tagged (fresh) variables in a monad.
-- The remove function is exectued /only once/ for each tagged variable,
-- and a `Map` of renamings is returned.
-- This is useful if you want to sync it with your own name supply monad.
unTagMapM :: (Functor m,Monad m) => (Tagged v -> m v') -> [TaggedObligation c v t]
-> m ([Obligation c v' t],Map (Tagged v) v')
unTagMapM f = flip runStateT M.empty . unTagM f'
where
f' tv = do
m <- get
case M.lookup tv m of
Just v' -> return v'
Nothing -> do
v' <- lift (f tv)
modify (M.insert tv v')
return v'