packages feed

haskhol-core-1.1.0: src/HaskHOL/Core/State.hs

{-# LANGUAGE PatternSynonyms, TypeFamilies #-}

{-|
  Module:    HaskHOL.Core.State
  Copyright: (c) The University of Kansas 2013
  LICENSE:   BSD3

  Maintainer:  ecaustin@ittc.ku.edu
  Stability:   unstable
  Portability: unknown

  This module exports the stateful layer of HaskHOL.  It consists of:

  * Stateful type primitives not found in "HaskHOL.Core.Types".

  * Stateful term primitives not found in "HaskHOL.Core.Terms".

  * Stateful theory extension primitives not found in "HaskHOL.Core.Kernel".

  * A very primitive debugging system.
-}
module HaskHOL.Core.State
    ( -- * Stateful Type Primitives
      types
    , tyDefinitions
    , getTypeArity
    , newType
    , mkType
    , mkFunTy
    -- * Stateful Term Primitives
    , constants
    , getConstType
    , newConstant
    , mkConst
    , mkConstFull
    , mkEq
    -- * Stateful Theory Extension Primitives
    , axioms
    , newAxiom
    , getAxiom
    , definitions
    , newBasicDefinition
    , getBasicDefinition
    , newBasicTypeDefinition
    , getBasicTypeDefinition
    -- * Primitive Debugging System
    , FlagDebug(..)
    , warn
    , printDebugLn
    , printDebug
      -- * Monad Re-Export
    , module HaskHOL.Core.State.Monad
    ) where

import HaskHOL.Core.Lib
import HaskHOL.Core.Kernel
import HaskHOL.Core.State.Monad

-- New flags and extensions
-- | Flag states whether or not to print debug statements.
newFlag "FlagDebug" True


data TypeConstants = TypeConstants !(Map Text TypeOp) deriving Typeable

deriveSafeCopy 0 'base ''TypeConstants

insertTypeConstant :: Text -> TypeOp -> Update TypeConstants ()
insertTypeConstant ty op =
    do TypeConstants m <- get
       put (TypeConstants (mapInsert ty op m))

getTypeConstants ::  Query TypeConstants (Map Text TypeOp)
getTypeConstants =
    do TypeConstants m <- ask
       return m

makeAcidic ''TypeConstants 
    ['insertTypeConstant, 'getTypeConstants]

initTypeConstants :: Map Text TypeOp
initTypeConstants = mapFromList [("bool", tyOpBool), ("fun", tyOpFun)]


data TypeDefinitions = TypeDefinitions !(Map Text (HOLThm, HOLThm)) 
    deriving Typeable

deriveSafeCopy 0 'base ''TypeDefinitions

insertTypeDefinition :: Text -> (HOLThm, HOLThm) -> Update TypeDefinitions ()
insertTypeDefinition ty defs =
    do TypeDefinitions m <- get
       put (TypeDefinitions (mapInsert ty defs m))

getTypeDefinitions :: Query TypeDefinitions (Map Text (HOLThm, HOLThm))
getTypeDefinitions  =
    do TypeDefinitions m <- ask
       return m

getTypeDefinition :: Text -> Query TypeDefinitions (Maybe (HOLThm, HOLThm))
getTypeDefinition lbl =
    do (TypeDefinitions m) <- ask
       return $! mapLookup lbl m 

makeAcidic ''TypeDefinitions 
    ['insertTypeDefinition, 'getTypeDefinitions, 'getTypeDefinition]


data TermConstants = TermConstants !(Map Text HOLTerm) deriving Typeable

deriveSafeCopy 0 'base ''TermConstants

insertTermConstant :: Text -> HOLTerm -> Update TermConstants ()
insertTermConstant tm op =
    do TermConstants m <- get
       put (TermConstants (mapInsert tm op m))

getTermConstants :: Query TermConstants (Map Text HOLTerm)
getTermConstants =
    do TermConstants m <- ask
       return m

makeAcidic ''TermConstants 
    ['insertTermConstant, 'getTermConstants]

initTermConstants :: Map Text HOLTerm
initTermConstants = mapFromList [("=", tmEq tyA)]

data TheAxioms = TheAxioms !(Map Text HOLThm) deriving Typeable

deriveSafeCopy 0 'base ''TheAxioms

insertAxiom :: Text -> HOLThm -> Update TheAxioms ()
insertAxiom lbl thm =
    do TheAxioms m <- get
       put (TheAxioms (mapInsert lbl thm m))

getAxioms :: Query TheAxioms (Map Text HOLThm)
getAxioms =
    do TheAxioms m <- ask
       return m

getAxiom' :: Text -> Query TheAxioms (Maybe HOLThm)
getAxiom' lbl =
    do TheAxioms m <- ask
       return $! mapLookup lbl m

makeAcidic ''TheAxioms ['insertAxiom, 'getAxioms, 'getAxiom']


data TheCoreDefinitions = 
    TheCoreDefinitions !(Map Text HOLThm) deriving Typeable

deriveSafeCopy 0 'base ''TheCoreDefinitions

insertCoreDefinition :: Text -> HOLThm -> Update TheCoreDefinitions ()
insertCoreDefinition lbl thm =
    do TheCoreDefinitions defs <- get
       put (TheCoreDefinitions (mapInsert lbl thm defs))

getCoreDefinitions :: Query TheCoreDefinitions [HOLThm]
getCoreDefinitions =
    do TheCoreDefinitions defs <- ask
       return $! mapElems defs

getCoreDefinition :: Text -> Query TheCoreDefinitions (Maybe HOLThm)
getCoreDefinition name =
    do (TheCoreDefinitions defs) <- ask
       return $! name `mapLookup` defs

makeAcidic ''TheCoreDefinitions 
    ['insertCoreDefinition, 'getCoreDefinitions, 'getCoreDefinition]


-- Stateful HOL Light Type Primitives
{-|
  Retrieves the 'Map' of type constants from the current working theory.  The
  mapping pairs strings recognized by the parser with the associated
  type operator value, i.e. 

  > ("bool", tyOpBool)
-}
types :: HOL cls thry (Map Text TypeOp)
types =
    do acid <- openLocalStateHOL (TypeConstants initTypeConstants)
       m <- queryHOL acid GetTypeConstants
       closeAcidStateHOL acid
       return m

-- | Retrieves the 'Map' of type definitions from the current working theory.
tyDefinitions :: HOL cls thry (Map Text (HOLThm, HOLThm))
tyDefinitions =
    do acid <- openLocalStateHOL (TypeDefinitions mapEmpty)
       m <- queryHOL acid GetTypeDefinitions
       closeAcidStateHOL acid
       return m

{-|
  Returns the arity associated with a type constant.
  Throws a 'HOLException' if the provided type constant
  name is not defined.
-}
getTypeArity :: Text -> HOL cls thry Int
getTypeArity name =
    do tys <- types
       liftMaybe ("getTypeArity: type " ++ show name ++ 
                  " has not been defined.") .
         liftM (snd . destTypeOp) $ mapLookup name tys

{- 
  Primitive type constant construction function.  Used by newType and 
  newBasicTypeDefinition.  Not exposed to the user.
-}
newType' :: Text -> TypeOp -> HOL Theory thry ()
newType' name tyop =
    do failWhen (can getTypeArity name) $
         "newType: type " ++ show name ++ " has already been declared."
       acid <- openLocalStateHOL (TypeConstants initTypeConstants)
       updateHOL acid (InsertTypeConstant name tyop)
       createCheckpointAndCloseHOL acid

{-| 
  Constructs a new primitve type constant of a given name and arity.  Also adds
  this new type to the current working theory.  Throws a 'HOLException' when a 
  type of the same name has already been declared.
-}
newType :: Text -> Int -> HOL Theory thry ()
newType name arity = 
    newType' name $ newPrimitiveTypeOp name arity

{-|
  Constructs a type application given an operator name and a list of argument
  types.  If the provided name is not a currently defined type constant then
  this function defaults it to a type operator variable.  Throws a 
  'HOLException' in the following cases:

  * A type operator's arity disagrees with the length of the argument list.

  * A type operator is applied to zero arguments.
-}
mkType :: Text -> [HOLType] -> HOL cls thry HOLType
mkType name args =
    do consts <- types
       case mapLookup name consts of
         Just tyOp -> tyApp tyOp args <#?> 
                        "mkType: type constructor application failed"
         Nothing -> 
           {- This seemed to be the easiest way to supress superfluous warnings
              when parsing type operators. -}
           do name' <- if textHead name == '_'
                       then return $! textTail name
                       else printDebugLn 
                              ("warning - mkType: type " ++ show name ++ 
                               " has not been defined.  Defaulting to type " ++ 
                               "operator variable.") $ 
                              return name
              failWhen (return $ null args)
                "mkType: type operator applied to zero args."
              tyApp (mkTypeOpVar name') args <#?> 
                "mkType: type operator variable application failed"

{-|
  Constructs a function type safely using 'mkType'.  Should never fail provided
  that the initial value for type constants has not been modified.
-}
mkFunTy :: HOLType -> HOLType -> HOL cls thry HOLType
mkFunTy ty1 ty2 = mkType "fun" [ty1, ty2]

-- State for Constants
{-|
  Retrieves the 'Map' of term constants from the current working theory.  The
  mapping pairs strings recognized by the parser and the associated
  term constant value, i.e. 

  > ("=", tmEq tyA)
-}
constants :: HOL cls thry (Map Text HOLTerm)
constants =
    do acid <- openLocalStateHOL (TermConstants initTermConstants)
       m <- queryHOL acid GetTermConstants
       closeAcidStateHOL acid
       return m

{-|
  Retrieves the type of a given term constant.  Throws a 'HOLException' if the
  provided term constant name is not defined.
-}
getConstType :: Text -> HOL cls thry HOLType
getConstType name =
    do consts <- constants
       liftMaybe "getConstType: not a constant name" .
         liftM typeOf $ mapLookup name consts

{-
  Primitive term constant construction function.  Used by newConstant,
  newBasicDefinition, and newBasicTypeDefinition.
-}
newConstant' :: Text -> HOLTerm -> HOL Theory thry ()
newConstant' name c =
    do failWhen (can getConstType name) $
         "newConstant: constant " ++ show name ++ " has already been declared."
       acid <- openLocalStateHOL (TermConstants initTermConstants)
       updateHOL acid (InsertTermConstant name c)
       createCheckpointAndCloseHOL acid

{-|
  Constructs a new primitive term constant of a given name and type.  Also adds
  this new term to the current working theory.  Throws a 'HOLException' when a
  term of the same name has already been declared.
-}
newConstant :: Text -> HOLType -> HOL Theory thry ()
newConstant name ty =
    do cond <- can getConstType name
       if cond
          then printDebugLn ("newConstant: ignoring redefintion of " ++ 
                             show name) $ return ()
          else newConstant' name $ newPrimitiveConst name ty

{-|
  Constructs a specific instance of a term constant when provided with its name
  and a type substition environment.  Throws a 'HOLException' in the 
  following cases:

  * The instantiation as performed by 'instConst' fails.

  * The provided name is not a currently defined constant.
-}
mkConst :: TypeSubst l r => Text -> [(l, r)] -> HOL cls thry HOLTerm
mkConst name tyenv =
    do consts <- constants
       tm <- liftMaybe "mkConst: not a constant name" $ 
               mapLookup name consts
       liftMaybe "mkConst: instantiation failed" $ 
         instConst tm tyenv

{-| 
  A version of 'mkConst' that accepts a triplet of type substitition 
  environments.  Frequently used with the 'typeMatch' function.
-}
mkConstFull :: Text -> SubstTrip -> HOL cls thry HOLTerm
mkConstFull name pat =
    do consts <- constants
       tm <- liftMaybe "mkConstFull: not a constant name" $
               mapLookup name consts
       liftMaybe "mkConstFull: instantiation failed" $ 
         instConstFull tm pat
                                    
{-| 
  Safely creates an equality between two terms using 'mkConst' using the type of
  the left hand side argument to perform the required instantiation.  Throws a
  'HOLException' in the case when the types of the two terms do not agree.
-}
mkEq :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm
mkEq l r =
    let ty = typeOf l in
      do eq <- mkConst "=" [(tyA, ty)]
         liftM1 mkComb (mkComb eq l) r <#?> "mkEq"

-- State for Axioms     

{-|
  Retrieves the list of axioms from the current working theory.  The list
  contains pairs of string names and the axioms.  This names exists such that
  compile time operations have a tag with which they can use to extract axioms 
  from saved theories.  See 'extractAxiom' for more details.
-}
axioms :: HOL cls thry (Map Text HOLThm)
axioms =        
    do acid <- openLocalStateHOL (TheAxioms mapEmpty)
       m <- queryHOL acid GetAxioms
       closeAcidStateHOL acid
       return m

{-| 
  Constructs a new axiom of a given name and conclusion term.  Also adds this
  new axiom to the current working theory.  Throws a 'HOLException' in the 
  following cases:

  * The provided term is not a proposition.

  * An axiom with the provided name has already been declared.
-}
newAxiom :: Text -> HOLTerm -> HOL Theory thry HOLThm
newAxiom name tm =
    do acid <- openLocalStateHOL (TheAxioms mapEmpty)
       qth <- queryHOL acid (GetAxiom' name)
       closeAcidStateHOL acid
       case qth of
         Just th -> 
             return th
         Nothing
             | typeOf tm /= tyBool -> 
                   fail "newAxiom: Not a proposition."
             | otherwise ->
                   let th = axiomThm tm in
                     do acid' <- openLocalStateHOL (TheAxioms mapEmpty)
                        updateHOL acid' (InsertAxiom name th)
                        createCheckpointAndCloseHOL acid'
                        return th
                   
 -- | Retrieves an axiom by label from the theory context.
getAxiom :: Text -> HOL cls thry HOLThm
getAxiom lbl =
    do acid <- openLocalStateHOL (TheAxioms mapEmpty)
       qth <- queryHOL acid (GetAxiom' lbl)
       closeAcidStateHOL acid
       liftMaybe ("getAxiom: axiom " ++ show lbl ++ " not found.") qth

-- State for Definitions
{-|
  Retrieves the list of definitions from the current working theory.  See
  'newBasicDefinition' for more details.
-}
definitions :: HOL cls thry [HOLThm]
definitions =
    do acid <- openLocalStateHOL (TheCoreDefinitions mapEmpty)
       m <- queryHOL acid GetCoreDefinitions
       closeAcidStateHOL acid
       return m

{-|
  Introduces a definition of the form @c = t@ into the current working theory.
  Throws a 'HOLException' when the definitional term is ill-formed.  See
  'newDefinedConst' for more details.
-}
newBasicDefinition :: Text -> HOLTerm -> HOL Theory thry HOLThm
newBasicDefinition lbl tm =
    getBasicDefinition lbl
    <|> case destEq tm of
          Just (Const _ _, _) ->
            fail "newBasicDefinition: constant already defined."
          Just (Var name _, _)
            | name /= lbl ->
                  fail $ "newBasicDefinition: provided label does not " ++
                         "match provided term."
            | otherwise ->
                  do (c@(Const x _), dth) <- liftEither "newBasicDefinition" $ 
                                               newDefinedConst tm
                     newConstant' x c
                     acid <- openLocalStateHOL (TheCoreDefinitions mapEmpty)
                     updateHOL acid (InsertCoreDefinition lbl dth)
                     createCheckpointAndCloseHOL acid
                     return dth
          _ -> fail "newBasicDefinition: provided term not an equation."
                    
-- | Retrieves a basic term definition by label from the theory context.
getBasicDefinition :: Text -> HOL cls thry HOLThm
getBasicDefinition lbl =
    do acid <- openLocalStateHOL (TheCoreDefinitions mapEmpty)
       qth <- queryHOL acid (GetCoreDefinition lbl)
       closeAcidStateHOL acid
       liftMaybe ("getBasicDefinition: definition for " ++ show lbl ++
                  " not found.") qth

{-|
  Introduces a new type constant, and two associated term constants, into the 
  current working theory that is defined as an inhabited subset of an existing 
  type constant.  Takes the following arguments:
  
  *  The name of the new type constant.

  *  The name of the new term constant that will be used to construct the type.

  *  The name of the new term constant that will be used to desctruct the type.

  *  A theorem that proves that the defining predicate has at least one
     satisfying value.

  Throws a 'HOLException' in the following cases:

  *  A term constant of either of the provided names has already been defined.

  *  A type constant of the provided name has already been defined.

  See 'newDefinedTypeOp' for more details.
-}
newBasicTypeDefinition :: Text -> Text -> Text -> HOLThm -> 
                          HOL Theory thry (HOLThm, HOLThm)
newBasicTypeDefinition tyname absname repname dth =
  do failWhen (return or <*> mapM (can getConstType) [absname, repname]) $
       "newBasicTypeDefinition: Constant(s) " ++ show absname ++ ", " ++ 
       show repname ++ " already in use."
     (atyop, a, r, dth1, dth2) <- liftEither "newBasicTypeDefinition" $
                                    newDefinedTypeOp tyname absname repname dth
     failWhen (canNot (newType' tyname) atyop) $
       "newBasicTypeDefinition: Type " ++ show tyname ++ " already defined."
     newConstant' absname a
     newConstant' repname r
     acid <- openLocalStateHOL (TypeDefinitions mapEmpty)
     updateHOL acid (InsertTypeDefinition tyname (dth1, dth2))
     createCheckpointAndCloseHOL acid
     return (dth1, dth2)

-- | Retrieves a basic type definition by label from the theory context.
getBasicTypeDefinition :: Text -> HOL cls thry (HOLThm, HOLThm)
getBasicTypeDefinition lbl =
    do acid <- openLocalStateHOL (TypeDefinitions mapEmpty)
       qth <- queryHOL acid (GetTypeDefinition lbl)
       closeAcidStateHOL acid
       liftMaybe ("getBasicTypeDefinition: definition for " ++ show lbl ++
                  " not found.") qth


-- Primitive Debugging Functions
{-| 
  Prints the provided string, with a new line, when the given boolean value is
  true.
-}
warn :: Bool -> String -> HOL cls thry ()
warn flag str = when flag $ putStrLnHOL str

{-|
  Prints the provided string, with a new line, when debugging is turned on, then
  returns the given 'HOL' computation.  A version of 'trace' for the 'HOL' monad
  that is referentially transparent.
-}
printDebugLn :: String -> HOL cls thry a -> HOL cls thry a
printDebugLn = printDebugBase putStrLnHOL

-- | A version of printDebug that does not print a new line.
printDebug :: String -> HOL cls thry a -> HOL cls thry a
printDebug = printDebugBase putStrHOL

-- Abstracted out for future flexibility.  Not exported.
printDebugBase :: (String -> HOL cls thry ()) -> String -> HOL cls thry a -> 
                  HOL cls thry a
printDebugBase fn str x =
    do debug <- getBenignFlag FlagDebug
       if debug
          then fn str >> x
          else x