packages feed

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

{-# LANGUAGE ScopedTypeVariables #-}
{-|
  Module:    HaskHOL.Core.Ext
  Copyright: (c) The University of Kansas 2013
  LICENSE:   BSD3

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

  This module exports HaskHOL's non-trivial extensions to the underlying HOL
  system, i.e. the compile time operations.  These operations are split into
  three categories:

  * Methods related to the Protect and Serve Mechanism for sealing and unsealing
    data against a provided theory context.

  * Methods related to quasi-quoting of 'HOLTerm's.  

  * Methods related to compile time extension and caching of theory contexts.
-}
module HaskHOL.Core.Ext
    ( -- * Protected Data Methods
       -- $Protect
      module HaskHOL.Core.Ext.Protected
      -- * Quasi-Quoter Methods
       -- $QQ
    , module HaskHOL.Core.Ext.QQ
      -- * Theory Extension Methods
    , templateProvers
    , extendTheory
      -- * Template Haskell Re-Exports
    , module Language.Haskell.TH {-|
        Re-exports 'Q', 'Dec', and 'Exp' for the purpose of writing type
        signatures external to this module.
      -}
    , module Language.Haskell.TH.Quote {-|
        Re-exports 'QuasiQuoter' for the purpose of writing type signatures
        external to this module.
      -}
    ) where

import HaskHOL.Core.Lib hiding (combine)
import HaskHOL.Core.State.Monad

import HaskHOL.Core.Ext.Protected
import HaskHOL.Core.Ext.QQ

import Data.Char (toUpper, toLower)

import Language.Haskell.TH (Q, Dec, Exp)
import Language.Haskell.TH.Quote (QuasiQuoter)
import Language.Haskell.TH.Syntax  

import Prelude hiding (FilePath)
import Paths_haskhol_core
import Shelly
import System.FilePath (combine)

{-|
  The 'templateTypes' splice automatically generates the types and type 
  families associated with a theory extension.  It constructs the following:

  * A new empty data declaration associated with the new theory.

  * A type synonym for the application of the new data type.

  * A closed type family for constraining computations to be used only with
    contexts containing this checkpoint.
  
  The first argument is the context to be extended and the second argument is
  the name of the new theory checkpoint.
  For example,

  > templateTypes ctxtBase "Bool"

  will produce the following declarations:

  > data BoolThry
  > instance CtxtName BoolThry where
  >     ctxtName _ = "BoolCtxt"
  > type BoolType = ExtThry BoolThry BaseThry
  >
  > type family BoolContext a :: Bool where
  >     BoolContext BaseThry = False
  >     BoolContext (ExtThry a b) = (a == BoolThry) || (BoolContext b)
  >
  > type instance BoolThry == BoolThry = True

  Regrettably, Template Haskell cannot currently handle splicing type 
  equalities.  Thus, to provide a cleaner interface, it is recommended to
  manually construct another type family to be used as the main 'Constraint':

  > type family BoolCtxt a :: Constraint where
  >     BoolCtxt a = (BaseCtxt a, BoolContext a ~ True)

  Note that this new 'Constraint' can also be used to enforce the linearity of
  type theory contexts.
-}
templateTypes :: forall thry. CtxtName thry => TheoryPath thry -> String 
              -> Q [Dec]
templateTypes _ lbl =
        -- upper case label for everything else
    let upLbl = toUpper (head lbl) : tail lbl
        -- type of old theory
        oldThry = ConT . mkName $
                    let oldCtxt = ctxtName (undefined::thry)
                        oldType = take (length oldCtxt - 4) oldCtxt in
                      if oldType == "Base" then "BaseThry"
                      else oldType ++ "Type"
        -- general use type variables
        aName = mkName "a"
        aVar = VarT aName
        bVar = VarT $ mkName "b"
-- build data types
        dataName = mkName $ upLbl ++ "Thry"
        dataType = ConT dataName
        -- splices: instance CtxtName _Thry where ...
        instDec = InstanceD [] (AppT (ConT ''CtxtName) (ConT dataName))
                    [FunD 'ctxtName [Clause [WildP] 
                      (NormalB (LitE (StringL $ upLbl ++ "Ctxt"))) []]]
        dataDec = DataD [] dataName [] [] []
        tyName = mkName $ upLbl ++ "Type"
        newThry = ConT ''ExtThry `AppT` dataType `AppT` oldThry
        -- splices: type _Type = ExtThry _Thry oldThry
        tyDec = TySynD tyName [] newThry
-- build class and instances
        clsName = mkName $ upLbl ++ "Context"
        -- splices: type instance _Context BaseThry = False
        famBase =TySynEqn [ConT ''BaseThry] $ PromotedT 'False
        famCond = AppT (AppT (ConT ''(||))
                        (AppT (ConT clsName) bVar))
                   (AppT (AppT (ConT ''(==)) aVar) (ConT dataName))     
        -- splices: type instance _Context (ExtThry a b) = 
        --              (a == _Thry) || (_Context b)     
        famInd = TySynEqn [AppT (AppT (ConT ''ExtThry) aVar) bVar] famCond
        -- splices: type family _Context a :: Bool
        fam = ClosedTypeFamilyD clsName [PlainTV aName] (Just $ ConT ''Bool)
                [famBase, famInd]
-- class wrapper
        -- splices: type instance _Thry == _Thry = True
        clsEq = TySynInstD ''(==) . 
                  TySynEqn [ConT dataName, ConT dataName] $ PromotedT 'True in
          return [ dataDec, tyDec, instDec               -- types
                 , fam
                 , clsEq
                 ]

{-|
  The 'templateProvers' splice automatically generates the 'QuasiQuoter'
  associated with a theory extension.  The provided
  name should be the theory context checkpoint built with the 'extendTheory' 
  splice.

  For example,

  > templateProvers 'ctxtBool

  will produce the following declarations:

  > bool :: QuasiQuoter
  > bool = baseQuoter ctxtBool
-}
templateProvers :: Name -> Q [Dec]
templateProvers ctxName =
-- build QuasiQuoter
    let upLbl = drop 4 $ nameBase ctxName
        lowLbl = toLower (head upLbl) : tail upLbl
        --tyName = mkName $ upLbl ++ "Type"
        qqName = mkName lowLbl
        -- splices: _ :: QuasiQuoter
        qqTySig = SigD qqName $ ConT ''QuasiQuoter
        -- splices: _ = baseQuoter ctxt_
        qqDec = ValD (VarP qqName) (NormalB $ 
                  VarE 'baseQuoter `AppE` VarE ctxName) [] in
      return [ qqTySig, qqDec ]

-- Documentation copied from sub-modules

{-$Protect
  The basic goal behind the Protect and Serve mechanism is to recapture some of
  the efficiency lost as a result of moving from an impure, interpretted host 
  language to a pure, compiled one.  We do this by forcing the evaluation of 
  large computations, usually proofs, such that they are only run once. To
  maintain soundness of our proof system, we must track what information
  was used to force the computation and guarantee that information is present
  in all cases where this new value is to be used.  This is the purpose of the
  @Protected@ class and the 'liftProtectedExp' and 'liftProtected' methods.
-}

{-$QQ
  Quasi-quoting provides a way to parse 'HOLTerm's at compile time safely.
  Just as with proofs, we seal these terms against the theory context used to
  parse them with 'protect' and 'serve' to preserve soundness.  See the
  documentation for 'base' for a brief discussion on when quasi-quoting should
  be used vs. 'toHTm'.
-}

{-|
  The 'extendTheory' splice acts as a compile-time wrapper to 'runHOL', used for
  the purpose of creating new theory contexts.  It takes three arguments:

  * The theory to be extended.

  * The base name of the new theory to be created.

  * The 'HOL' computation that will perform the extension.

  For example:

  > extendTheory ctxtBase "Bool" $ ...

  Additionally, 'extendTheory' calls the 'templateTypes' splice to create the
  type class wizardy associated with theory contexts and creates an
  appropriately typed wrapper for 'mkTheoryPath', e.g.:

  > ctxtBool :: TheoryPath BoolType
  > ctxtBool = mkTheoryPath
-}
extendTheory :: CtxtName thry => TheoryPath thry -> String -> HOL Theory thry ()
             -> Q [Dec]
extendTheory old new ld =
       -- run the load function
    do runIO $
         do dir <- getDataDir
            let new' = dir `combine` (new ++ "Ctxt")
            shelly $ 
              do cond <- test_d . fromText $ pack new'
                 when cond . echo . pack $
                   "Note:  Using existing theory context for " ++ new
                 unless cond . liftIO $
                   runHOL (ld >> checkpointProofs) old new'
       -- splices: ctxt_ :: TheoryPath _Type
       --          ctxt_ = mkTheoryPath
       let cname = mkName $ "ctxt" ++ new
           ctype = ConT . mkName $ new ++ "Type"
           tySig = SigD cname (ConT ''TheoryPath `AppT` ctype)
           def = ValD (VarP cname) (NormalB (VarE 'mkTheoryPath)) []
       tyDefs <- templateTypes old new
       return $! tyDefs ++ [tySig, def]