haskhol-core-1.0.0: src/HaskHOL/Core/Ext.hs
{-# LANGUAGE TemplateHaskell #-}
{-|
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
, extendCtxt -- :: Typeable thry => HOLContext thry ->
-- HOL cls thry () -> Name -> String -> Q [Dec]
-- * 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
import HaskHOL.Core.Kernel hiding (typeOf)
import HaskHOL.Core.State
import HaskHOL.Core.Ext.Protected
import HaskHOL.Core.Ext.QQ
import Data.Char (toUpper, toLower)
import Data.Typeable (typeOf, typeRepArgs, TypeRep)
import Language.Haskell.TH (Q, Dec, Exp)
import Language.Haskell.TH.Quote (QuasiQuoter)
import Language.Haskell.TH.Syntax
{-|
Extends a theory by evaluating a provided computation, returning a list of
declarations containing:
* A new empty data declaration associated with the new theory.
* A new type class associated with the new theory to be used with
@DerivedCtxt@ along with the appropriate instances.
* The context value for the new theory.
* A class constraint alias that can be safely exported for use in type
signatures external to the library where it was defined.
* A quasiquoter for the new theory.
* A compile-time proof function for the new theory.
For example:
> extendCtxt ctxtBase loadBoolLib "bool"
will produce the following code
> data BoolThry deriving Typeable
> type BoolType = ExtThry BoolThry BaseThry
>
> class BaseCtxt a => BoolContext a
> instance BaseCtxt b => BoolContext (ExtThry BoolThry b)
> instance BoolContext b => BoolContext (ExtThry a b)
>
> class BoolContext a => BoolCtxt a
> instance BoolContext a => BoolCtxt a
>
> ctxtBool :: HOLContext BoolType
> ctxtBool = ...
>
> bool :: QuasiQuoter
> bool = baseQuoter ctxtBool
>
> proveBool :: String -> HOL Proof BoolType HOLThm -> Q [Dec]
> proveBool = proveCompileTime ctxtBool
>
> proveBoolMany :: [String] -> HOL Proof BoolType [HOLThm] -> Q [Dec]
> proveBoolMany = proveCompileTimeMany ctxtBool
-}
extendCtxt :: Typeable thry =>
HOLContext thry -> HOL cls thry () -> String -> Q [Dec]
extendCtxt ctx ld lbl =
-- lower case label for quasiquoter
let lowLbl = toLower (head lbl) : tail lbl
-- upper case label for everything else
upLbl = toUpper (head lbl) : tail lbl
-- type of old theory
oldThry = buildOldThry . head . typeRepArgs $ typeOf ctx
-- general use type variables
aName = mkName "a"
aVar = VarT aName
bVar = VarT $ mkName "b"
-- build data types
dataName = mkName $ upLbl ++ "Thry"
dataType = ConT dataName
dataDec = DataD [] dataName [] [] [''Typeable]
tyName = mkName $ upLbl ++ "Type"
newThry = extThry `AppT` dataType `AppT` oldThry
tyDec = TySynD tyName [] newThry
-- build class and instances
clsName = mkName $ upLbl ++ "Context"
oldThryName = let oldt = stripList (\ x -> case x of
AppT l r -> Just (l, r)
_ -> Nothing ) oldThry in
case oldt of
(ConT x:[]) -> show x
(_:ConT x:_) -> show x
_ -> error "extendCtxt: bad theory type."
-- ConT XThry ---> XCtxt
oldClsName = mkName $ take (length oldThryName - 4) oldThryName ++
"Ctxt"
clsCon = ConT clsName
clsDec = ClassD [ClassP oldClsName [aVar]] clsName [PlainTV aName] [] []
clsIn1Dec = InstanceD [ClassP oldClsName [bVar]]
(clsCon `AppT` (extThry `AppT` dataType `AppT` bVar)) []
clsIn2Dec = InstanceD [ClassP clsName [bVar]]
(clsCon `AppT` (extThry `AppT` aVar `AppT` bVar)) []
-- class wrapper
clsName' = mkName $ upLbl ++ "Ctxt"
clsCon' = ConT clsName'
clsDec' = ClassD [ClassP clsName [aVar]] clsName' [PlainTV aName] [] []
clsInDec' = InstanceD [ClassP clsName [aVar]] (clsCon' `AppT` aVar) []
-- build context type; we build value later
ctxtName = mkName $ "ctxt" ++ upLbl
ctxtTySig = SigD ctxtName $ ConT ''HOLContext `AppT` newThry
-- build QuasiQuoter
qqName = mkName lowLbl
qqTySig = SigD qqName $ ConT ''QuasiQuoter
qqDec = ValD (VarP qqName) (NormalB $
VarE 'baseQuoter `AppE` VarE ctxtName) []
-- build provers
-- Q [Dec]
qdecType = ConT ''Q `AppT` (ListT `AppT` ConT ''Dec)
cont b = if b then AppT ListT else id
name b = mkName $ "prove" ++ upLbl ++ if b then "Many" else ""
proveName b = if b then 'proveCompileTimeMany
else 'proveCompileTime
-- HOL Proof newThry HOLThm
holType b = ConT ''HOL `AppT` ConT ''Proof `AppT`
newThry `AppT` cont b (ConT ''HOLThm)
proverTySig b = SigD (name b) $
ArrowT `AppT` cont b (ConT ''String) `AppT`
(ArrowT `AppT` holType b `AppT` qdecType)
proveDec b = ValD (VarP $ name b) (NormalB $
VarE (proveName b) `AppE` VarE ctxtName) [] in
-- build values
do lctx <- lift =<< runIO (execHOLCtxt ld ctx)
let ctxtDec = ValD (VarP ctxtName) (NormalB lctx) []
return [ dataDec, tyDec -- types
, clsDec, clsIn1Dec, clsIn2Dec -- class and instances
, clsDec', clsInDec' -- class alias
, ctxtTySig, ctxtDec -- context value
, qqTySig, qqDec -- quasiquoter
, proverTySig False, proveDec False -- provers
, proverTySig True, proveDec True
]
where extThry :: Type
extThry = ConT ''ExtThry
buildOldThry :: TypeRep -> Type
buildOldThry ty =
case typeRepArgs ty of
[] -> ConT . mkName $ show ty
ts -> let ts' = map buildOldThry ts in
foldl1 (\ acc x -> extThry `AppT` acc `AppT` x) ts'
-- 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'.
-}