packages feed

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

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

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

  This module defines a mechanism for sealing and unsealing values against a
  given context.  Additionally, a number of compile time operations are
  provided that leverage this technique as an example of how it can be used.

  The basic goal behind the content of this module 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.
-}
module HaskHOL.Core.Ext.Protected
    ( Protected
    , protect
    , protectM
    , serve
    , unsafeServe
    , unsafeProtect
    , PType
    , PTerm
    , PThm
    , liftProtectedExp
    , liftProtected
    ) where

import HaskHOL.Core.Kernel
import HaskHOL.Core.State

import Language.Haskell.TH
import Language.Haskell.TH.Syntax (Lift(..))

-- protected values
{-|
  The Protected class is the associated type class that facilitates our
  protect/serve protection mechanism.

  It defines:

  * A data wrapper for our protected type.

  * Conversions to/from this new type, protect and serve.

  * Some boilerplate code to enable template haskell lifting.
-}
class Lift a => Protected a where
    -- | The associated type for the 'Protected' class.
    data PData a thry
    -- | Protects a value by sealing it against a provided context.
    protect :: TheoryPath thry -> a -> PData a thry
    -- | Protects a value by sealing it against the current context.
    protectM :: forall b cls thry. (b ~ a) => b -> HOL cls thry (PData b thry)
    protectM a = return $! protect (undefined :: TheoryPath thry) a
    {-| 
      Unseals a protected value, returning it in a monadic computation whose
      current working theory satisfies the context that the value was originally
      sealed with.
    -}
    serve :: PData a thry -> HOL cls thry a
    -- | Unseals a protected value, returning a pure, unprotected value.
    unsafeServe :: PData a thry -> a
    {- | An alias to the internal 'PData' constructor to create a fully 
         polymorphic \"protected\" value. -}
    unsafeProtect :: a -> PData a thry
    liftTy :: a -> Type
    protLift :: PData a thry -> Q Exp

instance Protected HOLThm where
    data PData HOLThm thry = PThm HOLThm
    protect _ = PThm
    serve (PThm thm) = return thm
    unsafeServe (PThm thm) = thm
    unsafeProtect = PThm
    liftTy _ = ConT ''HOLThm
    protLift (PThm thm) = conE 'PThm `appE` lift thm
-- | Type synonym for protected 'HOLThm's.
type PThm thry = PData HOLThm thry

instance Protected HOLTerm where
    data PData HOLTerm thry = PTm HOLTerm
    protect _ = PTm
    serve (PTm tm) = return tm
    unsafeServe (PTm tm) = tm
    unsafeProtect = PTm
    liftTy _ = ConT ''HOLTerm
    protLift (PTm tm) = conE 'PTm `appE` lift tm
-- | Type synonym for protected 'HOLTerm's.
type PTerm thry = PData HOLTerm thry

instance Protected HOLType where
    data PData HOLType thry = PTy HOLType
    protect _ = PTy
    serve (PTy ty) = return ty
    unsafeServe (PTy ty) = ty
    unsafeProtect = PTy
    liftTy _ = ConT ''HOLType
    protLift (PTy ty) = conE 'PTy `appE` lift ty
-- | Type synonym for protected 'HOLType's.
type PType thry = PData HOLType thry

instance Protected Int where
    data PData Int thry = PInt Int
    protect _ = PInt
    serve (PInt n) = return n
    unsafeServe (PInt n) = n
    unsafeProtect = PInt
    liftTy _ = ConT ''Int
    protLift (PInt n) = conE 'PInt `appE` lift n

instance Protected a => Protected [a] where
    data PData [a] thry = PList [PData a thry]
    protect c as = PList $ map (protect c) as
    serve (PList as) = mapM serve as
    unsafeServe (PList as) = map unsafeServe as
    unsafeProtect as = PList $ map unsafeProtect as
    liftTy _ = AppT ListT $ liftTy (undefined::a)
    protLift (PList as) = conE 'PList `appE` listE (map protLift as)

instance (Protected a, Protected b) => Protected (a, b) where
    data PData (a, b) thry = PPair (PData a thry) (PData b thry)
    protect c (a, b) = PPair (protect c a) (protect c b)
    serve (PPair a b) = do a' <- serve a
                           b' <- serve b
                           return (a', b')
    unsafeServe (PPair a b) = (unsafeServe a, unsafeServe b)
    unsafeProtect (a, b) = PPair (unsafeProtect a) (unsafeProtect b)
    liftTy _ = AppT (AppT (TupleT 2) (liftTy (undefined::a))) 
                 (liftTy (undefined::b))
    protLift (PPair a b) = conE 'PPair `appE` protLift a `appE` protLift b

instance (Protected a, Protected b, Protected c) => Protected (a, b, c) where
    data PData (a, b, c) thry = 
        PTrip (PData a thry) (PData b thry) (PData c thry)
    protect ctx (a, b, c) = 
        PTrip (protect ctx a) (protect ctx b) (protect ctx c)
    serve (PTrip a b c) = do a' <- serve a
                             b' <- serve b
                             c' <- serve c
                             return (a', b', c')
    unsafeServe (PTrip a b c) = (unsafeServe a, unsafeServe b, unsafeServe c)
    unsafeProtect (a, b, c) = 
        PTrip (unsafeProtect a) (unsafeProtect b) (unsafeProtect c)
    liftTy _ = AppT (AppT (AppT (TupleT 3) (liftTy (undefined::a)))
                          (liftTy (undefined::b)))
                 (liftTy (undefined::c))
    protLift (PTrip a b c) = 
        conE 'PTrip `appE` protLift a `appE` protLift b `appE` protLift c

{-
  Builds the theory contrainst for a lifted, protected value.  
  For example:
  
  > buildThryType (x::PData a BoolType)

  builds the context

  > forall thry. BoolCtxt thry => PData a thry
-}
buildThryType :: forall a thry. (Protected a, CtxtName thry) => 
                                PData a thry -> Q Type
buildThryType _ =
    do tyname <- newName "thry"
       let cls = ClassP (mkName $ ctxtName (undefined::thry)) [VarT tyname]
       return . ForallT [PlainTV tyname] [cls] . 
         AppT (AppT (ConT ''PData) $ liftTy (undefined :: a)) $ 
           VarT tyname
         

{-| 
  Lifts a protected data value as an expression using an ascribed type.
  For example:

  > liftProtectedExp (x::PData a Bool)

  produces the following spliceable expression

  > [| x :: forall thry. BoolCtxt thry => PData a Bool |]
-}
liftProtectedExp :: (Protected a, CtxtName thry) => PData a thry -> Q Exp
liftProtectedExp pdata =
  do pdata' <- protLift pdata
     ty <- buildThryType pdata
     return $! SigE pdata' ty

{-| 
  Lifts a protected data value as a declaration of a given name with an ascribed
  type signature.
  For example:

  > liftProtected "protX" (x::PData a Bool)

  produces the following list of spliceable declarations

  > [ [d| protX :: forall thry. BoolCtxt thry => PData a Bool |]
  > , [d| protX = x |] ]

  See 'extractAxiom' for a basic example of how this function may be used.
-}
liftProtected :: (Protected a, CtxtName thry) => 
                 String -> PData a thry -> Q [Dec]
liftProtected lbl pdata =
  do pdata' <- protLift pdata
     ty <- buildThryType pdata
     let name = mkName lbl
         tysig = SigD name ty
         dec = ValD (VarP name) (NormalB pdata') []
     return [tysig, dec]