haskhol-core (empty) → 1.0.0
raw patch · 26 files changed
+8836/−0 lines, 26 filesdep +basedep +containersdep +deepseqsetup-changed
Dependencies added: base, containers, deepseq, parsec, pretty, template-haskell
Files
- LICENSE +24/−0
- Setup.hs +2/−0
- haskhol-core.cabal +59/−0
- src/HaskHOL/Core.hs +130/−0
- src/HaskHOL/Core/Basics.hs +1005/−0
- src/HaskHOL/Core/Basics.hs-boot +8/−0
- src/HaskHOL/Core/Basics/Nets.hs +196/−0
- src/HaskHOL/Core/Ext.hs +212/−0
- src/HaskHOL/Core/Ext/Protected.hs +257/−0
- src/HaskHOL/Core/Ext/QQ.hs +93/−0
- src/HaskHOL/Core/Kernel.hs +549/−0
- src/HaskHOL/Core/Kernel/Prims.hs +324/−0
- src/HaskHOL/Core/Kernel/Terms.hs +631/−0
- src/HaskHOL/Core/Kernel/Types.hs +643/−0
- src/HaskHOL/Core/Lib.hs +1342/−0
- src/HaskHOL/Core/Lib/Lift.hs +158/−0
- src/HaskHOL/Core/Parser.hs +92/−0
- src/HaskHOL/Core/Parser.hs-boot +9/−0
- src/HaskHOL/Core/Parser/Elab.hs +564/−0
- src/HaskHOL/Core/Parser/Lib.hs +526/−0
- src/HaskHOL/Core/Parser/Rep.hs +81/−0
- src/HaskHOL/Core/Parser/TermParser.hs +225/−0
- src/HaskHOL/Core/Parser/TypeParser.hs +176/−0
- src/HaskHOL/Core/Printer.hs +467/−0
- src/HaskHOL/Core/State.hs +390/−0
- src/HaskHOL/Core/State/Monad.hs +673/−0
+ LICENSE view
@@ -0,0 +1,24 @@+Copyright (c) 2013, University of Kansas+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright notice, this+ list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above copyright notice,+ this list of conditions and the following disclaimer in the documentation+ and/or other materials provided with the distribution.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ haskhol-core.cabal view
@@ -0,0 +1,59 @@+name: haskhol-core+version: 1.0.0+synopsis: The core logical system of HaskHOL, an EDSL for HOL theorem + proving. +description: More details can be found at the following page: + <haskhol.org>. +license: BSD3+license-file: LICENSE+author: Evan Austin <ecaustin@ittc.ku.edu>+maintainer: Evan Austin <ecaustin@ittc.ku.edu>+category: Theorem Provers+cabal-version: >=1.6+build-type: Simple+stability: experimental+Homepage: haskhol.org+ +library+ build-depends: base >=4.5 && <5+ , template-haskell >=2.7 && <3 + , parsec >=3.1 && <4+ , deepseq >=1.3 && <2+ , containers >=0.5 && <1+ , pretty >=1.1 && <2++ exposed-modules:+ HaskHOL.Core+ HaskHOL.Core.Basics+ HaskHOL.Core.Lib + HaskHOL.Core.Lib.Lift+ HaskHOL.Core.Kernel + HaskHOL.Core.Kernel.Terms+ HaskHOL.Core.Kernel.Types + HaskHOL.Core.State + HaskHOL.Core.State.Monad+ HaskHOL.Core.Parser + HaskHOL.Core.Printer+ HaskHOL.Core.Ext ++ exposed: True+ buildable: True+ hs-source-dirs: src++ other-modules:+ HaskHOL.Core.Basics.Nets+ HaskHOL.Core.Ext.Protected + HaskHOL.Core.Ext.QQ+ HaskHOL.Core.Kernel.Prims+ HaskHOL.Core.Parser.Elab+ HaskHOL.Core.Parser.Lib + HaskHOL.Core.Parser.Rep + HaskHOL.Core.Parser.TermParser + HaskHOL.Core.Parser.TypeParser + + ghc-prof-options: -prof -fprof-auto+ ghc-options: -Wall++source-repository head+ type: git + location: git://github.com/ecaustin/haskhol-core.git
+ src/HaskHOL/Core.hs view
@@ -0,0 +1,130 @@+{-|+ Module: HaskHOL.Core+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module is the one to import for users looking to include the entirety of+ the core of the HaskHOL proof system. It re-exports all of the core + sub-modules in addition to a number of overloaded functions that work with+ 'HOLTermRep' and 'HOLTypeRep' representations for convenience reasons.+-}+module HaskHOL.Core+ ( -- * 'HOLTermRep' and 'HOLTypeRep' Overloads+ newConstant -- :: HOLTypeRep ty thry => + -- String -> ty -> HOL Theory thry ()+ , newAxiom -- :: HOLTermRep tm thry => + -- String -> tm -> HOL Theory thry HOLThm+ , newBasicDefinition -- :: HOLTermRep tm thry => + -- tm -> HOL Theory thry HOLThm+ , makeOverloadable -- :: HOLTypeRep ty thry => + -- String -> ty -> HOL Theory thry ()+ , reduceInterface -- :: HOLTermRep tm thry => + -- String -> tm -> HOL Theory thry ()+ , overrideInterface -- :: HOLTermRep tm thry => + -- String -> tm -> HOL Theory thry ()+ , overloadInterface -- :: HOLTermRep tm thry => + -- String -> tm -> HOL Theory thry ()+ , prioritizeOverload -- :: HOLTypeRep ty thry => ty -> HOL Theory thry ()+ , newTypeAbbrev -- :: HOLTypeRep ty thry => + -- String -> ty -> HOL Theory thry ()+ -- * Library and Utility Functions+ , module HaskHOL.Core.Lib+ -- * Logical Kernel+ , module HaskHOL.Core.Kernel+ -- * Stateful Primitives+ , module HaskHOL.Core.State+ -- * Basic Derived Type and Term Functions+ , module HaskHOL.Core.Basics+ -- * HaskHOL Parsers+ , module HaskHOL.Core.Parser+ -- * HaskHOL Pretty Printers+ , module HaskHOL.Core.Printer+ -- * HaskHOL Core Extensions+ , module HaskHOL.Core.Ext+ ) where++import HaskHOL.Core.Lib+import HaskHOL.Core.Kernel+import HaskHOL.Core.State hiding ( newConstant, newAxiom, newBasicDefinition )+import HaskHOL.Core.Basics+import HaskHOL.Core.Parser hiding ( makeOverloadable, reduceInterface + , overrideInterface, overloadInterface+ , prioritizeOverload, newTypeAbbrev )+import HaskHOL.Core.Printer+import HaskHOL.Core.Ext++import qualified HaskHOL.Core.State as S ( newConstant, newAxiom+ , newBasicDefinition )+import qualified HaskHOL.Core.Parser as P ( makeOverloadable, reduceInterface + , overrideInterface, overloadInterface+ , prioritizeOverload, newTypeAbbrev )+-- from state+{-| + A redefinition of 'S.newConstant' to overload it for all valid term+ representations as defined by 'HOLTermRep'.+-}+newConstant :: HOLTypeRep ty thry => String -> ty -> HOL Theory thry ()+newConstant s = S.newConstant s <=< toHTy++{-| + A redefinition of 'S.newAxiom' to overload it for all valid term+ representations as defined by 'HOLTermRep'.+-}+newAxiom :: HOLTermRep tm thry => String -> tm -> HOL Theory thry HOLThm+newAxiom s = S.newAxiom s <=< toHTm++{-| + A redefinition of 'S.newBasicDefinition' to overload it for all valid term+ representations as defined by 'HOLTermRep'.+-}+newBasicDefinition :: HOLTermRep tm thry => tm -> HOL Theory thry HOLThm+newBasicDefinition = S.newBasicDefinition <=< toHTm+++-- from parser+{-|+ A redefinition of 'P.makeOverloadable' to overload it for all valid type+ representations as defined by 'HOLTypeRep'.+-}+makeOverloadable :: HOLTypeRep ty thry => String -> ty -> HOL Theory thry ()+makeOverloadable s = P.makeOverloadable s <=< toHTy++{-|+ A redefinition of 'P.reduceInterface' to overload it for all valid term+ representations as defined by 'HOLTermRep'.+-}+reduceInterface :: HOLTermRep tm thry => String -> tm -> HOL Theory thry ()+reduceInterface s = P.reduceInterface s <=< toHTm++{-|+ A redefinition of 'P.overrideInterface' to overload it for all valid term+ representations as defined by 'HOLTermRep'.+-}+overrideInterface :: HOLTermRep tm thry => + String -> tm -> HOL Theory thry ()+overrideInterface s = P.overrideInterface s <=< toHTm++{-|+ A redefinition of 'P.overloadInterface' to overload it for all valid term+ representations as defined by 'HOLTermRep'.+-}+overloadInterface :: HOLTermRep tm thry => String -> tm -> HOL Theory thry ()+overloadInterface s = P.overloadInterface s <=< toHTm++{-|+ A redefinition of 'P.prioritizeOverload' to overload it for all valid type+ representations as defined by 'HOLTypeRep'.+-}+prioritizeOverload :: HOLTypeRep ty thry => ty -> HOL Theory thry ()+prioritizeOverload = P.prioritizeOverload <=< toHTy++{-|+ A redefinition of 'P.newTypeAbbrev' to overload it for all valid type+ representations as defined by 'HOLTypeRep'.+-}+newTypeAbbrev :: HOLTypeRep ty thry => String -> ty -> HOL Theory thry ()+newTypeAbbrev s = P.newTypeAbbrev s <=< toHTy
+ src/HaskHOL/Core/Basics.hs view
@@ -0,0 +1,1005 @@+{-# LANGUAGE ViewPatterns #-}++{-|+ Module: HaskHOL.Core.Basics+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module defines common utility functions that depend on data types+ introduced by HaskHOL. See the "HaskHOL.Core.Lib" module for utility functions+ that do not have this dependence.+-}+module HaskHOL.Core.Basics+ ( -- * Variable Term Generation+ genVarWithName -- :: String -> HOLType -> HOL cls thry HOLTerm+ , genVar -- :: HOLType -> HOL cls thry HOLTerm+ -- * Common Type Functions+ , occursIn -- :: HOLType -> HOLType -> Bool+ , tysubst -- :: HOLTypeEnv -> HOLType -> Either String HOLType+ , alphaUtype -- :: HOLType -> HOLType -> Either String HOLType+ -- * Common Term Functions+ , freeIn -- :: HOLTerm -> HOLTerm -> Bool+ , subst -- :: HOLTermEnv -> HOLTerm -> HOL cls thry HOLTerm+ , alpha -- :: HOLTerm -> HOLTerm -> Either String HOLTerm+ , alphaTyabs -- :: HOLType -> HOLTerm -> Either String HOLTerm+ , findTerm -- :: (HOLTerm -> Bool) -> HOLTerm -> Maybe HOLTerm+ , findTerms -- :: (HOLTerm -> Bool) -> HOLTerm -> [HOLTerm]+ , findPath -- :: (HOLTerm -> Bool) -> HOLTerm -> Maybe String+ , followPath -- :: String -> HOLTerm -> Maybe HOLTerm+ -- * Common Theorem Functions+ , typeVarsInThm -- :: HOLThm -> [HOLType]+ , thmFrees -- :: HOLThm -> [HOLTerm]+ -- * Derived Destructors and Constructors for Basic Terms+ , listMkComb -- :: HOLTerm -> [HOLTerm] -> Either String HOLTerm+ , listMkAbs -- :: [HOLTerm] -> HOLTerm -> Either String HOLTerm+ , mkArgs -- :: String -> [HOLTerm] -> [HOLType] -> [HOLTerm]+ , rator -- :: HOLTerm -> Maybe HOLTerm+ , rand -- :: HOLTerm -> Maybe HOLTerm+ , bndvar -- :: HOLTerm -> Maybe HOLTerm+ , body -- :: HOLTerm -> Maybe HOLTerm+ , bndvarTyabs -- :: HOLTerm -> Maybe HOLType+ , bodyTyabs -- :: HOLTerm -> Maybe HOLTerm+ , stripComb -- :: HOLTerm -> (HOLTerm, [HOLTerm])+ , stripAbs -- :: HOLTerm -> ([HOLTerm], HOLTerm)+ -- * Type Matching Functions+ , typeMatch -- :: HOLType -> HOLType -> SubstTrip -> Maybe SubstTrip+ , mkMConst -- :: String -> HOLType -> HOL cls thry HOLTerm+ , mkIComb -- :: HOLTerm -> HOLTerm -> Maybe HOLTerm+ , listMkIComb -- :: String -> [HOLTerm] -> HOL cls thry HOLTerm+ -- * Predicates, Constructors, and Destructors for Binary Terms+ , isBinary -- :: String -> HOLTerm -> Bool+ , isBinop -- :: HOLTerm -> HOLTerm -> Bool+ , destBinary -- :: String -> HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , destBinop -- :: HOLTerm -> HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , mkBinary -- :: String -> HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+ , mkBinop -- :: HOLTerm -> HOLTerm -> HOLTerm -> Either String HOLTerm+ , listMkBinop -- :: HOLTerm -> [HOLTerm] -> Either String HOLTerm+ , binops -- :: HOLTerm -> HOLTerm -> [HOLTerm]+ -- * Predicates, Constructors, and Destructors for Complex Abstractions+ , isGAbs -- :: HOLTerm -> Bool+ , isBinder -- :: String -> HOLTerm -> Bool+ , isTyBinder -- :: String -> HOLTerm -> Bool+ , destGAbs -- :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , destBinder -- :: String -> HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , destTyBinder -- :: String -> HOLTerm -> Maybe (HOLType, HOLTerm)+ , mkGAbs -- :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+ , mkBinder -- :: String -> HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+ , mkTyBinder -- :: String -> HOLType -> HOLTerm -> HOL cls thry HOLTerm+ , listMkGAbs -- :: [HOLTerm] -> HOLTerm -> HOL cls thry HOLTerm+ , stripGAbs -- :: HOLTerm -> ([HOLTerm], HOLTerm)+ -- * Predicates, Constructors, and Destructors for Propositions+ , isConj -- :: HOLTerm -> Bool+ , isImp -- :: HOLTerm -> Bool+ , isForall -- :: HOLTerm -> Bool+ , isExists -- :: HOLTerm -> Bool+ , isDisj -- :: HOLTerm -> Bool+ , isNeg -- :: HOLTerm -> Bool+ , isUExists -- :: HOLTerm -> Bool+ , isTyAll -- :: HOLTerm -> Bool+ , isTyEx -- :: HOLTerm -> Bool+ , destConj -- :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , destImp -- :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , destForall -- :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , destExists -- :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , destDisj -- :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , destNeg -- :: HOLTerm -> Maybe HOLTerm+ , destUExists -- :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , destTyAll -- :: HOLTerm -> Maybe (HOLType, HOLTerm)+ , destTyEx -- :: HOLTerm -> Maybe (HOLType, HOLTerm)+ , mkConj -- :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+ , mkImp -- :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+ , mkForall -- :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+ , mkExists -- :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+ , mkDisj -- :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+ , mkNeg -- :: HOLTerm -> HOL cls thry HOLTerm+ , mkUExists -- :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+ , mkTyAll -- :: HOLType -> HOLTerm -> HOL cls thry HOLTerm+ , mkTyEx -- :: HOLType -> HOLTerm -> HOL cls thry HOLTerm+ , listMkConj -- :: [HOLTerm] -> HOL cls thry HOLTerm+ , listMkDisj -- :: [HOLTerm] -> HOL cls thry HOLTerm+ , listMkForall -- :: [HOLTerm] -> HOLTerm -> HOL cls thry HOLTerm+ , listMkExists -- :: [HOLTerm] -> HOLTerm -> HOL cls thry HOLTerm+ , conjuncts -- :: HOLTerm -> [HOLTerm]+ , disjuncts -- :: HOLTerm -> [HOLTerm]+ , stripForall -- :: HOLTerm -> ([HOLTerm], HOLTerm)+ , stripExists -- :: HOLTerm -> ([HOLTerm], HOLTerm)+ , stripTyAll -- :: HOLTerm -> ([HOLType], HOLTerm)+ , stripTyEx -- :: HOLTerm -> ([HOLType], HOLTerm)+ -- * Predicates, Constructors, and Destructors for Other Terms+ , isCons -- :: HOLTerm -> Bool+ , isList -- :: HOLTerm -> Bool+ , isLet -- :: HOLTerm -> Bool+ , destCons -- :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , destList -- :: HOLTerm -> Maybe [HOLTerm]+ , destLet -- :: HOLTerm -> Maybe ([(HOLTerm, HOLTerm)], HOLTerm)+ , destNumeral -- :: HOLTerm -> Maybe Integer+ -- * Term Nets+ , module HaskHOL.Core.Basics.Nets+ ) where++import HaskHOL.Core.Lib+import HaskHOL.Core.Kernel+import HaskHOL.Core.State+import HaskHOL.Core.Basics.Nets++-- Term Generation+{-| + Generates a new term variable consisting of a given prefix and the next value+ in the fresh term counter.+-}+genVarWithName :: String -> HOLType -> HOL cls thry HOLTerm+genVarWithName n ty =+ do count <- tickTermCounter+ return $! mkVar (n ++ show count) ty++-- | A version of 'genVarWithName' that defaults to the prefix \"_\".+genVar :: HOLType -> HOL cls thry HOLTerm+genVar = genVarWithName "_"++-- functions for manipulating types+{-| + Checks to see if the first type occurs in the second type. Note that the+ predicate is also satisfied if the two types are equal.+-}+occursIn :: HOLType -> HOLType -> Bool+occursIn ty bigTy+ | ty == bigTy = True+ | otherwise = case view bigTy of+ TyApp _ args -> any (occursIn ty) args+ _ -> False+++{-| + Basic type substitution that ignores type operators and prunes the + substitution environment of bound variables rather than handle renaming.+ Works for all types, variable and non-variable alike. Fails with 'Left' when + the substitution would result in an invalid type construction.++ Note that the order of the elements of the substitution pairs matches other + environments in the systems, such that for the pair @(A, B)@ @B@ will be + substituted for all instances of @A@.+-}+tysubst :: HOLTypeEnv -> HOLType -> Either String HOLType+tysubst env ty =+ note "tysubst" (lookup ty env) + <|> case view ty of+ TyVar {} -> return ty+ TyApp tycon tyvars -> + (tyApp tycon =<< mapM (tysubst env) tyvars) <?>+ "tysubst: bad type application"+ UType bv bod -> + (mkUType bv =<< tysubst (filter (\ (x, _) -> x /= bv) env) bod) <?>+ "tysubst: bad universal type"++{-|+ Alpha conversion for universal types. Renames a bound type variable to match+ the name of a provided type variable. Fails with 'Left' in the following+ cases:++ * First type is not a small type variable.++ * Second type is not a universal type.++ * The type variable is free in the body of the universal type.+-}+alphaUtype :: HOLType -> HOLType -> Either String HOLType+alphaUtype tv@(view -> TyVar True _) ty@(view -> UType tv0 bod)+ | tv == tv0 = Right ty+ | tv `elem` tyVars bod = Left "alphaUtype: variable free in body of type."+ | otherwise = mkUType tv (typeSubst [(tv0, tv)] bod) <?>+ "alphaUtype: construction of universal type failed."+alphaUtype _ (view -> UType{}) = + Left "alphaUtype: first type not a small type variable."+alphaUtype _ _ = Left "alphaUtype: second type not a universal type."++-- functions for manipulating terms+{-| + Predicate to check if the first term is free in the second modulo+ alpha-equivalence.+-}+freeIn :: HOLTerm -> HOLTerm -> Bool+freeIn tm1 tm2 =+ (tm1 `aConv` tm2) ||+ (case view tm2 of+ Comb l r -> freeIn tm1 l || freeIn tm1 r+ Abs bv bod -> not (varFreeIn bv tm1) && freeIn tm1 bod+ TyAbs bv bod -> bv `elem` typeVarsInTerm tm1 && freeIn tm1 bod+ TyComb tm _ -> freeIn tm1 tm+ _ -> False)++{-| + Basic term substitution. Throws a 'HOLException' when the substitution would + result in an invalid term construction.++ Note that the order of the elements of the substitution pairs matches other + environments in the systems, such that for the pair @(A, B)@ @B@ will be + substituted for all instances of @A@. +-}+subst :: HOLTermEnv -> HOLTerm -> HOL cls thry HOLTerm+subst ilist tm =+ let (xs, ts) = unzip ilist in+ do gs <- mapM (genVar . typeOf) xs+ tm' <- liftEither "subst" $ ssubst (zip xs gs) tm+ if tm' == tm+ then return tm+ else return $! varSubst (zip gs ts) tm'+ where ssubst :: HOLTermEnv -> HOLTerm -> Either String HOLTerm+ ssubst [] t = Right t+ ssubst env t = + case find (\ (t', _) -> t `aConv` t') env of+ Just (_, res) -> Right res+ Nothing ->+ case view t of+ Comb f x -> + liftM1 mkComb (ssubst env f) =<< ssubst env x+ Abs bv bod -> + mkAbs bv =<< + ssubst (filter (not . varFreeIn bv . fst) env) bod+ TyAbs ty bod ->+ mkTyAbs ty =<< + ssubst (filter (\ (_, x) -> ty `notElem` + typeVarsInTerm x) env) bod+ TyComb bod ty ->+ liftM1 mkTyComb (ssubst env bod) ty+ _ -> Right t++{-|+ Alpha conversion for term abstractions. Renames a bound variable to match+ the name of a provided variable. Fails with 'Left' in the following cases:++ * First term is not a variable.++ * Second term is not an abstraction.++ * The types of the variable and bound variable do no agree.++ * The variable is free in the body of the abstraction.+-}+alpha :: HOLTerm -> HOLTerm -> Either String HOLTerm+alpha v@(view -> Var _ ty) tm@(view -> Abs v0@(view -> Var _ ty0) bod)+ | v == v0 = Right tm+ | ty /= ty0 = Left "alpha: types of variables not equal."+ | v `varFreeIn` bod = Left "alpha: variable free in body of abstraction."+ | otherwise = mkAbs v (varSubst [(v0, v)] bod) <?> + "alpha: construction of abstraction failed."+alpha _ (view -> Abs{}) = Left "alpha: first term not a variable."+alpha _ _ = Left "alpha: second term not an abstraction."++{-|+ Alpha conversion for type abstractions. Renames a bound type variable to+ match the name of a provided type variable. Fails with 'Left' in the + following cases:++ * The provided type is not a small type variable.++ * The provided term is not a type abstraction.++ * The type is free in the body of the type abstraction.+-}+alphaTyabs :: HOLType -> HOLTerm -> Either String HOLTerm+alphaTyabs ty@(view -> TyVar True _) tm@(view -> TyAbs ty0 bod)+ | ty == ty0 = Right tm+ | ty `elem` typeVarsInTerm bod = + Left "alphaTyabs: type free in body of type abstraction."+ | otherwise = mkTyAbs ty $ inst [(ty0, ty)] bod+alphaTyabs _ (view -> TyAbs{}) = + Left "alphaTyabs: type not a small type variable."+alphaTyabs _ _ = + Left "alphaTyabs: term not a type abstraction."++-- searching for terms+{-| + Searches a term for a subterm that satisfies a given predicate. Fails with+ 'Nothing' if no such term is found.+-}+findTerm :: (HOLTerm -> Bool) -> HOLTerm -> Maybe HOLTerm+findTerm p tm+ | p tm = Just tm+ | otherwise =+ case view tm of+ Abs _ bod -> findTerm p bod+ Comb l r -> findTerm p l <|> findTerm p r+ TyAbs _ bod -> findTerm p bod+ TyComb tm' _ -> findTerm p tm'+ _ -> Nothing++-- | Searches a term for all unique subterms that satisfy a given predicate.+findTerms :: (HOLTerm -> Bool) -> HOLTerm -> [HOLTerm]+findTerms = findRec []+ where findRec :: [HOLTerm] -> (HOLTerm -> Bool) -> HOLTerm -> [HOLTerm]+ findRec tl p tm =+ let tl' = if p tm then insert tm tl else tl in+ case view tm of+ Abs _ bod -> findRec tl' p bod+ Comb l r -> findRec (findRec tl' p l) p r+ TyAbs _ bod -> findRec tl' p bod+ TyComb tm' _ -> findRec tl' p tm'+ _ -> tl'++-- Director strings down a term+{-|+ Searches a term for a subterm that satisfies a given predicate, returning+ a string that indicates the path to that subterm:++ * @\'b\'@ - Take the body of an abstraction.+ + * @\'t\'@ - Take the body of a type abstraction.+ + * @\'l\'@ - Take the left path in a term combination.+ + * @\'r\'@ - Take the right path in a term combination.+ + * @\'c\'@ - Take the body in a type combination.++ Fails with 'Nothing' if there is no satisfying subterm.+-}+findPath :: (HOLTerm -> Bool) -> HOLTerm -> Maybe String+findPath p tm+ | p tm = Just []+ | otherwise =+ case view tm of+ Abs _ bod -> liftM ((:) 'b') $ findPath p bod+ TyAbs _ bod -> liftM ((:) 't') $ findPath p bod+ Comb l r -> liftM ((:) 'r') (findPath p r) <|>+ liftM ((:) 'l') (findPath p l)+ TyComb bod _ -> liftM ((:) 'c') $ findPath p bod+ _ -> Nothing++{-|+ Returns the subterm found by following a 'String' path as produced by + 'findPath'. Fails with 'Nothing' if the provided term does not a suitable + subterm for the given path.+-}+followPath :: String -> HOLTerm -> Maybe HOLTerm+followPath [] tm = Just tm+followPath ('l':t) (view -> Comb l _) = followPath t l+followPath ('r':t) (view -> Comb _ r) = followPath t r+followPath ('c':t) (view -> TyComb tm _) = followPath t tm+followPath ('b':t) (view -> Abs _ bod) = followPath t bod+followPath ('t':t) (view -> TyAbs _ bod) = followPath t bod+followPath _ _ = Nothing++-- theorem manipulators+-- | Returns the list of all free type variables in a theorem.+typeVarsInThm :: HOLThm -> [HOLType]+typeVarsInThm (view -> Thm asl c) =+ foldr (union . typeVarsInTerm) (typeVarsInTerm c) asl++-- | Returns the list of all free term variables in a theorem.+thmFrees :: HOLThm -> [HOLTerm]+thmFrees (view -> Thm asl c) = + foldr (union . frees) (frees c) asl++-- more syntax+{-|+ Constructs a complex combination that represents the application of a + function to a list of arguments. Fails with 'Left' if any internal call to + 'mkComb' fails.+-}+listMkComb :: HOLTerm -> [HOLTerm] -> Either String HOLTerm+listMkComb = foldlM mkComb++{-|+ Constructs a complex abstraction that represents a term with multiple+ bound variables. Fails with 'Left' if any internal call to 'mkAbs' fails.+-}+listMkAbs :: [HOLTerm] -> HOLTerm -> Either String HOLTerm+listMkAbs = flip (foldrM mkAbs)++-- Useful function to create stylized arguments using numbers+{-|+ Constructs a list of term variables of a given prefix. Names are adjusted+ as necessary with 'variant' to avoid clashing with the provided list of term+ variables. The number and types of the resultant variables is directed by + the provided list of types, i.e.++ > mkArgs "x" avoids [ty1, ... tyn] === [x1:ty1, ..., xn:tyn] where {x1, ..., xn} are not elements of avoids+-}+mkArgs :: String -> [HOLTerm] -> [HOLType] -> [HOLTerm]+mkArgs s avoid (ty:[]) = [variant avoid $ mkVar s ty]+mkArgs s avoid tys = mkRec 0 s avoid tys+ where mkRec :: Int -> String -> [HOLTerm] -> [HOLType] -> [HOLTerm]+ mkRec _ _ _ [] = []+ mkRec n x avs (y:ys) =+ let v' = variant avs $ mkVar (x ++ show n) y+ vs = mkRec (n + 1) x (v':avs) ys in+ (v':vs)++{-| + Returns the left term of a combination. Fails with 'Nothing' if the provided+ term is not a combination.+-}+rator :: HOLTerm -> Maybe HOLTerm+rator (view -> Comb l _) = Just l+rator _ = Nothing++{-|+ Returns the right term of a combination. Fails with 'Nothing' if the provided+ term is not a combination.+-}+rand :: HOLTerm -> Maybe HOLTerm+rand (view -> Comb _ r) = Just r+rand _ = Nothing++{-|+ Returns the bound term of an abstraction. Fails with 'Nothing' if the+ provided term is not an abstraction.+-}+bndvar :: HOLTerm -> Maybe HOLTerm+bndvar (view -> Abs bv _) = Just bv+bndvar _ = Nothing++{-|+ Returns the body term of an abstraction. Fails with 'Nothing' if the+ provided term is not an abstraction.+-}+body :: HOLTerm -> Maybe HOLTerm+body (view -> Abs _ bod) = Just bod+body _ = Nothing++{-|+ Returns the bound type of a type abstraction. Fails with 'Nothing' if the+ provided term is not a type abstraction.+-}+bndvarTyabs :: HOLTerm -> Maybe HOLType+bndvarTyabs (view -> TyAbs bv _) = Just bv+bndvarTyabs _ = Nothing++{-|+ Returns the body term of a type abstraction. Fails with 'Nothing' if the+ provided term is not a type abstraction.+-}+bodyTyabs :: HOLTerm -> Maybe HOLTerm+bodyTyabs (view -> TyAbs _ bod) = Just bod+bodyTyabs _ = Nothing++{-|+ Destructs a complex combination returning its function term and its list of+ argument terms.+-}+stripComb :: HOLTerm -> (HOLTerm, [HOLTerm])+stripComb = revSplitList destComb++{-|+ Destructs a complex abstraction returning its list of bound variables and its+ body term.+-}+stripAbs :: HOLTerm -> ([HOLTerm], HOLTerm)+stripAbs = splitList destAbs++-- type matching+{-|+ Computes a tiplet of substitution environments that can be used to make two+ types match. The triplet argument can be used to constrain the match, or+ its three environments can be left empty to find the most general match.+ Fails with 'Nothing' in the event that a match cannot be found that satisfies+ the provided constraint.+-}+typeMatch :: HOLType -> HOLType -> SubstTrip -> Maybe SubstTrip+typeMatch vty cty sofar =+ return snd <*> typeMatchRec vty cty ([], sofar)+ where typeMatchRec :: HOLType -> HOLType -> ([HOLType], SubstTrip) ->+ Maybe ([HOLType], SubstTrip)+ typeMatchRec v@(view -> TyVar{}) c acc@(env, (sfar, opTys, opOps))+ | v `elem` env = Just acc+ | otherwise =+ case lookup v sfar of+ Just c'+ | c' == c -> Just acc+ | otherwise -> Nothing+ Nothing -> Just (env, ((v, c):sfar, opTys, opOps))+ typeMatchRec (view -> UType tvv varg) + (view -> UType tvc carg) (env, sfar) =+ let carg' = if tvv == tvc then carg + else typeSubst [(tvc, tvv)] carg in+ typeMatchRec varg carg' (tvv:env, sfar)+ typeMatchRec (view -> TyApp vop vargs) + (view -> TyApp cop cargs) acc@(env, (sfar, opTys, opOps))+ | vop == cop = foldr2M typeMatchRec acc vargs cargs+ | isTypeOpVar vop && isTypeOpVar cop =+ do copTy <- hush . uTypeFromTypeOpVar cop $ length cargs+ case lookup vop opTys of+ Just cop'+ | cop' == copTy -> + foldr2M typeMatchRec acc vargs cargs+ | otherwise -> Nothing+ Nothing -> + foldr2M typeMatchRec + (env, (sfar, (vop, copTy):opTys, opOps)) vargs cargs + | isTypeOpVar vop =+ case lookup vop opOps of+ Just cop'+ | cop' == cop -> foldr2M typeMatchRec acc vargs cargs+ | otherwise -> Nothing+ Nothing -> + foldr2M typeMatchRec + (env, (sfar, opTys, (vop, cop):opOps)) vargs cargs+ | otherwise = Nothing+ typeMatchRec _ _ _ = Nothing++-- matching version of mkConst+{-|+ Constructs an instance of a constant of the provided name and type. Relies+ internally on 'typeMatch' in order to provide a match between the most general+ type of the constant and the provided type. Throws a 'HOLException' in the+ following cases:++ * The provided string is not the name of a defined constant.++ * Type matching fails.+-}+mkMConst :: String -> HOLType -> HOL cls thry HOLTerm+mkMConst name ty = + do uty <- getConstType name <?> "mkMConst: not a constant name"+ (mkConstFull name . fromJust $ typeMatch uty ty ([], [], [])) <?>+ "mkMConst: generic type cannot be instantiated"++{-|+ A version of 'mkComb' that instantiates the type variables in the left hand+ argument. Relies internally on 'typeMatch' in order to provide a match+ between the domain type of the function and the type of the argument. Fails+ with 'Nothing' if instantiation is impossible.+-}+mkIComb :: HOLTerm -> HOLTerm -> Maybe HOLTerm+mkIComb tm1 tm2 =+ do (ty, _) <- destFunTy $ typeOf tm1+ mat <- typeMatch ty (typeOf tm2) ([], [], [])+ hush $ mkComb (instFull mat tm1) tm2++{-|+ An iterative version of 'mkIComb' that builds a complex combination given a+ constant name and a list of arguments, attempting to find a correct+ instantiation at every step. Throws a 'HOLException' in the following cases:++ * The provided name is not a currently defiend constant.++ * Any internal call to mkIComb fails.+-}+listMkIComb :: String -> [HOLTerm] -> HOL cls thry HOLTerm+listMkIComb cname args =+ do cnst <- mkConst cname ([]::HOLTypeEnv) <?> + "listMkIComb: not a constant name"+ liftMaybe "listMkIComb: type cannot be instantiated" $+ foldlM mkIComb cnst args+ +-- syntax for binary operators+{-| + Predicate that tests if a term is a binary application whose operator has the+ given name.+-}+isBinary :: String -> HOLTerm -> Bool+isBinary s (view -> Comb (view -> Comb (view -> Const s' _ _) _) _) = s == s'+isBinary _ _ = False++-- | A version of 'isBinary' that tests for operator terms, not strings.+isBinop :: HOLTerm -> HOLTerm -> Bool+isBinop op (view -> Comb (view -> Comb op' _) _) = op' == op+isBinop _ _ = False++{-|+ Destructs a binary application returning its left and right arguments. Fails + with 'Nothing' if the provided term is not a binary application with the + specified operator name.+-}+destBinary :: String -> HOLTerm -> Maybe (HOLTerm, HOLTerm)+destBinary s (view -> Comb (view -> Comb (view -> Const s' _ _) l) r)+ | s == s' = Just (l, r)+ | otherwise = Nothing+destBinary _ _ = Nothing++-- | A version of 'destBinary' that tests for operator terms, not strings.+destBinop :: HOLTerm -> HOLTerm -> Maybe (HOLTerm, HOLTerm)+destBinop op (view -> Comb (view -> Comb op' l) r)+ | op' == op = Just (l, r)+ | otherwise = Nothing+destBinop _ _ = Nothing++{-|+ Constructs a binary application given a constant name and two argument terms.+ Note that no instantiation is performed, thus the constant must be monomorphic+ or the provided arguments must match the constant's general type. Throws a+ 'HOLException' if any of the internal calls to 'mkConst' or 'mkComb' fail.+-}+mkBinary :: String -> HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+mkBinary s l r = + (do c <- mkConst s ([]::HOLTypeEnv)+ fromRightM $ mkComb (fromRight $ mkComb c l) r)+ <?> "mkBinary: " ++ s++{-| + A version of 'mkBinary' that accepts the operator as a pre-constructed term.+-}+mkBinop :: HOLTerm -> HOLTerm -> HOLTerm -> Either String HOLTerm+mkBinop op tm1 tm2 =+ liftM1 mkComb (mkComb op tm1) tm2 <?> "mkBinop"++{-| + Iteratively builds a complex combination using 'mkBinop', i.e.+ + > listMkBinop (/\) [T, F, T] === T /\ F /\ T+-} +listMkBinop :: HOLTerm -> [HOLTerm] -> Either String HOLTerm+listMkBinop = foldr1M . mkBinop++{-|+ The inverse of 'listMkBinop'. Destructs a complex combination built with+ a binary operator into its list of arguments.+-}+binops :: HOLTerm -> HOLTerm -> [HOLTerm]+binops = stripList . destBinop++-- syntax for complex abstractions+-- | Predicate for generalized abstractions. See 'mkGAbs' for more details.+isGAbs :: HOLTerm -> Bool+isGAbs = isJust . destGAbs++-- | Predicate that tests if a term is an abstraction of specified binder name.+isBinder :: String -> HOLTerm -> Bool+isBinder s (view -> Comb (view -> Const s' _ _) (view -> Abs{})) = s == s'+isBinder _ _ = False++{-| + Predicate that tests if a term is an abtraction of a specified type binder+ name.+-}+isTyBinder :: String -> HOLTerm -> Bool+isTyBinder s (view -> Comb (view -> Const s' _ _) (view -> TyAbs{})) = s == s'+isTyBinder _ _ = False++{-| + Destructor for generalized abstractions. Fails with 'Nothing' if the provided+ term is not an abstraction or generalized abstraction. See 'mkGAbs' for more + details.+-}+destGAbs :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+destGAbs tm@(view -> Abs{}) = destAbs tm+destGAbs (view -> Comb (view -> Const "GABS" _ _) (view -> Abs _ bod)) =+ firstM rand =<< (destGeq . snd $ stripForall bod)+ where destGeq :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+ destGeq = destBinary "GEQ"+destGAbs _ = Nothing++{-|+ Destructs an abstraction of specified binder name into its bound variable and+ its body term. Fails with 'Nothing' if the provided term is not an+ abstraction with the specified binder name.+-}+destBinder :: String -> HOLTerm -> Maybe (HOLTerm, HOLTerm)+destBinder s (view -> Comb (view -> Const s' _ _) (view -> Abs bv t))+ | s == s' = Just (bv, t)+ | otherwise = Nothing+destBinder _ _ = Nothing++{-|+ Destructs a type abstraction of specified binder name into its bound type+ variable and its body term. Fails with 'Nothing' if the provided term is not+ a type abstraction with the specified type binder name.+-}+destTyBinder :: String -> HOLTerm -> Maybe (HOLType, HOLTerm)+destTyBinder s (view -> Comb (view -> Const s' _ _) (view -> TyAbs bv t))+ | s == s' = Just (bv, t)+ | otherwise = Nothing+destTyBinder _ _ = Nothing++{-|+ Constructor for generalized abstractions. Generalized abstractions extend+ term abstractions to the more general of notion of a function mapping some+ structure to some term. This allows us to bind patterns more complicated+ than a variable, i.e. binding pairs++ > \ (x:num, y:num) -> x + y++ or lists++ > \ CONS x xs -> x++ Note that in the case where the pattern to bind is simply a variable 'mkGAbs'+ just calls 'mkAbs'.+-}+mkGAbs :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+mkGAbs tm1@(view -> Var{}) tm2 =+ liftEither "mkGAbs: simple abstraction failed" $ mkAbs tm1 tm2+mkGAbs tm1 tm2 = + let fvs = frees tm1 in+ (do fTy <- mkFunTy (typeOf tm1) $ typeOf tm2+ let f = variant (frees tm1++frees tm2) $ mkVar "f" fTy+ bodIn <- listMkForall fvs =<< mkGEq (fromRight $ mkComb f tm1) tm2+ bndr <- mkConst "GABS" [(tyA, fTy)]+ fromRightM $ mkComb bndr =<< mkAbs f bodIn)+ <?> "mkGAbs"+ where mkGEq :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+ mkGEq t1 t2 = + do p <- mkConst "GEQ" [(tyA, typeOf t1)]+ fromRightM $ mkBinop p t1 t2++{-|+ Constructs an abstraction given a binder name and two argument terms. Throws+ a 'HOLException' if any of the internal calls to 'mkConst', 'mkAbs', or + 'mkComb' fail.++ Note that the given string can actually be any constant name of type + @(A -> *) -> *@, such that a well-typed term of the form @c (\\x . t)@ can be+ produced.+-}+mkBinder :: String -> HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+mkBinder op v tm = + (do c <- mkConst op [(tyA, typeOf v)]+ fromRightM $ mkComb c =<< mkAbs v tm)+ <?> "mkBinder: " ++ op++{-|+ Constructs a type abstraction given a type binder name, a type variable to+ find, and a body term. Throws a 'HOLException' if any of the internal calls+ to 'mkConst', 'mkTyAbs', or 'mkComb' fail.++ Note that the given string can actually be any constant name of type+ @(% 'a . *) -> *@, such that a well-typed term of the form @c (\\\\x . t)@ can+ be produced.+-}+mkTyBinder :: String -> HOLType -> HOLTerm -> HOL cls thry HOLTerm+mkTyBinder op v tm =+ (do c <- mkConst op ([]::HOLTypeEnv)+ fromRightM $ mkComb c =<< mkTyAbs v tm)+ <?> "mkTyBinder: " ++ op++-- | A specific version of 'listMkAbs' for general abstractions.+listMkGAbs :: [HOLTerm] -> HOLTerm -> HOL cls thry HOLTerm+listMkGAbs = flip (foldrM mkGAbs)++-- | A specific version of 'stripAbs' for general abstractions.+stripGAbs :: HOLTerm -> ([HOLTerm], HOLTerm)+stripGAbs = splitList destGAbs++-- common special cases of binary ops+-- | Predicate for boolean conjunctions.+isConj :: HOLTerm -> Bool+isConj = isBinary "/\\"++-- | Predicate for boolean implications.+isImp :: HOLTerm -> Bool+isImp = isBinary "==>"++-- | Predicate for universal term quantification.+isForall :: HOLTerm -> Bool+isForall = isBinder "!"++-- | Predicate for existential term quantification.+isExists :: HOLTerm -> Bool+isExists = isBinder "?"++-- | Predicate for boolean disjunctions.+isDisj :: HOLTerm -> Bool+isDisj = isBinary "\\/"++-- | Predicate for boolean negations.+isNeg :: HOLTerm -> Bool+isNeg (view -> Comb (view -> Const "~" _ _) _) = True+isNeg _ = False++-- | Predicate for unique, existential quantification.+isUExists :: HOLTerm -> Bool+isUExists = isBinder "?!"++-- | Predicate for term-level universal type quantification.+isTyAll :: HOLTerm -> Bool+isTyAll = isTyBinder "!!"++-- | Predicate for term-level existential type quantification.+isTyEx :: HOLTerm -> Bool+isTyEx = isTyBinder "??"++-- | Destructor for boolean conjunctions.+destConj :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+destConj = destBinary "/\\"++-- | Destructor for boolean implications.+destImp :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+destImp = destBinary "==>"++-- | Destructor for universal term quantification.+destForall :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+destForall = destBinder "!"++-- | Destructor for existential term quantification.+destExists :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+destExists = destBinder "?"++-- | Destructor for boolean disjunctions.+destDisj :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+destDisj = destBinary "\\/"++-- | Destructor for boolean negations.+destNeg :: HOLTerm -> Maybe HOLTerm+destNeg (view -> Comb (view -> Const "~" _ _) p) = Just p+destNeg _ = Nothing++-- | Destructor for unique, existential quantification.+destUExists :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+destUExists = destBinder "?!"++-- | Destructor for term-level universal type quantification.+destTyAll :: HOLTerm -> Maybe (HOLType, HOLTerm)+destTyAll = destTyBinder "!!"++-- | Destructor for term-level existential type quantification.+destTyEx :: HOLTerm -> Maybe (HOLType, HOLTerm)+destTyEx = destTyBinder "??"++{-|+ Constructor for boolean conjunctions. Throws a 'HOLException' if the internal+ call to 'mkBinary' fails.+-}+mkConj :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+mkConj = mkBinary "/\\"++{-|+ Constructor for boolean implications. Throws a 'HOLException' if the internal+ call to 'mkBinary' fails.+-}+mkImp :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+mkImp = mkBinary "==>"++{-| + Constructor for universal term quantification. Throws a 'HOLException' if the+ internal call to 'mkBinder' fails.+-}+mkForall :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+mkForall = mkBinder "!"++{-| + Constructor for existential term quantification. Throws a 'HOLException' if + the internal call to 'mkBinder' fails.+-}+mkExists :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+mkExists = mkBinder "?"++{-|+ Constructor for boolean disjunctions. Throws a 'HOLException' if the internal+ call to 'mkBinary' fails.+-}+mkDisj :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+mkDisj = mkBinary "\\/"++{-|+ Constructor for boolean negations. Throws a 'HOLException' if any of the + internal calls to 'mkConst' or 'mkComb' fail.+-}+mkNeg :: HOLTerm -> HOL cls thry HOLTerm+mkNeg tm = + (do c <- mkConst "~" ([]::HOLTypeEnv)+ fromRightM $ mkComb c tm)+ <?> "mkNeg"++{-| + Constructor for unique, existential term quantification. Throws a + 'HOLException' if the internal call to 'mkBinder' fails.+-}+mkUExists :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+mkUExists = mkBinder "?!"++{-|+ Constructor for term-level universal type quantification. Throws a + 'HOLException' if the internal call to 'mkTyBinder' fails.+-}+mkTyAll :: HOLType -> HOLTerm -> HOL cls thry HOLTerm+mkTyAll = mkTyBinder "!!"++{-|+ Constructor for term-level existential type quantification. Throws a + 'HOLException' if the internal call to 'mkTyBinder' fails.+-}+mkTyEx :: HOLType -> HOLTerm -> HOL cls thry HOLTerm+mkTyEx = mkTyBinder "??"++-- | Constructs a complex conjunction from a given list of propositions.+listMkConj :: [HOLTerm] -> HOL cls thry HOLTerm+listMkConj = foldr1M mkConj++-- | A specific version of 'listMkAbs' for universal term quantification.+listMkForall :: [HOLTerm] -> HOLTerm -> HOL cls thry HOLTerm+listMkForall = flip (foldrM mkForall)++-- | A specific version of 'listMkAbs' for existential term quantification.+listMkExists :: [HOLTerm] -> HOLTerm -> HOL cls thry HOLTerm+listMkExists vs bod = foldrM mkExists bod vs++-- | Constructs a complex disjunction from a given list of propositions.+listMkDisj :: [HOLTerm] -> HOL cls thry HOLTerm+listMkDisj = foldr1M mkDisj++-- | Returns the list of propositions in a complex conjunction.+conjuncts :: HOLTerm -> [HOLTerm]+conjuncts = stripList destConj++-- | Returns the list of propositions in a complex disjunction.+disjuncts :: HOLTerm -> [HOLTerm]+disjuncts = stripList destDisj++-- | A specific version of 'stripAbs' for universal term quantification.+stripForall :: HOLTerm -> ([HOLTerm], HOLTerm)+stripForall = splitList destForall++-- | A specific version of 'stripAbs' for existential term quantification.+stripExists :: HOLTerm -> ([HOLTerm], HOLTerm)+stripExists = splitList destExists++{-| + A specific version of 'stripAbs' for term-level universal type quantification.+-}+stripTyAll :: HOLTerm -> ([HOLType], HOLTerm)+stripTyAll = splitList destTyAll++{-| + A specific version of 'stripAbs' for term-level existential type + quantification.+-}+stripTyEx :: HOLTerm -> ([HOLType], HOLTerm)+stripTyEx = splitList destTyEx++-- syntax for other terms+-- | Predicate for list @CONS@.+isCons :: HOLTerm -> Bool+isCons = isBinary "CONS"++-- | Predicate for list terms.+isList :: HOLTerm -> Bool+isList = isJust . destList++-- | Predicate for let binding terms.+isLet :: HOLTerm -> Bool+isLet = isJust . destLet++-- | Destructor for list @CONS@.+destCons :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+destCons = destBinary "CONS"++{-|+ Destructor for list terms. Returns a list of the elements in the term. Fails+ with 'Nothing' if internall the term is not of the form++ > x1 `CONS` .... xn `CONS` NIL+-}+destList :: HOLTerm -> Maybe [HOLTerm]+destList tm =+ let (tms, nil) = splitList destCons tm in+ case view nil of+ (Const "NIL" _ _) -> Just tms+ _ -> Nothing++{-|+ Destructs a let binding term into a list of its name and value pairs and its+ body term. Fails with 'Nothing' if internally the term is not of the form++ > LET (x1, v1) ... (xn, vn) LET_END+-}+destLet :: HOLTerm -> Maybe ([(HOLTerm, HOLTerm)], HOLTerm)+destLet tm =+ case stripComb tm of+ (view -> Const "LET" _ _, a:args) ->+ let (vars, lebod) = stripGAbs a+ eqs = zip vars args in+ case view lebod of+ Comb (view -> Const "LET_END" _ _) bod -> Just (eqs, bod)+ _ -> Nothing+ _ -> Nothing++{-|+ Converts a numeral term to an 'Integer'. Fails with 'Nothing' if internally+ the term is not of the form++ > NUMERAL bits _0, where bits is a series of BIT0 and BIT1 terms +-} +destNumeral :: HOLTerm -> Maybe Integer+destNumeral (view -> Comb (view -> Const "NUMERAL" _ _) r) = destNum r+ where destNum :: HOLTerm -> Maybe Integer+ destNum (view -> Const "_0" _ _) = Just 0+ destNum (view -> Comb (view -> Const "BIT0" _ _) r') =+ liftM (2 *) $ destNum r'+ destNum (view -> Comb (view -> Const "BIT1" _ _) r') =+ liftM (\ x -> 1 + 2 * x) $ destNum r'+ destNum _ = Nothing+destNumeral _ = Nothing
+ src/HaskHOL/Core/Basics.hs-boot view
@@ -0,0 +1,8 @@+module HaskHOL.Core.Basics where+ +import HaskHOL.Core.Kernel+import HaskHOL.Core.State++genVar :: HOLType -> HOL cls thry HOLTerm++stripComb :: HOLTerm -> (HOLTerm, [HOLTerm])
+ src/HaskHOL/Core/Basics/Nets.hs view
@@ -0,0 +1,196 @@+{-# LANGUAGE TemplateHaskell #-}++{-|+ Module: HaskHOL.Core.Basics.Nets+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module defines term nets, an efficient tree structure used for fast + lookups of values that match a given "pattern" term. Typically term nets are+ used to store a collection of conversions or tactics to be used for rewriting.+ By associating these operations with the pattern that they are valid for, the+ rewrite process can quickly prune computations that will obviously fail.++ For more information see the "nets" module from John Harrison's HOL Light.+-}+module HaskHOL.Core.Basics.Nets+ ( Net+ , netEmpty -- :: Net a+ , netEnter -- :: Ord a => [HOLTerm] -> (HOLTerm, a) -> Net a -> + -- HOL cls thry (Net a)+ , netLookup -- :: HOLTerm -> Net a -> [a]+ , netMerge -- :: Ord a => Net a -> Net a -> Net a+ ) where++import HaskHOL.Core.Lib+import HaskHOL.Core.Kernel+import HaskHOL.Core.State+import {-# SOURCE #-} HaskHOL.Core.Basics (genVar, stripComb)++-- ordered, unique insertion for sets as lists+setInsert :: Ord a => a -> [a] -> [a]+setInsert a xs = fromMaybe xs $ sinsert a xs+ where sinsert :: Ord a => a -> [a] -> Maybe [a]+ sinsert x [] = Just [x]+ sinsert x l@(h:t)+ | h == x = Nothing+ | x < h = Just (x:l)+ | otherwise = do t' <- sinsert x t+ return (h:t')++-- ordered, unique merging of two sets+setMerge :: Ord a => [a] -> [a] -> [a]+setMerge [] l2 = l2+setMerge l1 [] = l1+setMerge l1@(h1:t1) l2@(h2:t2)+ | h1 == h2 = h1 : setMerge t1 t2+ | h1 < h2 = h1 : setMerge t1 l2+ | otherwise = h2 : setMerge l1 t2++-- The data type that defines a label for each node in a term net.+data TermLabel + = VNet -- variables+ | LCNet String Int -- local constants+ | CNet String Int -- constants+ | LNet Int -- term abstraction+ | LTyAbs -- type abstraction+ | LTyComb -- type combination+ deriving (Eq, Show)+ +{-|+ Internally, 'Net's are represented with a tree structure; each node has a list+ of labeled branches and a list of values. The node labels are generated via+ the following guidelines:++ * Flattening of combinations favors the left hand side such that the head of + an application is looked at first.++ * If the head of an application is variable, the whole term is considered + variable.++ * Type abstractions and type combinations are effectively treated as local + constants, though they do have their own node lable representations to avoid+ any potential issues with user provided variable lists for 'enter'.++ * Matching is conservative, such that all matching values will be returned, + but some non-matching values may be returned. For example, a pattern term + of the form @x \`op\` x@ will match any term of the form @a \`op\` b@ + regardless of the values of @a@ and @b@.+-}+data Net a = NetNode [(TermLabel, Net a)] [a] deriving Show++-- | The empty 'Net'.+netEmpty :: Net a+netEmpty = NetNode [] []++{-+ Generates a net node label given a pattern term. Differs from labelToLookup+ in that it accepts a list of variables to treat as local constants when+ generating the label.+-}+labelToStore :: [HOLTerm] -> HOLTerm -> HOL cls thry (TermLabel, [HOLTerm])+labelToStore lconsts tm = + let (op, args) = stripComb tm in+ case view op of+ (Const x _ _) -> return (CNet x (length args), args)+ (Abs bv bod) -> + do bod' <- if bv `elem` lconsts+ then do v <- genVar $ typeOf bv+ return $! varSubst [(bv, v)] bod+ else return bod+ return (LNet (length args), bod':args)+ (TyAbs _ t) -> return (LTyAbs, [t])+ (TyComb t _) -> return (LTyComb, [t])+ (Var x _) -> return $! if op `elem` lconsts+ then (LCNet x (length args), args)+ else (VNet, [])+ _ -> error "labelToStore: stripComb broken"++{- + Used by enter in order to update a net. Recursively generates node labels for+ the provided pattern using labelToStore.+-}+netUpdate :: Ord a => [HOLTerm] -> (a, [HOLTerm], Net a) -> HOL cls thry (Net a)+netUpdate _ (b, [], NetNode edges tips) = + return . NetNode edges $ setInsert b tips+netUpdate lconsts (b, tm:rtms, NetNode edges tips) =+ do (label, ntms) <- labelToStore lconsts tm+ let (child, others) = case remove (\ (x, _) -> x == label) edges of+ Just edges' -> (snd `ffComb` id) edges'+ Nothing -> (netEmpty, edges)+ newChild <- netUpdate lconsts (b, ntms++rtms, child)+ return $! NetNode ((label, newChild):others) tips++{-| + Inserts a new element, paired with a pattern term, into a provided net. The + first argument is a list of variables that should be treated as local + constants, such that only patterns with those variables at the exact same + position will match. See the documentation for 'Net' for more details.++ Never fails.+-}+netEnter :: Ord a => [HOLTerm] -> (HOLTerm, a) -> Net a -> HOL cls thry (Net a)+netEnter lconsts (tm, b) net = netUpdate lconsts (b, [tm], net)++{-+ Generates a node label from a provided pattern term. Differs from+ labelToStore in that no list of local constants to consider is given.+-}+labelForLookup :: HOLTerm -> (TermLabel, [HOLTerm])+labelForLookup tm =+ let (op, args) = stripComb tm in+ case view op of+ (Const x _ _ ) -> (CNet x (length args), args)+ (Abs _ bod) -> (LNet (length args), bod:args)+ (TyAbs _ t) -> (LTyAbs, [t])+ (TyComb t _) -> (LTyComb, [t])+ (Var x _) -> (LCNet x (length args), args)+ _ -> error "labelForLookup: stripComb broken"++{-+ Traverses a Net following the labels generated from pattern terms via+ labelForLookup. Returns a list of all values that satisfy the generated+ pattern.+-}+follow :: ([HOLTerm], Net a) -> [a]+follow ([], NetNode _ tips) = tips+follow (tm:rtms, NetNode edges _) = + let (label, ntms) = labelForLookup tm+ collection = case lookup label edges of+ Just child -> follow (ntms++rtms, child)+ Nothing -> [] in+ if label == VNet then collection+ else case lookup VNet edges of+ Just vn -> collection ++ follow (rtms, vn)+ Nothing -> collection++{-|+ Returns the list of all values stored in a term net that satisfy a provided+ pattern term. See the documentation for 'Net' for more details.+-}+netLookup :: HOLTerm -> Net a -> [a]+netLookup tm net = follow ([tm], net)++{-|+ Merges two term nets together. The values for the two nets are merged,+ maintaining order and uniqueness, with the term labels adjusted appropriately.+ The algorithm to do so is courtesy of Don Syme via John Harrison's+ implementation in HOL Light.+-}+netMerge :: Ord a => Net a -> Net a -> Net a+netMerge (NetNode l1 data1) (NetNode l2 data2) =+ NetNode (foldr addNode (foldr addNode [] l1) l2) $ setMerge data1 data2+ where addNode :: Ord a => (TermLabel, Net a) -> [(TermLabel, Net a)] ->+ [(TermLabel, Net a)]+ addNode p@(lab, net) l =+ case remove (\ (x, _) -> x == lab) l of+ Just ((lab', net'), rest) ->+ (lab', netMerge net net'):rest+ Nothing -> p:l+ +-- Lift derivations+deriveLiftMany [''TermLabel, ''Net]
+ src/HaskHOL/Core/Ext.hs view
@@ -0,0 +1,212 @@+{-# 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'.+-}
+ src/HaskHOL/Core/Ext/Protected.hs view
@@ -0,0 +1,257 @@+{-# LANGUAGE ExistentialQuantification, FlexibleInstances, + FunctionalDependencies, MultiParamTypeClasses, ScopedTypeVariables,+ TemplateHaskell, TypeFamilies, TypeSynonymInstances, + UndecidableInstances, ViewPatterns #-}++{-|+ 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, serve)+ , PData+ , PType+ , PTerm+ , PThm+ , liftProtectedExp -- :: (Protected a, Typeable thry) => + -- PData a thry -> Q Exp+ , liftProtected -- :: (Protected a, Typeable thry) => + -- String -> PData a thry -> Q [Dec]+ , proveCompileTime -- :: Typeable thry => HOLContext thry -> String ->+ -- HOL Proof thry HOLThm -> Q [Dec]+ , proveCompileTimeMany -- :: Typeable thry => HOLContext thry -> [String] + -- -> HOL Proof thry [HOLThm] -> Q [Dec]+ , extractBasicDefinition -- :: Typeable thry => HOLContext thry -> + -- String -> String -> Q [Dec]+ , extractAxiom -- :: Typeable thry => + -- HOLContext thry -> String -> Q [Dec]+ ) where++import HaskHOL.Core.Lib+import HaskHOL.Core.Kernel hiding (typeOf)+import HaskHOL.Core.State+import HaskHOL.Core.Parser++import Data.Typeable (typeOf, typeRepArgs)+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+ data PData a thry+ -- | Protects a value by sealing it against a provided context.+ protect :: HOLContext thry -> a -> PData a thry+ {-| + 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+ liftTy :: a -> Name+ protLift :: PData a thry -> Q Exp++instance Protected HOLThm where+ data PData HOLThm thry = PThm HOLThm+ protect _ = PThm+ serve (PThm thm) = return thm+ liftTy _ = ''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+ liftTy _ = ''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+ liftTy _ = ''HOLType+ protLift (PTy ty) = conE 'PTy `appE` lift ty+-- | Type synonym for protected 'HOLType's.+type PType thry = PData HOLType thry++instance HOLTermRep (PTerm thry) thry where+ toHTm = serve++instance HOLTypeRep (PType thry) thry where+ toHTy = serve+++{-+ Builds the theory contrainst for a lifted, protected value. + For example:+ + > buildThryType (x::PData a Bool)++ builds the context++ > forall thry. BoolCtxt thry => PData a thry+-}+buildThryType :: forall a thry. (Protected a, Typeable thry) => + PData a thry -> Type+buildThryType _ =+ let tyname = mkName "thry"+ ctxtName = mkName $ topTheory ++ "Ctxt"+ cls = ClassP ctxtName [VarT tyname] in+ ForallT [PlainTV tyname] [cls] . + AppT (AppT (ConT ''PData) . ConT $ liftTy (undefined :: a)) $ + VarT tyname+ where topTheory :: String+ topTheory = + let base = show . head . typeRepArgs $ typeOf (undefined :: thry) in+ take (length base - 4) base++{-| + 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, Typeable thry) => + PData a thry -> Q Exp+liftProtectedExp pdata =+ do pdata' <- protLift pdata+ let 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, Typeable thry) => + String -> PData a thry -> Q [Dec]+liftProtected lbl pdata =+ do pdata' <- protLift pdata+ let ty = buildThryType pdata+ name = mkName lbl+ tysig = SigD name ty+ dec = ValD (VarP name) (NormalB pdata') []+ return [tysig, dec]+++-- Compile Time Proof+--EvNote: long-term idea: change debuging printing to be based on cabal flag+{-|+ Evaluates a proof compilation, protects it with the theory used to evaluate+ it, and then lifts it as a declaration of a given name with an ascribed type+ signature.++ Relies internally on 'protect' and 'liftProtected' to guarantee that the+ resultant theorem is sealed with the right type.+-}+proveCompileTime :: Typeable thry => HOLContext thry -> String -> + HOL Proof thry HOLThm -> Q [Dec]+proveCompileTime ctx lbl th =+ do thm <- runIO $ + do putStr $ "proving: " ++ lbl ++ "..."+ thm <- evalHOLCtxt (setBenignFlag FlagDebug >> th) ctx+ putStrLn "...proved."+ return thm+ liftProtected lbl $ protect ctx thm++{-|+ A version of 'proveCompileTime' that works for a proof computation returning+ multiple theorems.++ Note that each resultant theorem must have a unique, provided name.+-}+proveCompileTimeMany :: Typeable thry => HOLContext thry -> [String] -> + HOL Proof thry [HOLThm] -> Q [Dec]+proveCompileTimeMany ctx lbls ths =+ let n = length lbls in+ do thms <- runIO $ + do putStrLn $ "proving " ++ show n ++ " theorems"+ thms <- evalHOLCtxt (setBenignFlag FlagDebug >> ths) ctx+ if length thms /= n+ then fail $ "proveCompileTimeMany: number of " ++ + "theorems and labels does not agree."+ else do putStrLn $ unwords lbls ++ " proved."+ return thms+ liftM concat . mapM (\ (lbl, thm) -> liftProtected lbl $ + protect ctx thm) $ zip lbls thms++-- Extraction functions for Core State values+{-|+ Extracts a basic term definition from a provided context, protecting and + lifting it with 'liftProtected'. The extraction is performed by looking for + a definition whose left hand side matches a provided constant name.+ For example:++ > extractBasicDefinition ctxtBool "defT" "T"++ will return the spliceable list of declarations for the following theorem++ @ |- T = (\ p:bool . p) = (\ p:bool . p) @+-}+extractBasicDefinition :: Typeable thry => + HOLContext thry -> String -> String -> Q [Dec]+extractBasicDefinition ctx lbl name =+ do defns <- runIO $ evalHOLCtxt definitions ctx+ let mb = find (\ x -> case destEq $ concl x of+ Just (view -> Const l _ _, _) -> l == name+ _ -> False) defns+ case mb of+ Nothing -> fail "extractBasicDefinition: definition not found"+ Just th -> liftProtected lbl $ protect ctx th++{-|+ Extracts an axiom from a provided context, protecting and lifting it with+ 'liftProtected'. The extraction is performed by looking for an axioms of+ a given name, as specified when the axiom was created with 'newAxiom'.+-}+extractAxiom :: Typeable thry => HOLContext thry -> String -> Q [Dec]+extractAxiom ctx lbl =+ do ax <- runIO $ evalHOLCtxt + (getAxiom lbl <?> "extractAxiom: axiom not found") ctx+ liftProtected lbl $ protect ctx ax
+ src/HaskHOL/Core/Ext/QQ.hs view
@@ -0,0 +1,93 @@+{-|+ Module: HaskHOL.Core.Ext.QQ+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module defines a mechanism for compile time quasi-quoting of 'HOLTerm's.+ The 'baseQuoter' method constructs a theory specific quasi-quoter that parses+ 'HOLTerm's at the expression level using 'toHTm'. An example, 'base' is + provided to demonstrate how this process works.++ Additionally, a specialized quasi-quoter for 'String's is provided that+ escapes special characters and trims white-space. This can be helpful when+ expressing 'HOLTerm's as 'String's, i.e. @\"\\ x . x\"@.+-}+module HaskHOL.Core.Ext.QQ+ ( baseQuoter -- :: Typeable thry => HOLContext thry -> QuasiQuoter+ , base -- :: QuasiQuoter+ , str -- :: QuasiQuoter+ ) where++import HaskHOL.Core.Lib+import HaskHOL.Core.State+import HaskHOL.Core.Parser+import HaskHOL.Core.Ext.Protected++{-+ We require some Template Haskell primitives that shouldn't be exposed outside+ of this module, i.e. runIO+-}+import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Language.Haskell.TH.Syntax (Lift(..))++-- Only used here, not really necessary to include in Core.Lib+import Data.Char (isSpace)++{-|+ This is the base quasi-quoter for the HaskHOL system. When provided with a+ theory context value, it constucts a theory specific quasi-quoter that parses+ a 'String' as a term, protecting and lifting the result.++ Note that, at this point in time, we only allowing quoting at the expression+ level.+-}+baseQuoter :: Typeable thry => HOLContext thry -> QuasiQuoter+baseQuoter ctxt = QuasiQuoter quoteBaseExps nothing nothing nothing+ where quoteBaseExps x =+ liftProtectedExp =<< + (runIO . evalHOLCtxt (liftM (protect ctxt) $ toHTm x) $ ctxt)+ nothing _ = fail "quoting here not supported"++{-| + An instance of 'baseQuoter' for the core theory context, 'ctxtBase'.+ Example:++ > [base| x = y |]++ will parse the provided string and construct the 'HOLTerm' @x = y@ at compile+ time. Note that this term is protected, such that it has to be accessed via+ 'serve'. This is advantageous in computations that may be run many times, + for example:++ > do tm <- serve [base| x = y |]+ > ...++ will parse the term exactly once, only checking the @thry@ tag of the+ computation for each evaluation. Conversely,++ > do tm <- toHTm "x = y"+ > ...++ will parse the term for every evaluation of that computation. Generally, the+ use of 'toHTm' is reserved for run time parsing and in larger computations+ that themselves are evaluated at copmile time to minimize the amount of work+ Template Haskell has to do.+-}+base :: QuasiQuoter+base = baseQuoter ctxtBase++{-|+ This is a specialized quasi-quoter for 'String's. It can be used to strip+ white space and automatically escape special characters. It is typically used+ in conjunction with 'toHTm' directly or indirectly.+-}+str :: QuasiQuoter+str = QuasiQuoter quoteStrExp nothing nothing nothing+ where quoteStrExp x = lift $ trim x+ trim = dropWhile isSpace . dropWhileEnd isSpace+ nothing _ = fail "quoting here not supported"
+ src/HaskHOL/Core/Kernel.hs view
@@ -0,0 +1,549 @@+{-|+ Module: HaskHOL.Core.Kernel+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module exports the logical kernel of HaskHOL. It consists of:++ * The view pattern required to pattern match on terms outside of the kernel.++ * A safe view of HOL theorems for HaskHOL.++ * The primitive inference rules of the system.++ * The primitive, stateless theory extension functions.++ For clarity, all of these items have been seperated based on their influential+ system: HOL Light, Stateless HOL, and HOL2P.++ Note that, per the stateless approach, any stateful, but still primitive,+ functions related to theorems or theory extension have been relocated to the + "HaskHOL.Core.State" module.+-}+module HaskHOL.Core.Kernel+ ( -- * A View of HOL Types, Terms, and Theorems+ -- ** A Quick Note on View Patterns+ -- $ViewPatterns+ view -- :: a -> b+ -- ** Destructors and Accessors for Theorems+ , HOLThm+ , HOLThmView(..)+ , destThm -- :: HOLThm -> ([HOLTerm], HOLTerm)+ , hyp -- :: HOLThm -> [HOLTerm]+ , concl -- :: HOLThm -> HOLTerm+ -- * HOL Light Primitive Inference Rules+ , primREFL -- :: HOLTerm -> HOLThm+ , primTRANS -- :: HOLThm -> HOLThm -> Either String HOLThm+ , primMK_COMB -- :: HOLThm -> HOLThm -> Either String HOLThm + , primABS -- :: HOLTerm -> HOLThm -> Either String HOLThm + , primBETA -- :: HOLTerm -> Either String HOLThm+ , primASSUME -- :: HOLTerm -> Maybe HOLThm+ , primEQ_MP -- :: HOLThm -> HOLThm -> Either String HOLThm+ , primDEDUCT_ANTISYM_RULE -- :: HOLThm -> HOLThm -> HOLThm+ , primINST_TYPE -- :: Inst a b => [(a, b)] -> HOLThm -> HOLThm+ , primINST_TYPE_FULL -- :: SubstTrip -> HOLThm -> HOLThm+ , primINST -- :: HOLTermEnv -> HOLThm -> HOLThm+ -- * HOL2P Primitive Inference Rules+ , primTYABS -- :: HOLType -> HOLThm -> Either String HOLThm+ , primTYAPP2 -- :: HOLType -> HOLType -> HOLThm -> Either String HOLThm+ , primTYAPP -- :: HOLType -> HOLThm -> Maybe HOLThm+ , primTYBETA -- :: HOLTerm -> Either String HOLThm+ -- * Stateless HOL Primitive Theory Extensions+ , axiomThm -- :: HOLTerm -> HOLThm+ , newDefinedConst -- :: HOLTerm -> Either String (HOLTerm, HOLThm)+ , newDefinedTypeOp -- :: String -> String -> String -> HOLThm -> Either + -- String (TypeOp, HOLTerm, HOLTerm, HOLThm, HOLThm)+ -- * Primitive Re-Exports+ , module HaskHOL.Core.Kernel.Types+ , module HaskHOL.Core.Kernel.Terms+ ) where++import HaskHOL.Core.Lib+import HaskHOL.Core.Kernel.Prims+import HaskHOL.Core.Kernel.Types+import HaskHOL.Core.Kernel.Terms++{-+ Used to quickly make an equality between two terms we know to be of the same+ type. Not exposed to the user.+-}+safeMkEq :: HOLTerm -> HOLTerm -> HOLTerm+safeMkEq l = CombIn $ CombIn (tmEq $ typeOf l) l++{- + Unions two lists of terms, ordering the result modulo alpha-equivalence. Not+ exposed to the user.+-}+termUnion :: [HOLTerm] -> [HOLTerm] -> [HOLTerm]+termUnion [] l2 = l2+termUnion l1 [] = l1+termUnion l1@(h1:t1) l2@(h2:t2) = + case alphaOrder h1 h2 of+ EQ -> h1 : termUnion t1 t2+ LT -> h1 : termUnion t1 l2+ _ -> h2 : termUnion l1 t2++{- + Removes a term from a term list, ordering the result modulo alpha-equivalence.+ Not exposed to the user.+-}+termRemove :: HOLTerm -> [HOLTerm] -> [HOLTerm]+termRemove _ [] = []+termRemove t l@(s:ss) =+ case alphaOrder t s of+ GT -> s : termRemove t ss+ EQ -> ss+ _ -> l++{- + Maps a function over a list of terms, termUnion-ing the result at each step.+ Roughly equivalent to a composition of nub and map that orders the result+ modulo alpha-equivalence. Not exposed to the user+-}+termImage :: (HOLTerm -> HOLTerm) -> [HOLTerm] -> [HOLTerm]+termImage _ [] = []+termImage f (h:t) = termUnion [f h] $ termImage f t++{-+ HOL Light Theorem Primitives+-}+{-| + Destructs a theorem, returning its list of assumption terms and conclusion+ term.+-}+destThm :: HOLThm -> ([HOLTerm], HOLTerm)+destThm (ThmIn a c) = (a, c)++-- | Accessor for the hypotheses, or assumption terms, of a theorem.+hyp :: HOLThm -> [HOLTerm]+hyp (ThmIn a _) = a++-- | Accessor for the conclusion term of a theorem.+concl :: HOLThm -> HOLTerm+concl (ThmIn _ c) = c++{-+ HOL Light Primitive Inference Rules+-}++-- Basic Equality Rules++{-|@+ t +-----------+ |- t = t+@++ Never fails.+-}+primREFL :: HOLTerm -> HOLThm+primREFL t = ThmIn [] $ safeMkEq t t++{-|@+ A1 |- t1 = t2 A2 |- t2 = t3+-------------------------------+ A1 U A2 |- t1 = t3 +@++ Fails with 'Left' in the following cases:+ + * The middle terms are not alpha-equivalent.+ + * One, or both, of the theorem conclusions is not an equation.+-}+primTRANS :: HOLThm -> HOLThm -> Either String HOLThm+primTRANS (ThmIn a1 (CombIn eql@(CombIn (ConstIn "=" _ Prim) _) m1)) + (ThmIn a2 (CombIn (CombIn (ConstIn "=" _ Prim) m2) r))+ | m1 `aConv` m2 =+ Right . ThmIn (termUnion a1 a2) $ CombIn eql r+ | otherwise = Left "primTRANS: middle terms don't agree" +primTRANS _ _ = Left "primTRANS: not both equations"++-- Basic Congruence Rules++{-|@+ A1 |- f = g A2 |- x = y+---------------------------+ A1 U A2 |- f x = g y+@++ Fails with 'Left' in the following cases:+ + * One, or both, of the theorem conclusions is not an equation.+ + * The first theorem conclusion is not an equation of function terms.+ + * The types of the function terms and argument terms do not agree.+-}+primMK_COMB :: HOLThm -> HOLThm -> Either String HOLThm +primMK_COMB (ThmIn a1 (CombIn (CombIn (ConstIn "=" _ Prim) l1) r1))+ (ThmIn a2 (CombIn (CombIn (ConstIn "=" _ Prim) l2) r2)) =+ case typeOf l1 of+ (TyAppIn (TyPrim "fun" _) (ty:_:_))+ | typeOf l2 `tyAConv` ty ->+ Right . + ThmIn (termUnion a1 a2) . safeMkEq (CombIn l1 l2) $ CombIn r1 r2+ | otherwise -> Left "primMK_COMB: types do not agree"+ _ -> Left "primMK_COMB: not a function type"+primMK_COMB _ _ = Left "primMK_COMB: not both equations"++{-|@+ A |- t1 = t2+-------------------------------+ A |- (\\ x . t1) = (\\ x . t2)+@++ Fails with 'Left' in the following cases:+ + * The term to bind is free in the assumption list of the theorem.+ + * The conclusion of the theorem is not an equation.+-}+primABS :: HOLTerm -> HOLThm -> Either String HOLThm +primABS v@VarIn{} (ThmIn a (CombIn (CombIn (ConstIn "=" _ Prim) l) r))+ | any (varFreeIn v) a = + Left "primABS: variable is free in assumptions" + | otherwise = + Right . ThmIn a . safeMkEq (AbsIn v l) $ AbsIn v r+primABS _ _ = Left "primABS: not an equation"++-- Beta Reduction+{-|@+ (\\ x . t[x]) x+-------------------------------+ |- (\\ x . t) x = t[x]+@++ Fails with 'Left' in the following cases:+ + * The term is not a valid application.+ + * The reduction is not a trivial one, i.e. the argument term is not equivalent+ to the bound variable.+-}+primBETA :: HOLTerm -> Either String HOLThm+primBETA tm@(CombIn (AbsIn bv bod) arg)+ | arg == bv = Right . ThmIn [] $ safeMkEq tm bod+ | otherwise = Left "primBETA_PRIM: not a trivial beta reduction"+primBETA _ = Left "primBETA_PRIM: not a valid application"++-- Deduction Rules+{-|@+ t+-----------+ t |- t+@++ Fails with 'Nothing' if the term is not a proposition.+-}+primASSUME :: HOLTerm -> Maybe HOLThm+primASSUME tm+ | typeOf tm == tyBool = Just $ ThmIn [tm] tm+ | otherwise = Nothing++{-|@+ A1 |- t1 = t2 A2 |- t1+----------------------------+ A1 U A2 |- t2+@++ Fails with 'Left' in the following cases:++ * The conclusion of the first theorem is not an equation.++ * The conclusion term of the second theorem and the left hand side of the + equation are not alpha-equivalent.+-}+primEQ_MP :: HOLThm -> HOLThm -> Either String HOLThm+primEQ_MP (ThmIn a1 (CombIn (CombIn (ConstIn "=" _ Prim) l) r)) (ThmIn a2 c)+ | l `aConv` c = Right $ ThmIn (termUnion a1 a2) r+ | otherwise = Left "primEQ_MP: terms do not agree"+primEQ_MP _ _ = Left "primEQ_MP: term is not an equation"++{-|@+ A |- p B |- q +----------------------------------+ (A - {q}) U (B - {p}) |- p \<=\> q+@++ Never fails.+-}+primDEDUCT_ANTISYM_RULE :: HOLThm -> HOLThm -> HOLThm+primDEDUCT_ANTISYM_RULE (ThmIn a p) (ThmIn b q) =+ ThmIn (termRemove q a `termUnion` termRemove p b) $ safeMkEq p q++-- Instantiation Rules+{-|@+ [(ty1, tv1), ..., (tyn, tvn)] A |- t +----------------------------------------+ A[ty1, ..., tyn/tv1, ..., tvn]+ |- t[ty1, ..., tyn/tv1, ..., tvn]+@++ Never fails.+-}+primINST_TYPE :: Inst a b => [(a, b)] -> HOLThm -> HOLThm+primINST_TYPE tyenv (ThmIn a t) = + let instFun = inst tyenv in+ ThmIn (termImage instFun a) $ instFun t++-- | A version of 'primINST_TYPE' that instantiates a theorem via 'instFull'.+primINST_TYPE_FULL :: SubstTrip -> HOLThm -> HOLThm+primINST_TYPE_FULL tyenv (ThmIn a t) =+ let instFun = instFull tyenv in+ ThmIn (termImage instFun a) $ instFun t++{-|@+ [(t1, x1), ..., (tn, xn)] A |- t +------------------------------------+ A[t1, ..., tn/x1, ..., xn]+ |- t[t1, ..., tn/x1, ..., xn] +@++ Never fails.+-}+primINST :: HOLTermEnv -> HOLThm -> HOLThm+primINST env (ThmIn a t) = + let instFun = varSubst env in+ ThmIn (termImage instFun a) $ instFun t++{-+ HOL2P Primitive Inference Rules+-}++-- Type Congruence rules++{-|@+ A |- t1 = t2+-------------------------------+ A |- (\\\\ x . t1) = (\\\\ x . t2)+@++ Fails with 'Left' in the following cases:++ * The type to bind is not a small type variable. ++ * The conclusion of the theorem is not an equation.++ * The type to bind is free in the assumption list of the theorem. + + * The type variable to bind is free in the conclusion of the theorem.+-}+primTYABS :: HOLType -> HOLThm -> Either String HOLThm+primTYABS tv@(TyVarIn True _) + (ThmIn a (CombIn (CombIn (ConstIn "=" _ Prim) l) r))+ | tv `notElem` typeVarsInTerms a =+ let fvs = frees l `union` frees r in+ if any (\ x -> tv `elem` tyVars (typeOf x)) fvs+ then Left "primTYABS: type variable is free in conclusion"+ else Right . ThmIn a . safeMkEq (TyAbsIn tv l) $ TyAbsIn tv r+ | otherwise =+ Left "primTYABS: type variable is free in assumptions"+primTYABS (TyVarIn True _) _ = + Left "primTYABS: conclusion not an equation"+primTYABS _ _ =+ Left "primTYABS: first argument not a small type variable"++{-|@+ A |- t1 = t2+-------------------------------+ A |- t1 [: ty1] = t2 [: ty2]+@++ Fails with 'Left' in the following cases:++ * The conclusion of the theorem is not an equation of terms of universal type.++ * The type arguments are not alpha-equivalent.++ * One, or both, of the type arguments is not small.+-}+primTYAPP2 :: HOLType -> HOLType -> HOLThm -> Either String HOLThm+primTYAPP2 ty1 ty2 (ThmIn a (CombIn (CombIn (ConstIn "=" _ Prim) l) r))+ | ty1 `tyAConv` ty2 = + case typeOf l of+ UTypeIn{} + | not $ isSmall ty1 ->+ Left "primTYAPP2: ty1 not small"+ | not $ isSmall ty2 ->+ Left "primTYAPP2: ty2 not small"+ | otherwise -> + Right . ThmIn a . safeMkEq (TyCombIn l ty1) $ TyCombIn r ty2+ _ -> Left "primTYAPP2: terms not of universal type"+ | otherwise = + Left "primTYAPP2: type arguments not alpha-convertible"+primTYAPP2 _ _ _ = Left "primTYAPP2: conclusion not an equation"+ +{-|@+ A |- t1 = t2+----------------------------+ A |- t1 [: ty] = t2 [: ty]+@++ Fails with 'Nothing' if the conclusion of the theorem is not an equation.++ Note that 'primTYAPP' is equivalent to 'primTYAPP2' when the same type is+ applied to both sides, i.e. ++ @ primTYAPP ty === primTYAPP2 ty ty+ @+-}+primTYAPP :: HOLType -> HOLThm -> Maybe HOLThm+primTYAPP ty (ThmIn a (CombIn (CombIn (ConstIn "=" _ Prim) l) r)) = + Just . ThmIn a $ safeMkEq (TyCombIn l ty) (TyCombIn r ty)+primTYAPP _ _ = Nothing++-- Type Beta Reduction++{-|@+ (\\\\ ty . t[ty]) [: ty] +---------------------------------+ |- (\\\\ ty . t[ty]) [: ty] = t+@++ Fails with 'Left' in the following cases:++ * The term is not a valid type application.++ * The reduction is not a trivial one, i.e. the argument type is not equivalent+ to the bound type variable.+-}+primTYBETA :: HOLTerm -> Either String HOLThm+primTYBETA tm@(TyCombIn (TyAbsIn tv bod) argt)+ | argt == tv = Right . ThmIn [] $ safeMkEq tm bod+ | otherwise = Left "primTYBETA: not a trivial type beta reduction"+primTYBETA _ = Left "primTYBETA: not a valid type application"++{-+ Stateless HOL Theory Extension Primitives+ Note that the following primitives are in HaskHOL.Core.State as per+ Stateless HOL:+ axioms, newAxiom, newBasicDefinition, newBasicTypeDefinition+-}++{-|+ Creates a new axiom theorem. ++ Note that, as discussed in the documentation for 'HOLThm', the introduction of+ axioms is not tracked until the stateful layer of the system is introduced so + be careful using this function.+-}+axiomThm :: HOLTerm -> HOLThm +axiomThm = ThmIn []++{-|@+ c = t +-----------+ |- c = t+@++ Creates a new defined constant given a term that equates a variable of the+ desired constant name and type to its desired definition. The return value + is a pair of the new constant and its definitional theorem. ++ Note that internally the constant is tagged with its definitional term via the+ @Defined@ 'ConstTag'.++ Fails with 'Left' in the following cases:++ * The provided term is not an equation.++ * The provided term is not closed.++ * There are free type variables present in the definition that are not also in+ the desired type of the constant.+-} +newDefinedConst :: HOLTerm -> Either String (HOLTerm, HOLThm)+newDefinedConst tm@(CombIn (CombIn (ConstIn "=" _ Prim) (VarIn cname ty)) r)+ | not (freesIn [] r) =+ Left "newDefinedConst: not closed"+ | not (subset (typeVarsInTerm r) (tyVars ty)) =+ Left "newDefinedConst: type vars not refelcted in const"+ | otherwise = + let c = ConstIn cname ty $ Defined tm+ dth = ThmIn [] $ safeMkEq c r in+ Right (c, dth)+newDefinedConst _ = Left "newDefinedConst: not an equation"++{-|@+ |- p x:rep+-----------------------------------------------------------------+ (|- mk:rep->ty (dest:ty->rep a) = a, |- P r \<=\> dest(mk r) = r)+@++ Creates a new defined type constant that is defined as an inhabited subset+ of an existing type constant. The return value is a pentuple that + collectively provides a bijection between the new type and the old type.++ The following four items are taken as input:++ * The name of the new type constant - @ty@ in the above sequent.++ * The name of the new term constant that will be used to make an instance of + the new type - @mk@ in the above sequent.++ * The name of the new term constant that will be used to destruct an instance+ of the new type - @dest@ in the above sequent.++ * A theorem proving that the desired subset is non-empty. The conclusion of+ this theorem must take the form @p x@ where @p@ is the predicate that+ defines the subset and @x@ is a witness to inhabitation.++ The following items are returned as part of the resultant pentuple:++ * The new defined type operator. These type operators carry their name,+ arity, and definitional theorem. The arity, in this case, is inferred from+ the number of free type variables found in the predicate of the definitional+ theorem.++ * The new term constants, @mk@ and @dest@, as described above. Note that + constants constructed in this manner are tagged with special instances of + 'ConstTag', @MkAbstract@ and @DestAbstract@ accordingly, that carry the + name, arity, and definitional theorem of their related type constant.++ * The two theorems proving the bijection, as shown in the sequent above.+-}+newDefinedTypeOp :: String -> String -> String -> HOLThm -> + Either String (TypeOp, HOLTerm, HOLTerm, HOLThm, HOLThm)+newDefinedTypeOp tyname absname repname dth'@(ThmIn [] (CombIn p x))+ | containsUType $ typeOf x =+ Left "newDefinedTypeOp: must not contain universal types"+ | not $ freesIn [] p =+ Left "newDefinedTypeOp: predicate is not closed"+ | otherwise = + let tys = sort (<=) (typeVarsInTerm p)+ arity = length tys+ atyop = TyDefined tyname arity dth'+ rty = typeOf x+ aty = TyAppIn atyop tys+ atm = VarIn "a" aty+ rtm = VarIn "r" rty+ absCon = ConstIn absname (TyAppIn tyOpFun [rty, aty]) $ + MkAbstract tyname arity dth'+ repCon = ConstIn repname (TyAppIn tyOpFun [aty, rty]) $ + DestAbstract tyname arity dth' in+ Right (atyop, absCon, repCon,+ ThmIn [] (safeMkEq (CombIn absCon (CombIn repCon atm)) atm),+ ThmIn [] (safeMkEq (CombIn p rtm) $ + safeMkEq (CombIn repCon (CombIn absCon rtm)) rtm))+newDefinedTypeOp _ _ _ _ = Left "newDefinedTypeOp: poorly formed predicate"+++-- Documentation copied from HaskHOL.Core.Prims++{-$ViewPatterns+ The primitive data types of HaskHOL are implemented using view patterns in+ order to simulate private data types:++ * Internal constructors are hidden to prevent manual construction of terms.++ * View constructors (those of 'HOLTypeView', 'HOLTermView', and 'HOLThmView') + are exposed to enable pattern matching. ++ * View patterns, as defined by instances of the 'view' function from the + @Viewable@ class, provide a conversion between the two sets of constructors.+-}
+ src/HaskHOL/Core/Kernel/Prims.hs view
@@ -0,0 +1,324 @@+{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses, + TemplateHaskell #-}++{-|+ Module: HaskHOL.Core.Kernel.Prims+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module defines the primitive data types for HaskHOL: + 'HOLType', 'HOLTerm', and 'HOLThm'.++ Note: This module is intended to be hidden by cabal to prevent manual, and + possibly unsound, construction of the primitive data types. ++ To include the contents of this module with the appropriate restrictions in + place, along with the entirey of the core system, import the "HaskHOL.Core"+ module. Alternatively, the following modules also export individual+ primitive types with their associated restrictions:+ * "HaskHOL.Core.Types" - Exports types+ * "HaskHOL.Core.Terms" - Exports terms+ * "HaskHOL.Core.Kernel" - Exports theorems+-}++module HaskHOL.Core.Kernel.Prims+ ( -- * HOL types+ HOLType(..)+ , HOLTypeView(..)+ , TypeOp(..)+ , HOLTypeEnv+ , SubstTrip+ -- * HOL terms+ , HOLTerm(..)+ , HOLTermView(..)+ , ConstTag(..)+ , HOLTermEnv+ -- * HOL theorems+ , HOLThm(..)+ , HOLThmView(..)+ -- * The View pattern class+ , Viewable(..)+ ) where++import HaskHOL.Core.Lib++{-+ A quick note on how the primitive data types of HaskHOL are implemented -- + view patterns are used to simulate private data types for HOL types and + terms:+ * Internal constructors are hidden to prevent manual construction of terms.+ + * View constructors (those of 'HOLTypeView', 'HOLTermView', and 'HOLThmView')+ are exposed to enable pattern matching. + + * View patterns, as defined by instances of the 'view' function from the + @Viewable@ class, provide a conversion between the two sets of constructors.+-}++{-+ The following data types combined provide the definition of HOL types in + HaskHOL.++ The primary data type, 'HOLType', follows closely from the + simply typed lambda calculus approach used in John Harrison's HOL Light + system. ++ There are two principle changes to Harrison's implementation:+ 1. Type operators have been introduced, via the 'TypeOp' data type, to + facilitate a stateless logical kernel following from Freek Wiedijk's + Stateless HOL system.++ 2. Universal types and type operator variables have been introduced to move+ the logic from simply typed to polymorphic following from Norbert + Voelker's HOL2P system.+-}++{-|+ The 'HOLType' data type defines the internal constructors for HOL types in+ HaskHOL. For more details, see the documentation for its view pattern data+ type, 'HOLTypeView'.+-}+data HOLType+ = TyVarIn !Bool !String+ | TyAppIn !TypeOp ![HOLType]+ | UTypeIn !HOLType !HOLType+ deriving (Eq, Ord, Typeable) ++-- | The view pattern data type for HOL types.+data HOLTypeView+ -- | A type variable consisting of a constraint flag and name.+ = TyVar Bool String+ {-| + A type application consisting of a type operator and a list of type+ arguments. See 'TypeOp' for more details.+ -}+ | TyApp TypeOp [HOLType]+ {-| + A universal type consisting of a bound type and a body type. Note that + the bound type must be a small, type variable.+ -}+ | UType HOLType HOLType++{-|+ The data type for type operators, 'TypeOp', is a mashing together of the+ representation of type operators from both both HOL2P and Stateless HOL.+ For more information regarding construction of the different operators, see+ the documentation of the following functions: 'mkTypeOpVar', 'newPrimTypeOp',+ 'newDefinedTypeOp'+-}+data TypeOp + = TyOpVar !String+ | TyPrim !String !Int+ | TyDefined !String !Int !HOLThm+ deriving (Eq, Ord, Typeable)++{-+ In order to keep HaskHOL's type system decidable, we follow the same + \"smallness\" constraint used by HOL2P: type variables that are constrained + to be small cannot be replaced with types that contain either universal types+ or unconstrained type variables. This constraint, in addition to the+ restriction that universal types can only bind small type variables, prevents+ the system from performing a substitution that would result in a higher rank+ type than the system is capable of dealing with. This effectively limits the+ type system to 2nd order polymorphism.++ Voelker elected to rely on syntactic distinction to differentiate between the+ many kinds of type variables (small, unconstrained, and operator); depending + on how it was to be used, the name of a variable was prepended with a special + symbol. Internal to HaskHOL, we elected to replace these syntactic + distinctions with structural ones such that the following hold true:++ * @TyVarIn True \"x\"@ represents the small type variable @\'x@+ + * @TyVarIn False \"x\"@ represents the unconstrainted type variable @x@+ + * @TyOpVar "x"@ represents the type operator variable @_x@++ Note that external to HaskHOL, during I/O of terms, both the parser and+ pretty-printer still rely on the syntactic distinctions introduced by+ Voelker.+-} ++-- | Type synonym for the commonly used, list-based, type environment.+type HOLTypeEnv = [(HOLType, HOLType)]++{-| + Type synonym for the commonly used triplet of substitution environments.+ See 'TypeSubst' for more information.+-}+type SubstTrip = (HOLTypeEnv, [(TypeOp, HOLType)], [(TypeOp, TypeOp)])++-- Viewable and Show instances for HOLType+instance Viewable HOLType HOLTypeView where+ view (TyVarIn b s) = TyVar b s+ view (TyAppIn tyop tys) = TyApp tyop tys+ view (UTypeIn v b) = UType v b++instance Show TypeOp where+ show (TyOpVar s) = '_' : s+ show (TyPrim s _) = s+ show (TyDefined s _ _) = s++{-+ The following data types combined provide the definition of HOL terms in + HaskHOL.++ Corresponding with the 'HOLType' data type, 'HOLTerm' follows closely from+ the definition of terms in HOL Light. Again, the appropriate modifications+ have been made to facilitate a stateless and polymorphic term language.++ Most notably this includes:+ (1) The introduction of tags for constants to carry information formerly+ contained in the state.++ 2. Additional constructors have been added to 'HOLTerm' to facilitate+ term-level, type abstractions and applications.+-}++{-|+ The 'HOLTerm' data type defines the internal constructors for HOL terms in+ HaskHOL. For more details, see the documentation for its view pattern data+ type, 'HOLTermView'.+-}+data HOLTerm+ = VarIn !String !HOLType+ | ConstIn !String !HOLType !ConstTag+ | CombIn !HOLTerm !HOLTerm+ | AbsIn !HOLTerm !HOLTerm+ | TyCombIn !HOLTerm !HOLType+ | TyAbsIn !HOLType !HOLTerm+ deriving (Eq, Ord, Typeable)++-- | The view pattern data type for HOL terms.+data HOLTermView+ -- | A term variable consisting of a name and type.+ = Var String HOLType+ {-| + A term constant consisting of a name, type, and tag. See 'ConstTag' for + more information.+ -}+ | Const String HOLType ConstTag+ -- | A term application consisting of a function term and argument term.+ | Comb HOLTerm HOLTerm+ {-| + A term abstraction consisting of a bound term and a body term. Note that+ the bound term must be a type variable.+ -}+ | Abs HOLTerm HOLTerm+ {-| + A term-level, type application consisting of a body term and an argument + type. Note that the body term must have a universal type.+ -}+ | TyComb HOLTerm HOLType+ {-| + A term-level, type abstraction consisting of a bound type and a body term.+ Note that the bound type must be a small, type variable.+ -}+ | TyAbs HOLType HOLTerm+ +{-| + The data type for constant tags, 'ConstTag', follows identically from the+ implementation in Stateless HOL. For more information regarding construction+ of the different tags, see the documentation of the following functions:+ 'newPrimConst', 'newDefinedConst', and 'newDefinedTypeOp'.+-}+data ConstTag+ = Prim+ | Defined !HOLTerm+ | MkAbstract !String !Int !HOLThm+ | DestAbstract !String !Int !HOLThm+ deriving (Eq, Ord, Typeable)++-- | Type synonym for the commonly used, list-based, term environment.+type HOLTermEnv = [(HOLTerm, HOLTerm)]++{- + The Viewable instance for terms. + Note that the Show instance for terms is more complicated than for types and, + as such, is included separately in the HaskHOL.Core.Printer module.+-}+instance Viewable HOLTerm HOLTermView where+ view (VarIn s ty) = Var s ty+ view (ConstIn s ty tag) = Const s ty tag+ view (CombIn l r) = Comb l r+ view (AbsIn v bod) = Abs v bod+ view (TyAbsIn tv tb) = TyAbs tv tb+ view (TyCombIn tm ty) = TyComb tm ty++{-| + The 'HOLThm' data type defines HOL Theorems in HaskHOL. A theorem is defined+ simply as a list of assumption terms and a conclusion term.++ Note that this representation, in combination with a stateless + approach, means that the introduction of axioms is not tracked in the kernel.+ Axioms can be tracked once the stateful layer of the prover is introduced,+ though. For more details see the documentation for `newAxiom`.+-}+data HOLThm = ThmIn ![HOLTerm] !HOLTerm+ deriving (Eq, Ord, Typeable)++-- | The view pattern data type for HOL theorems.+data HOLThmView = Thm [HOLTerm] HOLTerm++instance Viewable HOLThm HOLThmView where+ view (ThmIn asl c) = Thm asl c++{-| + The @Viewable@ class is used to provide a polymorphic view pattern for+ HaskHOL's primitive data types.+-}+class Viewable a b where+ {-| + The view pattern function for HaskHOL's primitive data types:+ + * For types - Converts from 'HOLType' to 'HOLTypeView'.+ + * For terms - Converts from 'HOLTerm' to 'HOLTermView'.++ * For theorems - Converts from 'HOLThm' to 'HOLThmView'.+ -}+ view :: a -> b++{- + Deepseq instances for the primitive data types. These are included as they + are commonly used by a number of benchmarking libraries.+-}+instance NFData HOLType where+ rnf (TyVarIn b s) = rnf b `seq` rnf s+ rnf (TyAppIn s tys) = rnf s `seq` rnf tys+ rnf (UTypeIn tv tb) = rnf tv `seq` rnf tb++instance NFData TypeOp where+ rnf (TyOpVar s) = rnf s+ rnf (TyPrim s n) = rnf s `seq` rnf n+ rnf (TyDefined s n thm) = rnf s `seq` rnf n `seq` rnf thm++instance NFData HOLTerm where+ rnf (VarIn s ty) = rnf s `seq` rnf ty+ rnf (ConstIn s ty d) = rnf s `seq` rnf ty `seq` rnf d+ rnf (CombIn l r) = rnf l `seq` rnf r+ rnf (AbsIn bv bod) = rnf bv `seq` rnf bod+ rnf (TyAbsIn bty bod) = rnf bty `seq` rnf bod+ rnf (TyCombIn tm ty) = rnf tm `seq` rnf ty++instance NFData ConstTag where+ rnf Prim = ()+ rnf (Defined tm) = rnf tm+ rnf (MkAbstract s i thm) = rnf s `seq` rnf i `seq` rnf thm+ rnf (DestAbstract s i thm) = rnf s `seq` rnf i `seq` rnf thm++instance NFData HOLThm where+ rnf (ThmIn asl c) = rnf asl `seq` rnf c++{- + These are the automatically derived Lift instances for the primitive data+ types. These instances are used by the compile-time operations found in+ the HaskHOL.Core.Protected module.+-}+$(deriveLiftMany [ ''TypeOp, ''HOLType+ , ''ConstTag, ''HOLTerm+ , ''HOLThm])
+ src/HaskHOL/Core/Kernel/Terms.hs view
@@ -0,0 +1,631 @@+{-# LANGUAGE MultiParamTypeClasses #-}++{-|+ Module: HaskHOL.Core.Kernel.Terms+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module exports a safe view of HOL terms for HaskHOL. It also defines+ the primitive functions related to terms. For clarity, these functions have+ been seperated based on their influential system: HOL Light, Stateless HOL,+ and HOL2P.++ Note that, per the stateless approach, any stateful, but still primitive,+ functions related to terms have been relocated to the "HaskHOL.Core.State"+ module.+-}+module HaskHOL.Core.Kernel.Terms+ ( -- * A View of HOL Terms+ -- ** A Quick Note on View Patterns+ -- $ViewPatterns+ -- ** A High-Level Overview of HOL Terms+ -- $HOLTerms+ HOLTerm+ , HOLTermView(..)+ , ConstTag+ , HOLTermEnv+ -- * HOL Light Term Primitives+ -- ** Alpha-Equivalence of Terms+ , alphaOrder -- :: HOLTerm -> HOLTerm -> Ordering+ , aConv -- :: HOLTerm -> HOLTerm -> Bool+ -- ** Predicates, Constructors, and Destructors for Basic Terms+ , isVar -- :: HOLTerm -> Bool+ , isConst -- :: HOLTerm -> Bool+ , isAbs -- :: HOLTerm -> Bool+ , isComb -- :: HOLTerm -> Bool+ , mkVar -- :: String -> HOLType -> HOLTerm+ , mkAbs -- :: HOLTerm -> HOLTerm -> Either String HOLTerm+ , mkComb -- :: HOLTerm -> HOLTerm -> Either String HOLTerm+ , destVar -- :: HOLTerm -> Maybe (String, HOLType)+ , destConst -- :: HOLTerm -> Maybe (String, HOLType)+ , destComb -- :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , destAbs -- :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+ -- ** Term and Type Variable Extractors+ , frees -- :: HOLTerm -> [HOLTerm]+ , catFrees -- :: [HOLTerm] -> [HOLTerm]+ , freesIn -- :: [HOLTerm] -> HOLTerm -> Bool+ , varFreeIn -- :: HOLTerm -> HOLTerm -> Bool+ , typeVarsInTerm -- :: HOLTerm -> [HOLType]+ , typeVarsInTerms -- :: [HOLTerm] -> [HOLType]+ -- ** Term Substitution and Instantiation+ , varSubst -- :: HOLTermEnv -> HOLTerm -> HOLTerm+ , Inst+ , inst -- :: Inst a b => [(a, b)] -> HOLTerm -> HOLTerm+ , instFull -- :: SubstTrip -> HOLTerm -> HOLTerm+ , instConst -- :: TypeSubst a b => HOLTerm -> [(a, b)] -> Maybe HOLTerm+ , instConstFull -- :: HOLTerm -> SubstTrip -> Maybe HOLTerm+ -- ** Commonly Used Terms and Functions+ , tmEq -- :: HOLType -> HOLTerm+ , isEq -- :: HOLTerm -> Bool+ , primMkEq -- :: HOLTerm -> HOLTerm -> Maybe HOLTerm+ , destEq -- :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+ , variant -- :: [HOLTerm] -> HOLTerm -> HOLTerm+ , variants -- :: [HOLTerm] -> [HOLTerm] -> [HOLTerm]+ -- * Stateless HOL Term Primitives+ -- ** Constructors for Constant Tags+ , newPrimConst -- :: String -> HOLType -> HOLTerm+ -- ** Type Operator Variable Extractors+ , typeOpVarsInTerm -- :: HOLTerm -> [TypeOp]+ , typeOpVarsInTerms -- :: [HOLTerm] -> [TypeOp]+ -- * HOL2P Term Primitives+ -- ** Predicates, Constructors, and Destructors for Term-Level Types+ , isTyAbs -- :: HOLTerm -> Bool+ , isTyComb -- :: HOLTerm -> Bool+ , mkTyAbs -- :: HOLType -> HOLTerm -> Either String HOLTerm+ , mkTyComb -- :: HOLTerm -> HOLType -> Either String HOLTerm+ , destTyAbs -- :: HOLTerm -> Maybe (HOLType, HOLTerm)+ , destTyComb -- :: HOLTerm -> Maybe (HOLTerm, HOLType)+ ) where++import HaskHOL.Core.Lib+import HaskHOL.Core.Kernel.Prims+import HaskHOL.Core.Kernel.Types++{- + HOL Light Term Primitives+ Note that the following primitives are in HaskHOL.Core.State as per + Stateless HOL:+ constants, getConstType, newConstant, mkConst, mkEq+-}+-- | Provides an ordering for two terms modulo alpha-equivalence+alphaOrder :: HOLTerm -> HOLTerm -> Ordering+alphaOrder = orda []+ where orda :: HOLTermEnv -> HOLTerm -> HOLTerm -> Ordering+ orda env tm1 tm2+ | tm1 == tm2 && all (uncurry (==)) env = EQ+ | otherwise =+ case (tm1, tm2) of+ (VarIn{}, VarIn{}) -> ordav env tm1 tm2+ (ConstIn{}, ConstIn{}) -> tm1 `aorder` tm2+ (CombIn s1 t1, CombIn s2 t2) ->+ case orda env s1 s2 of+ EQ -> orda env t1 t2+ res -> res+ (AbsIn x1@(VarIn _ ty1) t1, AbsIn x2@(VarIn _ ty2) t2) ->+ case tyAlphaOrder ty1 ty2 of+ EQ -> orda ((x1, x2):env) t1 t2+ res -> res+ (AbsIn{}, AbsIn{}) -> compare tm1 tm2+ (TyAbsIn tv1@(TyVarIn True _) tb1, + TyAbsIn tv2@(TyVarIn True _) tb2) ->+ orda env tb1 $ inst [(tv2, tv1)] tb2+ (TyAbsIn{}, TyAbsIn{}) -> compare tm1 tm2+ (TyCombIn t1 ty1, TyCombIn t2 ty2) ->+ case orda env t1 t2 of+ EQ -> tyAlphaOrder ty1 ty2+ res -> res+ (ConstIn{}, _) -> LT+ (_, ConstIn{}) -> GT+ (VarIn{}, _) -> LT+ (_, VarIn{}) -> GT+ (CombIn{}, _) -> LT+ (_, CombIn{}) -> GT+ (AbsIn{}, _) -> LT+ (_, AbsIn{}) -> GT+ (TyAbsIn{}, _) -> LT+ (_, TyAbsIn{}) -> GT++ ordav :: HOLTermEnv -> HOLTerm -> HOLTerm -> Ordering+ ordav [] x1 x2 = x1 `aorder` x2+ ordav ((t1, t2):oenv) x1 x2+ | x1 == t1 = if x2 == x2 then EQ else LT+ | otherwise = if x2 == t2 then GT else ordav oenv x1 x2++ aorder :: HOLTerm -> HOLTerm -> Ordering+ aorder tm1@(VarIn s1 ty1) tm2@(VarIn s2 ty2)+ | s1 == s2 = tyAlphaOrder ty1 ty2+ | otherwise = compare tm1 tm2+ aorder tm1@(ConstIn s1 ty1 d1) tm2@(ConstIn s2 ty2 d2)+ | s1 == s2 && d1 == d2 = tyAlphaOrder ty1 ty2+ | otherwise = compare tm1 tm2+ aorder tm1 tm2 = compare tm1 tm2++-- | Tests if two terms are alpha-equivalent+aConv :: HOLTerm -> HOLTerm -> Bool+aConv tm1 tm2 = alphaOrder tm1 tm2 == EQ++-- | Predicate for term variables.+isVar :: HOLTerm -> Bool+isVar VarIn{} = True+isVar _ = False++-- | Predicate for term constants.+isConst :: HOLTerm -> Bool+isConst ConstIn{} = True+isConst _ = False++-- | Predicate for term abstractions.+isAbs :: HOLTerm -> Bool+isAbs AbsIn{} = True+isAbs _ = False++-- | Predicate for term combinations.+isComb :: HOLTerm -> Bool+isComb CombIn{} = True+isComb _ = False++-- | Constructs a term variable of a given name and type.+mkVar :: String -> HOLType -> HOLTerm+mkVar = VarIn++{-| + Constructs a term abstraction of a given bound term and body term. Fails with+ 'Left' if the bound term is not a variable.+-}+mkAbs :: HOLTerm -> HOLTerm -> Either String HOLTerm+mkAbs bv@VarIn{} bod = Right $ AbsIn bv bod+mkAbs _ _ = Left "mkAbs"++{-|+ Constructs a combination of two given terms. Fails with 'Left' in the+ following cases:++ * The first term does not have a function type.++ * The types of the two terms does not agree.+-}+mkComb :: HOLTerm -> HOLTerm -> Either String HOLTerm+mkComb f a = + case typeOf f of+ (TyAppIn (TyPrim "fun" _) (ty:_)) -> + if typeOf a `tyAConv` ty then Right $ CombIn f a+ else Left "mkComb: argument type mismatch."+ _ -> Left "mkComb: argument not of function type."++{-| + Destructs a term variable, returning its name and type. Fails with 'Nothing'+ if the provided term is not a variable.+-}+destVar :: HOLTerm -> Maybe (String, HOLType)+destVar (VarIn s ty) = Just (s, ty)+destVar _ = Nothing++{-|+ Destructs a term constant, returning its name and type. Note that no constant+ tag information is returned. Fails with 'Nothing' if the provided term is+ not a constant.+-}+destConst :: HOLTerm -> Maybe (String, HOLType)+destConst (ConstIn s ty _) = Just (s, ty)+destConst _ = Nothing++{-|+ Destructs a term combination, returning its function and argument terms. + Fails with 'Nothing' if the provided term is not a combination.+-}+destComb :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+destComb (CombIn f x) = Just (f, x)+destComb _ = Nothing++{-|+ Destructs a term abstraction, returning its bound term and body term. Fails+ with 'Nothing' if the provided term is not an abstraction.+-}+destAbs :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+destAbs (AbsIn v b) = Just (v, b)+destAbs _ = Nothing++-- | Returns a list of all free, term variables in a term.+frees :: HOLTerm -> [HOLTerm]+frees tm@VarIn{} = [tm]+frees ConstIn{} = []+frees (AbsIn bv bod) = frees bod \\ [bv]+frees (CombIn s t) = frees s `union` frees t+frees (TyAbsIn _ tm) = frees tm+frees (TyCombIn tm _) = frees tm++-- | Returns a list of all free, term variables in a list of terms.+catFrees :: [HOLTerm] -> [HOLTerm]+catFrees = foldr (union . frees) []++-- | Checks a list of term variables to see if they are all free in a give term.+freesIn :: [HOLTerm] -> HOLTerm -> Bool+freesIn acc tm@VarIn{} = tm `elem` acc+freesIn _ ConstIn{} = True+freesIn acc (AbsIn bv bod) = freesIn (bv:acc) bod+freesIn acc (CombIn s t) = freesIn acc s && freesIn acc t+freesIn acc (TyAbsIn _ t) = freesIn acc t+freesIn acc (TyCombIn t _) = freesIn acc t++-- | Checks if a variable or constant term is free in a given term.+varFreeIn :: HOLTerm -> HOLTerm -> Bool+varFreeIn v (AbsIn bv bod) = v /= bv && varFreeIn v bod+varFreeIn v (CombIn s t) = varFreeIn v s || varFreeIn v t+varFreeIn v (TyAbsIn _ t) = varFreeIn v t+varFreeIn v (TyCombIn t _) = varFreeIn v t+varFreeIn v tm = v == tm++{-| + Returns a list of all free, type variables in a term, not including + type operator variables.+-}+typeVarsInTerm :: HOLTerm -> [HOLType]+typeVarsInTerm (VarIn _ ty) = tyVars ty+typeVarsInTerm (ConstIn _ ty _) = tyVars ty+typeVarsInTerm (CombIn s t) = typeVarsInTerm s `union` typeVarsInTerm t+typeVarsInTerm (AbsIn bv t) = typeVarsInTerm bv `union` typeVarsInTerm t+typeVarsInTerm (TyAbsIn tv tm) = typeVarsInTerm tm \\ [tv]+typeVarsInTerm (TyCombIn tm ty) = typeVarsInTerm tm `union` tyVars ty++{-|+ Returns a list of all free, type variables in a list of terms, not including+ type operator variables.+-}+typeVarsInTerms :: [HOLTerm] -> [HOLType]+typeVarsInTerms =+ foldr (\ tm tvs -> typeVarsInTerm tm `union` tvs) []++{-| + Performs a basic term substitution using a substitution environment containing+ pairs consisting of a term variable and a term to be substituted for that + variable. Note that the order of elements in a substitution pair follows the+ convention of most Haskell libraries, rather than the traditional HOL + convention:+ + * The second element is substituted for the first, i.e. the substitution pair+ @(A, \\ x.x)@ indicates that the lambda term @\\x.x@ should be substituted + for the term variable @A@.+-}+varSubst :: HOLTermEnv -> HOLTerm -> HOLTerm+varSubst [] term = term+varSubst theta term =+ varSubstRec (filter validPair theta) term+ where validPair :: (HOLTerm, HOLTerm) -> Bool+ validPair (VarIn _ ty, t) = ty `tyAConv` typeOf t+ validPair _ = False++ varSubstRec :: HOLTermEnv -> HOLTerm -> HOLTerm+ varSubstRec env tm@VarIn{} = lookupd tm env tm+ varSubstRec _ tm@ConstIn{} = tm+ varSubstRec env (CombIn s t) =+ CombIn (varSubstRec env s) $ varSubstRec env t+ varSubstRec env tm@(AbsIn v s) =+ let env' = filter (\ (x, _) -> x /= v) env in+ if null env' then tm+ else let s' = varSubstRec env' s in+ if s' == s then tm+ else if any (\ (x, t) -> varFreeIn v t && + varFreeIn x s) env'+ then let v' = variant [s'] v in+ AbsIn v' $ varSubstRec ((v, v'):env') s+ else AbsIn v $ varSubstRec env' s+ varSubstRec env (TyAbsIn tv t) = TyAbsIn tv $ varSubstRec env t+ varSubstRec env (TyCombIn t ty) = TyCombIn (varSubstRec env t) ty++{-|+ The @Inst@ class provides the framework for type instantiation in HaskHOL.+ Note that in the simplest cases, instantiation is simply a type substitution+ for the types of term variables and constants. Therefore, instantiation is + constrained by the 'TypeSubst' class.++ The move to a polymorphic type system further complicates things as types can+ now be bound at the term level, requiring renaming for type instantiation.+ Since we have three different possible substitution environment types, we have+ three different possible instantiation environment types and, therefore, three+ different ways to handle renaming:++ * For @(x::'HOLTerm', r::'HOLTerm')@ substitution pairs we rename in the case + where a type abstraction binds a type variable present in @r@ and @x@ is+ present in the body of the type abstraction.++ * For @(_::'TypeOp', _::'TypeOp')@ substitution pairs we can safely ignore + renaming as our logic does not permit the binding of type operator + variables.++ * For @(x::'TypeOp', r::'HOLTerm')@ substitution pairs we rename in the case + where a type abstraction binds a type variable present in @r@ and @x@ is + present in the body of the type abstraction.++ Just as we did for the 'TypeSubst' class, we hide the internals of @Inst@ to+ prevent unsound re-definition. The correct functions to call for+ type instantiation are 'inst' and 'instFull'.+-}+class TypeSubst a b => Inst a b where+ {-| + Handles the specific case of instantiating a type abstraction term. This+ method is not exposed to the user. Call the 'inst' or 'instFull' function+ instead.+ -}+ instTyAbs :: HOLTermEnv -> [(a, b)] -> HOLTerm -> Either HOLTerm HOLTerm++instance Inst HOLType HOLType where+ instTyAbs env tyenv tm@(TyAbsIn tv t) = + let tyenv' = filter (\ (x, _) -> x /= tv) tyenv in+ if null tyenv' then Right tm+ else if any (\ (x, r) -> tv `elem` tyVars r && + x `elem` typeVarsInTerm t) tyenv'+ -- avoid capture by renaming type variable+ then let tvt = typeVarsInTerm t+ tvpatts = map fst tyenv'+ tvrepls = catTyVars . mapMaybe (`lookup` tyenv') $+ tvt `intersect` tvpatts+ tv' = variantTyVar ((tvt \\ tvpatts) `union` tvrepls) tv in+ liftM (TyAbsIn tv') $ instRec env ((tv, tv'):tyenv') t+ else liftM (TyAbsIn tv) $ instRec env tyenv' t+ instTyAbs _ _ tm = Right tm++instance Inst TypeOp TypeOp where+ instTyAbs env tyenv (TyAbsIn tv t) = + liftM (TyAbsIn tv) $ instRec env tyenv t+ instTyAbs _ _ tm = Right tm++instance Inst TypeOp HOLType where+ instTyAbs env tyenv (TyAbsIn tv t) =+ if any (\ (x, ty) -> tv `elem` tyVars ty && + x `elem` typeOpVarsInTerm t) tyenv+ -- avoid capture by renaming type variable+ then let tvbs = typeOpVarsInTerm t+ tvpatts = map fst tyenv+ tvrepls = catTyVars . mapMaybe (`lookup` tyenv) $+ tvbs `intersect` tvpatts+ tv' = variantTyVar tvrepls tv in+ liftM (TyAbsIn tv') . instRec env tyenv $ inst [(tv, tv')] t+ else liftM (TyAbsIn tv) $ instRec env tyenv t+ instTyAbs _ _ tm = Right tm++{-|+ Type instantiation for terms. Accepts the same types of substitution+ environments as discussed in the documentation for the 'TypeSubst' class, + with invalid substitution pairs being pruned internally by 'typeSubst' as + necessary. ++ For more information on why the 'Inst' class constraint is necessary and how + renaming of bound types is performed, see that classes documentation.+-}+inst :: Inst a b => [(a, b)] -> HOLTerm -> HOLTerm+inst [] tm = tm+inst theta tm = + case instRec [] theta tm of+ Right res -> res+ Left _ -> tm++-- Used internally by inst and instTyAbs both. Not exposed to the user.+instRec :: Inst a b => HOLTermEnv -> [(a, b)] -> HOLTerm -> + Either HOLTerm HOLTerm+instRec env tyenv tm@(VarIn n ty) =+ let ty' = typeSubst tyenv ty+ tm' = VarIn n ty' in+ if lookupd tm' env tm == tm then Right tm' + else Left tm' -- Clash+instRec _ tyenv (ConstIn c ty tag) =+ let ty' = typeSubst tyenv ty in+ Right $ ConstIn c ty' tag+instRec env tyenv (CombIn f x) =+ return CombIn <*> instRec env tyenv f <*> instRec env tyenv x+instRec env tyenv (AbsIn y@(VarIn _ ty) t) =+ do y' <- instRec [] tyenv y+ case instRec ((y', y):env) tyenv t of+ Right t' -> Right $ AbsIn y' t'+ e@(Left w') -> + if w' /= y' then e+ else do ifrees <- mapM (instRec [] tyenv) $ frees t+ case variant ifrees y' of+ VarIn x _ -> + let z = VarIn x ty in+ instRec env tyenv . AbsIn z $ varSubst [(y, z)] t+ _ -> e+instRec env tyenv tm@TyAbsIn{} = instTyAbs env tyenv tm+instRec env tyenv (TyCombIn tm ty) =+ do tm' <- instRec env tyenv tm+ return . TyCombIn tm' $ typeSubst tyenv ty+instRec _ _ _ = Left undefined++{-| + A version of 'inst' that accepts a triplet of type substitution environments.+-}+instFull :: SubstTrip -> HOLTerm -> HOLTerm+instFull (tyenv, tyOps, opOps) = inst opOps . inst tyOps . inst tyenv++{-|+ A simplified version of 'inst' that works only for term constants. Fails with+ 'Nothing' if the provided term is not a constant. Used internally by + 'mkConst' to guarantee that only constants are constructed.+-}+instConst :: TypeSubst a b => HOLTerm -> [(a, b)] -> Maybe HOLTerm+instConst (ConstIn name uty tag) tyenv = + Just $ ConstIn name (typeSubst tyenv uty) tag+instConst _ _ = Nothing++{-| + A version of 'instConst' that accepts a triplet of type substitition + environments.+-}+instConstFull :: HOLTerm -> SubstTrip -> Maybe HOLTerm+instConstFull (ConstIn name uty tag) tyenv = + Just $ ConstIn name (typeSubstFull tyenv uty) tag+instConstFull _ _ = Nothing++-- | Constructs an instance of the HOL equality constant, @=@, for a given type.+tmEq :: HOLType -> HOLTerm+tmEq ty = + ConstIn "=" (TyAppIn tyOpFun [ty, TyAppIn tyOpFun [ty, tyBool]]) Prim++-- | Predicate for equations, i.e. terms of the form @l = r@.+isEq :: HOLTerm -> Bool+isEq (CombIn (CombIn (ConstIn "=" _ Prim) _) _) = True+isEq _ = False++{-| + Constructs an equation term given the left and right hand side arguments. + Fails with 'Left' if the types of the terms are not alpha-equivalent.+-}+primMkEq :: HOLTerm -> HOLTerm -> Either String HOLTerm+primMkEq l r+ | typeOf l `tyAConv` typeOf r =+ Right $ CombIn (CombIn (tmEq $ typeOf l) l) r+ | otherwise = Left "primMkEq"++{-|+ Destructs an equation term, returning the left and right hand side arguments.+ Fails with 'Nothing' if the term is not an equation, i.e. of the form @l = r@.+-}+destEq :: HOLTerm -> Maybe (HOLTerm, HOLTerm)+destEq (CombIn (CombIn (ConstIn "=" _ Prim) l) r) =+ Just (l, r)+destEq _ = Nothing++{-|+ Renames a term variable to avoid sharing a name with any of a given list of+ term variables. Rreturns the original term if it's not a term variable.+-}+variant :: [HOLTerm] -> HOLTerm -> HOLTerm+variant avoid v@(VarIn s ty)+ | any (varFreeIn v) avoid = variant avoid $ VarIn (s++"'") ty+ | otherwise = v+variant _ tm = tm++{-|+ Renames a list of term variables to avoid sharing a name with any of a given+ list of term variables. As each term variable is processed it is added to+ the list of avoids such that the resultant list of term variables are all+ uniquely named.+-}+variants :: [HOLTerm] -> [HOLTerm] -> [HOLTerm]+variants _ [] = []+variants avoid (v:vs) = + let vh = variant avoid v in+ vh : variants (vh:avoid) vs++{- + Stateless HOL Term Primitives+-}++{-| + Constructs a primitive constant given a name and type. Note that primitive+ constants are tagged with a @Prim@ 'ConstTag' indicating that they have no+ definition.+-} +newPrimConst :: String -> HOLType -> HOLTerm+newPrimConst name ty = ConstIn name ty Prim++-- | Returns the list of all type operator variables in a term.+typeOpVarsInTerm :: HOLTerm -> [TypeOp]+typeOpVarsInTerm (VarIn _ ty) = typeOpVars ty+typeOpVarsInTerm (ConstIn _ ty _) = typeOpVars ty+typeOpVarsInTerm (CombIn s t) = typeOpVarsInTerm s `union` typeOpVarsInTerm t+typeOpVarsInTerm (AbsIn bv t) = typeOpVarsInTerm bv `union` typeOpVarsInTerm t+typeOpVarsInTerm (TyAbsIn _ tm) = typeOpVarsInTerm tm+typeOpVarsInTerm (TyCombIn tm ty) = typeOpVarsInTerm tm `union` typeOpVars ty++-- | Returns the list of all type operator variables in a list of terms.+typeOpVarsInTerms :: [HOLTerm] -> [TypeOp]+typeOpVarsInTerms =+ foldr (\ tm topvs -> typeOpVarsInTerm tm `union` topvs) []++{- + HOL2P Term Primitives+-}+-- | Predicate for type abstraction terms.+isTyAbs :: HOLTerm -> Bool+isTyAbs TyAbsIn{} = True+isTyAbs _ = False++-- | Predicate for type combination terms.+isTyComb :: HOLTerm -> Bool+isTyComb TyCombIn{} = True+isTyComb _ = False++{-|+ Constructs a type abstraction term given a bound type and a body term. Fails+ with 'Left' in the following cases:++ * The bound type is not a small type variable.++ * The bound type variable occurs in the type of a free variable in the body + term. +-}+mkTyAbs :: HOLType -> HOLTerm -> Either String HOLTerm+mkTyAbs tv@(TyVarIn True s) bod+ | not . any (\ x -> tv `elem` tyVars (typeOf x)) $ frees bod =+ Right $ TyAbsIn tv bod+ | otherwise = + Left $ "mkTyAbs: tyvar " ++ s ++ " occurs in type of free variable in body term."+mkTyAbs _ _ = Left "mkTyAbs: first argument not a small type variable."++{-|+ Constructs a type combination term given a body term and a type argument to + apply. Fails with 'Left' in the following cases:++ * The type argument is not a small type.++ * The type of the body term is not a universal type.+-}+mkTyComb :: HOLTerm -> HOLType -> Either String HOLTerm+mkTyComb tm ty+ | isSmall ty =+ case typeOf tm of+ UTypeIn{} -> Right $ TyCombIn tm ty+ _ -> Left "mkTyComb: term must have universal type."+ | otherwise =+ Left "mkTyComb: type argument not small."++{-| + Destructs a type abstraction, returning its bound type and body term. Fails+ with 'Nothing' if the provided term is not a type abstraction.+-}+destTyAbs :: HOLTerm -> Maybe (HOLType, HOLTerm)+destTyAbs (TyAbsIn tv bod) = Just (tv, bod)+destTyAbs _ = Nothing++{-|+ Destructs a type combination, returning its body term and type argument.+ Fails with 'Nothing' if the provided term is not a type combination.+-}+destTyComb :: HOLTerm -> Maybe (HOLTerm, HOLType)+destTyComb (TyCombIn tm ty) = Just (tm, ty)+destTyComb _ = Nothing++-- Documentation copied from HaskHOL.Core.Prims++{-$ViewPatterns+ The primitive data types of HaskHOL are implemented using view patterns in+ order to simulate private data types:++ * Internal constructors are hidden to prevent manual construction of terms.++ * View constructors (those of 'HOLTypeView', 'HOLTermView', and 'HOLThmView')+ are exposed to enable pattern matching. ++ * View patterns, as defined by instances of the 'view' function from the + @Viewable@ class, provide a conversion between the two sets of constructors.+-}++{-$HOLTerms+ The following data types combined provide the definition of HOL terms in + HaskHOL.++ Corresponding with the 'HOLType' data type, 'HOLTerm' follows closely from+ the definition of terms in HOL Light. Again, the appropriate modifications+ have been made to facilitate a stateless and polymorphic term language.++ Most notably this includes:+ (1) The introduction of tags for constants to carry information formerly+ contained in the state.++ 2. Additional constructors have been added to 'HOLTerm' to facilitate+ term-level, type abstractions and applications.+-}
+ src/HaskHOL/Core/Kernel/Types.hs view
@@ -0,0 +1,643 @@+{-# LANGUAGE MultiParamTypeClasses #-}++{-|+ Module: HaskHOL.Core.Kernel.Types+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module exports a safe view of HOL types for HaskHOL. It also defines+ the primitive functions related to types. For clarity, these functions have+ been seperated based on their influential system: HOL Light, Stateless HOL,+ and HOL2P.++ Note that, per the stateless approach, any stateful, but still primitive,+ functions related to types have been relocated to the "HaskHOL.Core.State"+ module.+-}+module HaskHOL.Core.Kernel.Types+ ( -- * A View of HOL Types+ -- ** A Quick Note on View Patterns+ -- $ViewPatterns+ -- ** A High-Level Overview of HOL Types+ -- $HOLTypes+ HOLType+ , HOLTypeView(..)+ -- ** A Quick Note on Type Variable Distinction+ -- $TypeDistinction+ , TypeOp+ , HOLTypeEnv+ , SubstTrip+ -- * HOL Light Type Primitives+ -- ** Alpha-Equivalence of Types+ , tyAlphaOrder -- :: HOLType -> HOLType -> Ordering+ , tyAConv -- :: HOLType -> HOLType -> Bool+ -- ** Predicates, Constructors, and Destructors for Basic Types+ , isVarType -- :: HOLType -> Bool+ , isType -- :: HOLType -> Bool+ , mkVarType -- :: String -> HOLType+ , destVarType -- :: HOLType -> Maybe String+ , destType -- :: HOLType -> Maybe (String, [HOLType])+ -- ** Type Variable Extractors+ , tyVars -- :: HOLType -> [HOLType]+ , catTyVars -- :: [HOLType] -> [HOLType]+ -- ** Type Substitution+ , TypeSubst+ , typeSubst -- :: TypeSubst a b => [(a, b)] -> HOLType -> HOLType+ , typeSubstFull -- :: SubstTrip -> HOLType -> HOLType+ -- ** Commonly Used Types and Functions+ , tyBool -- :: HOLType+ , tyA -- :: HOLType+ , tyB -- :: HOLType+ , destFunTy -- :: HOLType -> Maybe (HOLType, HOLType)+ , typeOf -- :: HOLTerm -> HOLType+ -- * Stateless HOL Type Primitives+ -- ** Predicates, Constructors, and Destructors for Type Operators+ , isTypeOpVar -- :: TypeOp -> Bool+ , newPrimTypeOp -- :: String -> Int -> TypeOp+ , mkTypeOpVar -- :: String -> TypeOp+ , destTypeOp -- :: TypeOp -> (String, Int)+ -- ** Commonly Used Type Operators+ , tyOpBool -- :: TypeOp+ , tyOpFun -- :: TypeOp+ , tyApp -- :: TypeOp -> [HOLType] -> Either String HOLType+ -- ** Type Operator Variable Extractors+ , typeOpVars -- :: HOLType -> [TypeOp]+ , catTypeOpVars -- :: [HOLType] -> [TypeOp]+ -- * HOL2P Type Primitives+ -- ** Predicates, Constructors, and Destructors for Universal Types+ , isUType -- :: HOLType -> Bool+ , isSmall -- :: HOLType -> Bool+ , mkUType -- :: HOLType -> HOLType -> Either String HOLType+ , mkUTypes -- :: [HOLType] -> HOLType -> Either String HOLType+ , uTypeFromTypeOpVar -- :: TypeOp -> Int -> Either String HOLType+ , mkSmall -- :: HOLType -> Either String HOLType+ , destUType -- :: HOLType -> Maybe (HOLType, HOLType)+ , destUTypes -- :: HOLType -> Maybe ([HOLType], HOLType)+ -- ** Commonly Used Functions+ , containsUType -- :: HOLType -> Bool+ , variantTyVar -- :: [HOLType] -> HOLType -> HOLType+ , variantTyVars -- :: [HOLType] -> [HOLType] -> HOLType+ ) where++import HaskHOL.Core.Lib+import HaskHOL.Core.Kernel.Prims++{- + HOL Light Type Primitives+ Note that the following primitives are in HaskHOL.Core.State as per + Stateless HOL:+ types, getTypeArityCtxt, getTypeArity, newType, mkType, mkFunTy+-}+-- | Provides an ordering for two types modulo alpha-equivalence.+tyAlphaOrder :: HOLType -> HOLType -> Ordering+tyAlphaOrder = tyorda []+ where tyorda :: HOLTypeEnv -> HOLType -> HOLType -> Ordering+ tyorda env ty1 ty2+ | ty1 == ty2 && all (uncurry (==)) env = EQ+ | otherwise =+ case (ty1, ty2) of+ (TyVarIn{}, TyVarIn{}) -> alphavars env ty1 ty2+ (TyAppIn tyop1 args1, TyAppIn tyop2 args2) ->+ case compare tyop1 tyop2 of+ EQ -> tyordas env args1 args2+ res -> res+ (UTypeIn v1 t1, UTypeIn v2 t2) -> + tyorda ((v1, v2):env) t1 t2+ (TyVarIn{}, _) -> LT+ (_, TyVarIn{}) -> GT+ (TyAppIn{}, _) -> LT+ (_, TyAppIn{}) -> GT++ alphavars :: HOLTypeEnv -> HOLType -> HOLType -> Ordering+ alphavars [] ty1 ty2 = compare ty1 ty2+ alphavars ((t1, t2):oenv) ty1 ty2+ | ty1 == t1 = if ty2 == t2 then EQ else LT+ | otherwise = if ty2 == t2 then GT else alphavars oenv ty1 ty2++ tyordas :: HOLTypeEnv -> [HOLType] -> [HOLType] -> Ordering+ tyordas _ [] [] = EQ+ tyordas _ [] _ = LT+ tyordas _ _ [] = GT+ tyordas env (x:xs) (y:ys) =+ case tyorda env x y of+ EQ -> tyordas env xs ys+ res -> res++-- | Tests if two types are alpha-equivalent.+tyAConv :: HOLType -> HOLType -> Bool+tyAConv ty1 ty2 = tyAlphaOrder ty1 ty2 == EQ++-- | Predicate for type variables.+isVarType :: HOLType -> Bool+isVarType TyVarIn{} = True+isVarType _ = False++-- | Predicate for type applications+isType :: HOLType -> Bool+isType TyAppIn{} = True+isType _ = False++{-| + Constructs a type variable of a given name. Note that the resultant type + variable is unconstrained.+-}+mkVarType :: String -> HOLType+mkVarType = TyVarIn False++{-| + Destructs a type variable, returning its name. Fails with 'Nothing' if called+ on a non-variable type.+-}+destVarType :: HOLType -> Maybe String+destVarType (TyVarIn _ s) = Just s+destVarType _ = Nothing++{-| + Destructs a type application, returning its operator name and its list of type+ arguments. Fails with 'Nothing' if called on a type that is not an + application.+-}+destType :: HOLType -> Maybe (TypeOp, [HOLType])+destType (TyAppIn op args) = Just (op, args)+destType _ = Nothing++{-| + Returns the list of all free, type variables in a type, not including type+ operator variables.+-}+tyVars :: HOLType -> [HOLType]+tyVars tv@TyVarIn{} = [tv]+tyVars (TyAppIn _ args) = catTyVars args+tyVars (UTypeIn tv ty) = tyVars ty \\ [tv]++{-| + Returns the list of all type variables in a list of types, not including type+ operator variables.+-}+catTyVars :: [HOLType] -> [HOLType]+catTyVars = foldr (union . tyVars) []++{-|+ The @TypeSubst@ class provides the framework for type substitution in HaskHOL.+ Note that, with the introduction of universal types and type operator+ variables, we now have three kinds of substitution to handle:++ * Substitution of types for type variables, satisfying type variable + constraints.++ * Instantiation of type operators with universal types.++ * Substitution of type operators for type operator variables.++ Rather than have three separate functions exposed to the user, we elected to+ provide a polymorphic type substitution function that will accept any+ well-formed, homogenous substitution environment.++ Note that the internals of @TypeSubst@ are hidden to prevent unsound+ re-definition. The relevant type substitution function is re-exported as+ 'typeSubst'. We also provide a function, 'typeSubstFull', that+ accepts a triplet of all possible substitution environments that can be+ conveniently used in combination with 'typeMatch'.++ See the ITP2013 paper, "Stateless Higher-Order Logic with Quantified Types,"+ for more details.+-}+class TypeSubst a b where+ -- | Tests if a pair is a valid element in a substitution environment.+ validSubst :: (a, b) -> Bool+ {-| + Perfoms a type substitution as described above using the provided + environment.+ -}+ typeSubst' :: [(a, b)] -> HOLType -> HOLType++instance TypeSubst HOLType HOLType where+ validSubst (TyVarIn False _, _) = True+ validSubst (TyVarIn{}, ty) = isSmall ty+ validSubst _ = False+ typeSubst' = typeTypeSubst++instance TypeSubst TypeOp TypeOp where+ validSubst (_, TyOpVar{}) = False+ validSubst (TyOpVar{}, _) = True+ validSubst _ = False+ typeSubst' = typeOpSubst++instance TypeSubst TypeOp HOLType where+ validSubst (TyOpVar{}, UTypeIn{}) = True+ validSubst _ = False+ typeSubst' = typeOpInst++{-|+ Re-exports the internal type substitution function of the 'TypeSubst' class+ to prevent unsound re-definition. Invalid substitution pairs are pruned from+ the environment such that substitution never fails.++ Note that the order of elements in a substitution pair follows the convention+ of most Haskell libraries, rather than the traditional HOL convention:+ + * The second element is substituted for the first, i.e. the substitution pair+ @(tyA, tyBool)@ indicates that the boolean type should be substituted for+ the type variable @A@.+-}+{-# INLINEABLE typeSubst #-}+typeSubst :: TypeSubst a b => [(a, b)] -> HOLType -> HOLType+typeSubst = typeSubst'++{-| + A version of 'typeSubst' that accepts a triplet of type substitution + environments.+-}+typeSubstFull :: SubstTrip -> HOLType -> HOLType+typeSubstFull (tyenv, tyOps, opOps) =+ typeOpSubst opOps . typeOpInst tyOps . typeSubst' tyenv++-- Type subst for (HOLType, HOLType) pairs.+typeTypeSubst :: HOLTypeEnv -> HOLType -> HOLType+typeTypeSubst [] t = t+typeTypeSubst tyenv t =+ typeSubstRec (filter validSubst tyenv) t+ where typeSubstRec :: HOLTypeEnv -> HOLType -> HOLType+ typeSubstRec tyins ty@TyVarIn{} = assocd ty tyins ty+ typeSubstRec tyins (TyAppIn op args) =+ TyAppIn op $ map (typeSubstRec tyins) args+ typeSubstRec tyins ty@(UTypeIn tv tbody) =+ let tyins' = filter (\ (x, _) -> x /= tv) tyins in+ if null tyins' then ty+ -- test for name capture, renaming instances of tv if necessary+ else if any (\ (x, t') -> tv `elem` tyVars t' && + x `elem` tyVars tbody) tyins'+ then let tvbs = tyVars tbody+ tvpatts = map fst tyins'+ tvrepls = catTyVars . mapMaybe (`lookup` tyins') $+ intersect tvbs tvpatts+ tv' = variantTyVar ((tvbs \\ tvpatts) `union` + tvrepls) tv in+ UTypeIn tv' $ typeSubstRec ((tv, tv') : tyins') tbody+ else UTypeIn tv $ typeSubstRec tyins' tbody + +-- | Alias to the primitive boolean type.+{-# INLINEABLE tyBool #-}+tyBool :: HOLType+tyBool = TyAppIn tyOpBool []++-- Used for error cases in type checking only. Not exported.+{-# INLINEABLE tyBottom #-}+tyBottom :: HOLType+tyBottom = TyAppIn tyOpBottom []++-- | Alias to the unconstrained type variable @A@.+{-# INLINEABLE tyA #-}+tyA :: HOLType+tyA = TyVarIn False "A"++-- | Alias to the unconstrained type variable @B@.+{-# INLINEABLE tyB #-}+tyB :: HOLType+tyB = TyVarIn False "B"++{-| + Specialized version of 'destType' that returns the domain and range of a+ function type. Fails with 'Nothing' if the type to be destructed isn't a+ primitive function type.+-}+destFunTy :: HOLType -> Maybe (HOLType, HOLType)+destFunTy (TyAppIn (TyPrim "fun" _) [ty1, ty2]) = Just (ty1, ty2)+destFunTy _ = Nothing++{-|+ Returns the type of term. Fails with a special type, @tyBottom@, if the type+ is poorly constructed; this keeps the function total without requiring the+ use of an additional guard type like 'Maybe'.++ In practice, this type will never be seen provided the kernel is not modified+ to expose the internal constructors for terms.+-}+typeOf :: HOLTerm -> HOLType+typeOf (VarIn _ ty) = ty+typeOf (ConstIn _ ty _) = ty+typeOf (CombIn x _) =+ case destType $ typeOf x of+ Just (_, _ : ty : _) -> ty+ _ -> tyBottom+typeOf (AbsIn (VarIn _ ty) tm) =+ TyAppIn tyOpFun [ty, typeOf tm]+typeOf AbsIn{} = tyBottom+typeOf (TyAbsIn tv tb) = UTypeIn tv $ typeOf tb+typeOf (TyCombIn t ty) =+ case typeOf t of+ (UTypeIn tv tbody) -> typeSubst [(tv, ty)] tbody+ _ -> tyBottom++{-+ Stateless HOL Type Primitives+-}+-- | Predicate for type operator variables.+isTypeOpVar :: TypeOp -> Bool+isTypeOpVar TyOpVar{} = True+isTypeOpVar _ = False++{-| + Constructs a primitive type operator of a given name and arity. Primitive+ type operators are used to represent constant, but undefined, types.+-}+newPrimTypeOp :: String -> Int -> TypeOp+newPrimTypeOp = TyPrim++{-|+ Constructs a type operator variable of a given name. Note that type+ operator arities are not stored, only inferred from the context where the+ operator is used.++ The parser makes an attempt to guarantee that all instances of a type operator+ in a term have the same arity. The same protection is not provided for terms+ that are manually constructed.+-}+mkTypeOpVar :: String -> TypeOp+mkTypeOpVar = TyOpVar++{-| + Destructs a type operator, returning its name and arity. Note that we use -1 + to indicate the arity of a type operator variable since that information is + not carried.+-}+destTypeOp :: TypeOp -> (String, Int)+destTypeOp (TyOpVar name) = (name, -1)+destTypeOp (TyPrim name arity) = (name, arity)+destTypeOp (TyDefined name arity _) = (name, arity)++-- | Alias to the primitive boolean type operator.+{-# INLINEABLE tyOpBool #-}+tyOpBool :: TypeOp+tyOpBool = TyPrim "bool" 0+-- Used for error cases in type checking only. Not exported.+{-# INLINEABLE tyOpBottom #-}+tyOpBottom :: TypeOp+tyOpBottom = TyPrim "_|_" 0+-- | Alias to the primitive function type operator.+{-# INLINEABLE tyOpFun #-}+tyOpFun :: TypeOp+tyOpFun = TyPrim "fun" 2++{-|+ Constructs a type application from a provided type operator and list of type+ arguments. Fails with 'Left' in the following cases:++ * A type operator variable is applied to zero arguments.++ * A type operator's arity disagrees with the length of the argument list.+-}+tyApp :: TypeOp -> [HOLType] -> Either String HOLType+tyApp tyOp@TyOpVar{} [] = + Left $ "tyApp: " ++ show tyOp ++ ": TyOpVar applied to zero args."+tyApp tyOp@TyOpVar{} args = Right $ TyAppIn tyOp args+tyApp tyOp args =+ let (_, arity) = destTypeOp tyOp in+ if arity == length args + then Right $ TyAppIn tyOp args+ else Left $ "tyApp: " ++ show tyOp ++ ": wrong number of arguments."++-- | Returns the list of all type operator variables in a type.+typeOpVars :: HOLType -> [TypeOp]+typeOpVars (TyAppIn op@TyOpVar{} args) = foldr (union . typeOpVars) [op] args+typeOpVars (UTypeIn _ tbody) = typeOpVars tbody+typeOpVars _ = []++-- | Returns the list of all type operator variables in a list of types.+catTypeOpVars :: [HOLType] -> [TypeOp]+catTypeOpVars = foldr (union . typeOpVars) []++-- substitution of type operator variables for other type operators.+-- Note that replacement with another type operator variable is allowed+typeOpSubst :: [(TypeOp, TypeOp)] -> HOLType -> HOLType+typeOpSubst [] t = t+typeOpSubst tyopenv t =+ tyOpSubstRec (filter validSubst tyopenv) t+ where tyOpSubstRec :: [(TypeOp, TypeOp)] -> HOLType -> HOLType+ tyOpSubstRec tyopins (TyAppIn op args) =+ let args' = map (tyOpSubstRec tyopins) args in+ case tryFind (\ (tp, tr) ->+ if tp /= op ||+ snd (destTypeOp tr) /= length args + then Nothing+ else Just tr) tyopins of+ Nothing -> TyAppIn op args'+ Just op' -> TyAppIn op' args'+ tyOpSubstRec tyopins (UTypeIn tv tbody) =+ UTypeIn tv $ tyOpSubstRec tyopins tbody+ tyOpSubstRec _ ty = ty++-- instantiation of type operator variables with universal types.+typeOpInst :: [(TypeOp, HOLType)] -> HOLType -> HOLType+typeOpInst [] t = t+typeOpInst tyopenv t = tyOpInstRec (filter validSubst tyopenv) t+ where arityOf :: HOLType -> Maybe Int+ arityOf ty = return (length . fst) <*> destUTypes ty ++ tyOpInstRec :: [(TypeOp, HOLType)] -> HOLType -> HOLType+ tyOpInstRec tyopins ty@(TyAppIn op args) =+ let args' = map (tyOpInstRec tyopins) args in+ case tryFind (\ (tp, tr) ->+ if tp /= op || + arityOf tr /= (Just $ length args) + then Nothing+ else destUTypes tr) tyopins of+ Nothing -> TyAppIn op args'+ Just (rtvs, rtbody)+ | isSmall rtbody -> typeSubst (zip rtvs args') rtbody+ | otherwise -> ty+ tyOpInstRec tyopins (UTypeIn tv tbody) =+ if any (\ (x, ty) -> tv `elem` tyVars ty && + x `elem` typeOpVars tbody) tyopins+ -- test for name capture, renaming instances of tv if necessary + then let tvbs = typeOpVars tbody+ tvpatts = map fst tyopins+ tvrepls = catTyVars . mapMaybe (`lookup` tyopins) $+ intersect tvbs tvpatts+ tv' = variantTyVar tvrepls tv in+ UTypeIn tv' . tyOpInstRec tyopins $ + typeSubst [(tv, tv')] tbody+ else UTypeIn tv $ tyOpInstRec tyopins tbody+ tyOpInstRec _ ty = ty+ + +{- + HOL2P Type Primitives+-}++-- | Predicate for universal types.+isUType :: HOLType -> Bool+isUType UTypeIn{} = True+isUType _ = False++{-|+ Predicate for small types. Returns 'True' if all type variables in the type+ are constrained to be small and the type contains no universal types; returns + 'False' otherwise. +-}+isSmall :: HOLType -> Bool+isSmall (TyVarIn small _) = small+isSmall (TyAppIn _ args) = all isSmall args+isSmall UTypeIn{} = False++{-|+ Constructs a universal type of a given bound type and body type. Fails with+ 'Left' if the bound type is not a small, type variable.+-}+mkUType :: HOLType -> HOLType -> Either String HOLType+mkUType tv@(TyVarIn True _) tybody = Right $ UTypeIn tv tybody+mkUType _ _ = Left "mkUType"++{-|+ Constructs a compound universal type given a list of bound types and a body. Fails with 'Left' if any internal call to 'mkUType' fails.+-}+mkUTypes :: [HOLType] -> HOLType -> Either String HOLType+mkUTypes = flip (foldrM mkUType)++{-|+ Constructs a compound universal type from a type operator variable and a given+ number of bound variables, i.e. + + > uTypeFromTypeOpVar _T n === % 'A1 ... 'An. ('A1, ..., 'An)_T ++ Fails with 'Left' in the following cases:++ * @n<=0@ which would result in the application of a type operator to an+ empty list of type arguments.++ * The type operator argument is not a variable. +-}+uTypeFromTypeOpVar :: TypeOp -> Int -> Either String HOLType+uTypeFromTypeOpVar s@TyOpVar{} n+ | n > 0 = + let tvs = map (\ x -> TyVarIn True $ 'A' : show x) [1 .. n] in+ mkUTypes tvs =<< tyApp s tvs+ | otherwise = + Left "uTypeFromTypeOpVar: must have a positive number of bound types."+uTypeFromTypeOpVar _ _ = + Left "uTypeFromTypeOpVar: type operator not a variable."++{-|+ Constructs a small type from a given type by constraining all of the type+ variables in the type to be small. Fails with 'Left' if the type contains+ any universal types.+-}+mkSmall :: HOLType -> Either String HOLType+mkSmall (TyVarIn _ s) = Right $ TyVarIn True s+mkSmall (TyAppIn s tvs) = liftM (TyAppIn s) $ mapM mkSmall tvs+mkSmall UTypeIn{} = Left "mkSmall"++{-| + Destructs a universal type, returning its bound type and body type. Fails+ with 'Nothing' if the provided type is not universally quantified.+-}+destUType :: HOLType -> Maybe (HOLType, HOLType)+destUType (UTypeIn tv ty) = Just (tv, ty)+destUType _ = Nothing++{-|+ Destructs a compound universal type, returning the list of bound variables+ and the final body type. Fails if the provided type is not universally+ quantified.+-} +destUTypes :: HOLType -> Maybe ([HOLType], HOLType)+destUTypes (UTypeIn tv tb) = Just $ destUTypesRec ([tv], tb)+ where destUTypesRec :: ([HOLType], HOLType) -> ([HOLType], HOLType)+ destUTypesRec (acc, UTypeIn tv' tb') = destUTypesRec (acc++[tv'], tb')+ destUTypesRec res = res+destUTypes _ = Nothing++-- | Predicate to test if a type contains a universal type at any level.+containsUType :: HOLType -> Bool+containsUType TyVarIn{} = False+containsUType (TyAppIn _ args) = any containsUType args+containsUType UTypeIn{} = True++{-|+ Renames a type variable to avoid sharing a name with any of a given list of+ type variables. Note that this function is both smallness presserving and+ respecting. Returns the original type if it's not a type variable.+-}+variantTyVar :: [HOLType] -> HOLType -> HOLType+variantTyVar avoid tv@(TyVarIn small name)+ | tv `elem` avoid = variantTyVar avoid . TyVarIn small $ name ++ "'"+ | otherwise = tv+variantTyVar _ ty = ty++{-|+ Renames a list of type variables to avoid sharing a name with any of a given+ list of type variables. As each type variable is processed it is added to the+ list of avoids such that the resultant list of type variables are all uniquely+ named.+-}+variantTyVars :: [HOLType] -> [HOLType] -> [HOLType]+variantTyVars _ [] = []+variantTyVars avoid (tv:tvs) =+ let tv' = variantTyVar avoid tv in+ tv : variantTyVars (tv':avoid) tvs+++-- Documentation copied from HaskHOL.Core.Prims++{-$ViewPatterns+ The primitive data types of HaskHOL are implemented using view patterns in+ order to simulate private data types:++ * Internal constructors are hidden to prevent manual construction of terms.++ * View constructors (those of 'HOLTypeView', 'HOLTermView', 'HOLThmView') are+ exposed to enable pattern matching. ++ * View patterns, as defined by instances of the 'view' function from the + @Viewable@ class, provide a conversion between the two sets of constructors.+-}++{-$HOLTypes+ The following data types combined provide the definition of HOL types in + HaskHOL.++ The primary data type, 'HOLType', follows closely from the + simply typed lambda calculus approach used in John Harrison's HOL Light + system. ++ There are two principle changes to Harrison's implementation:++ 1. Type operators have been introduced, via the 'TypeOp' data type, to + facilitate a stateless logical kernel following from Freek Wiedijk's + Stateless HOL system.++ 2. Universal types and type operator variables have been introduced to move+ the logic from simply typed to polymorphic following from Norbert + Voelker's HOL2P system.+-}++{-$TypeDistinction+ In order to keep HaskHOL's type system decidable, we follow the same + \"smallness\" constraint used by HOL2P: type variables that are constrained + to be small cannot be replaced with types that contain either universal types+ or unconstrained type variables. This constraint, in addition to the+ restriction that universal types can only bind small type variables, prevents+ the system from performing a substitution that would result in a higher rank+ type than the system is capable of dealing with. This effectively limits the+ type system to 2nd order polymorphism.++ Voelker elected to rely on syntactic distinction to differentiate between the+ many kinds of type variables (small, unconstrained, and operator); depending + on how it was to be used, the name of a variable was prepended with a special + symbol. Internal to HaskHOL, we elected to replace these syntactic + distinctions with structural ones such that the following hold true:++ * @TyVarIn True \"x\"@ represents the small type variable \"\'x\"++ * @TyVarIn False \"x\"@ represents the unconstrainted type variable \"x\"++ * @TyOpVar \"x\"@ represents the type operator variable \"_x\"++ Note that external to HaskHOL, during I/O of terms, both the parser and+ pretty-printer still rely on the syntactic distinctions introduced by+ Voelker.+-}
+ src/HaskHOL/Core/Lib.hs view
@@ -0,0 +1,1342 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, + StandaloneDeriving, TemplateHaskell #-}++{-|+ Module: HaskHOL.Core.Lib+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module defines or re-exports common utility functions, type classes, + and auxilliary data types used in HaskHOL. The following conventions hold + true:+ * Where possible, we favor re-exporting common functions rather than+ redefining them. + * We favor re-exporting individual functions rather entire modules to reduce+ the number of items in our utility library.+ * We default to the names of functions commonly used by Haskell libraries,+ however, if there's a different name for a function in HOL systems we+ include an alias for it. For example, 'iComb' and 'id'.++ Note that none of the functions in this module depend on data types + introduced by HaskHOL. Utility functions that do have such a dependence are + found in the "HaskHOL.Core.Basics" module.+-}+module HaskHOL.Core.Lib+ ( -- * Function Combinators+ iComb -- :: a -> a+ , kComb -- :: a -> b -> a+ , cComb -- :: (a -> b -> c) -> b -> a -> c+ , wComb -- :: (a -> a -> b) -> a -> b+ , ffComb -- :: (a -> c) -> (b -> d) -> (a, b) -> (c, d)+ , ffCombM -- :: Monad m => (a -> m c) -> (b -> m d) -> (a, b) -> m (c, d)+ , liftM1 -- :: Monad m => (a -> b -> m c) -> m a -> b -> m c+ -- * Basic Operations on Pairs+ , swap -- :: (a, b) -> (b, a)+ , pairMap -- :: (a -> b) -> (a, a) -> (b, b)+ , pairMapM -- :: Monad m => (a -> m b) -> (a, a) -> m (b, b)+ , first -- :: (a -> c) -> (a, b) -> (c, b)+ , firstM -- :: Monad m => (a -> m c) -> (a, b) -> m (c, b)+ , second -- :: (b -> c) -> (a, b) -> (a, c)+ , secondM -- :: Monad m => (b -> m c) -> (a, b) -> m (a, c)+ -- * Basic Operations on Lists+ , tryHead -- :: [a] -> Maybe a+ , tryTail -- :: [a] -> Maybe a+ , tryInit -- :: [a] -> Maybe [a]+ , butLast -- :: [a] -> Maybe [a]+ , tryLast -- :: [a] -> Maybe a+ , tryIndex -- :: [a] -> Int -> Maybe a+ , el -- :: Int -> [a] -> Maybe a+ , rev -- :: [a] -> [a]+ -- * Basic Operations on Association Lists+ , assoc -- :: Eq a => a -> [(a, b)] -> Maybe b+ , revLookup -- :: Eq a => a -> [(b, a)] -> Maybe b+ , revAssoc -- :: Eq a => a -> [(b, a)] -> Maybe b+ , assocd -- :: Eq a => a -> [(a, b)] -> b -> b+ , lookupd -- :: Eq a => a -> [(a, b)] -> b -> b+ , revLookupd -- :: Eq a => a -> [(b, a)] -> b -> b+ , revAssocd -- :: Eq a => a -> [(b, a)] -> b -> b+ -- * Methods for Error Handling+ , can -- :: (Alternative m, Monad m) => (a -> m b) -> a -> m Bool+ , canNot -- :: (Alternative m, Monad m) => (a -> m b) -> a -> m Bool+ , check -- :: (a -> Bool) -> a -> Maybe a+ , note -- :: a -> Maybe b -> Either a b+ , hush -- :: Either a b -> Maybe b+ , fromRight -- :: Either err a -> a+ , fromRightM -- :: MonadPlus m => Either err a -> m a+ , fromJustM -- :: MonadPlus m => Maybe a -> m a+ , LiftOption(..)+ , Note(..)+ -- * Methods for Function Repetition+ , funpow -- :: Int -> (a -> a) -> a -> a+ , funpowM -- :: Monad m => Int -> (a -> m a) -> a -> m a+ , repeatM -- :: (Alternative M, Monad m) => (a -> m a) -> a -> m a+ , map2 -- :: (a -> b -> c) -> [a] -> [b] -> Maybe c+ , map2M -- :: (Monad m, MonadPlus m) => + -- (a -> b -> m c) -> [a] -> [b] -> m c+ , doList -- :: Monad m => (a -> m b) -> [a] -> m ()+ , allpairs -- :: (a -> b -> c) -> [a] -> [b] -> [c]+ -- * Methods for List Iteration+ , itlist -- :: (a -> b -> b) -> [a] -> b -> b+ , itlistM -- :: (F.Foldable t, Monad m) =>+ -- (a -> b -> m b) -> t a -> b -> m b+ , foldrM -- :: (F.Foldable t, Monad m) => + -- (a -> b -> m b) -> b -> t a -> m b+ , revItlist -- :: (a -> b -> b) -> [a] -> b -> b+ , foldlM -- :: (F.Foldable t, Monad m) => + -- (a -> b -> m b) -> a -> t b -> m a+ , tryFoldr1 -- :: (a -> a -> a) -> [a] -> Maybe a+ , endItlist -- :: (a -> a -> a) -> [a] -> Maybe a+ , foldr1M -- :: (Monad m, MonadPlus m) => (a -> a -> m a) -> [a] -> m a+ , foldr2 -- :: (a -> b -> c -> c) -> c -> [a] -> [b] -> Maybe c+ , itlist2 -- :: (a -> b -> c -> c) -> [a] -> [b] -> c -> Maybe c+ , foldr2M -- :: (Monad m, MonadPlus m) => + -- (a -> b -> c -> m c) -> c -> [a] -> [b] -> m c+ , foldl2 -- :: (c -> a -> b -> c) -> c -> [a] -> [b] -> Maybe c+ , revItlist2 -- :: (b -> b -> c -> c) -> [a] -> [b] -> c -> Maybe c+ , foldl2M -- :: (Monad m, MonadPlus m) => + -- (c -> a -> b -> m c) -> c -> [b] -> m c+ -- * Methods for Sorting and Merging Lists+ , sort -- :: Eq a => (a -> a -> Bool) -> [a] -> [a]+ , sortBy -- :: (a -> a -> Ordering) -> [a] -> [a]+ , merge -- :: (a -> a -> Bool) -> [a] -> [a] -> [a]+ , mergesort -- :: forall a. (a -> a -> Bool) -> [a] -> [a]+ -- * Methods for Splitting and Stripping Binary Terms+ , splitList -- :: (b -> Maybe (a, b)) -> b -> ([a], b)+ , splitListM -- :: (Alternative m, Monad m) => + -- (b -> m (a, b)) -> b -> m ([a], b)+ , revSplitList -- :: (a -> Maybe (a, a)) -> a -> (a, [a])+ , revSplitListM -- :: (Alternative m, Monad m) => + -- (b -> m (b, b)) -> b -> m (b, [b])+ , nsplit -- :: (a -> Maybe (a, a)) -> [b] -> a -> Maybe ([a], a)+ , nsplitM -- :: Monad m => (b -> m (b, b)) -> [c] -> b -> m ([b], b)+ , stripList -- :: (a -> Maybe (a, a)) -> a -> [a]+ , stripListM -- :: (Alternative m, Monad m) => + -- (a -> m (a, a)) -> a -> m [a]+ -- * Methods for Searching and Manipulating Lists+ , forall -- :: (a -> Bool) -> [a] -> Bool+ , forall2 -- :: (a -> b -> Bool) -> [a] -> [b] -> Maybe Bool+ , exists -- :: (a -> Bool) -> [a] -> Bool+ , partition -- :: (a -> Bool) -> [a] -> ([a], [a])+ , mapFilter -- :: (a -> Maybe b) -> [a] -> [b]+ , mapFilterM -- :: (Alternative m, Monad m) => (a -> m b) -> [a] -> m [b]+ , find -- :: (a -> Bool) -> [a] -> Maybe a+ , findM -- :: (Monad m, MonadPlus m) => (a -> m Bool) -> [a] -> m a+ , tryFind -- :: (Monad m, MonadPlus m) => (a -> m b) -> [a] -> m b+ , flat -- :: [[a]] -> [a]+ , dropWhileEnd -- :: (a -> Bool) -> [a] -> [a]+ , remove -- :: (a -> Bool) -> [a] -> Maybe (a, [a])+ , trySplitAt -- :: Int -> [a] -> Maybe ([a], [a])+ , chopList -- :: Int -> [a] -> Maybe ([a], [a])+ , elemIndex -- :: Eq a => a -> [a] -> Maybe Int+ , index -- :: Eq a => a -> [a] -> Maybe Int+ , stripPrefix -- :: Eq a => [a] -> [a] -> Maybe [a]+ , uniq -- :: Eq a => [a] -> [a]+ , shareOut -- :: [[a]] -> [b] -> Maybe [[b]]+ -- * Set Operations on Lists+ , mem -- :: Eq a => a -> [a] -> Bool+ , insert -- :: Eq a => a -> [a] -> [a]+ , insertMap -- :: Eq a => a -> b -> [(a, b)] -> [(a, b)]+ , union -- :: Eq a => [a] -> [a] -> [a]+ , unions -- :: Eq a => [[a]] -> [a]+ , intersect -- :: Eq a => [a] -> [a] -> [a]+ , delete -- :: Eq a => a -> [a] -> [a]+ , (\\) -- :: Eq a => [a] -> [a] -> [a]+ , subset -- :: Eq a => [a] -> [a] -> Bool+ , setEq -- :: Eq a => [a] -> [a] -> Bool+ , setify -- :: Eq a => [a] -> [a]+ , nub -- :: Eq a => [a] -> [a]+ -- * Set Operations Parameterized by Predicate+ , mem' -- :: (a -> a -> Bool) -> a -> [a] -> Bool+ , insert' -- :: (a -> a -> Bool) -> a -> [a] -> [a]+ , union' -- :: (a -> a -> Bool) -> [a] -> [a] -> [a]+ , unions' -- :: (a -> a -> Bool) -> [[a]] -> [a]+ , subtract' -- :: (a -> a -> Bool) -> [a] -> [a] -> [a]+ , group' -- :: (a -> a -> Bool) -> [a] -> [[a]]+ , uniq' -- :: Eq a => (a -> a -> Bool) -> [a] -> [a]+ , setify' -- :: Eq a => (a -> a -> Bool) -> (a -> a -> Bool) -> [a] -> [a]+ -- * Operations on \"Num\" Types+ -- $NumAliases+ , num0 -- :: Integer+ , num1 -- :: Integer+ , num2 -- :: Integer+ , num10 -- :: Integer+ , pow2 -- :: Integer -> Integer+ , pow10 -- :: Integer -> Integer+ , numdom -- :: Real a => a -> Rational+ , numerator -- :: Rational -> Integer+ , denominator -- :: Rational -> Integer+ , gcdNum -- :: Integer -> Integer -> Integer+ , lcmNum -- :: Integer -> Integer -> Integer+ , numOfString -- :: (Eq a, Num a) => String -> Maybe a+ -- * Classes for Common \"Language\" Operations+ -- $LangClasses+ , Lang(..)+ , LangSeq(..)+ -- * Miscellaneous Re-exported Libraries+ , module HaskHOL.Core.Lib.Lift {-|+ Re-exports 'deriveLift', 'deriveLiftMany', and 'Lift' to be used with+ our extensible state mechanisms.+ -}+ , module Control.Applicative {-| + Re-exports 'Applicative', 'Alternative', and the utility functions for+ use with the 'HOL' monad.+ -}+ , module Control.DeepSeq {-|+ Re-exports the entirety of the library, but currently only 'NFData' is+ used. Necessary for using the "Criterion" benchmarking library.+ -}+ , module Control.Monad {-|+ Re-exports the entirety of the library for use with the 'HOL' monad.+ -}+ , module Data.Maybe {-|+ Re-exports the entirety of the library. Used primarily to make+ interacting with primitive rules easier at later points in the system.+ -}+ , module Data.Either {-|+ Re-exports the entirety of the library. Used primarily to make+ interacting with primitive rules easier at later points in the system.+ -}+ , module Data.Typeable {-|+ Re-exports the 'Typeable' class name for use in deriving clauses.+ -}+ , module Text.ParserCombinators.Parsec.Expr {-|+ Re-exports the entirety of the library. Used primarily for its 'Assoc'+ data type, but also contains a number of primitives used by the parser.+ -}+ ) where++import HaskHOL.Core.Lib.Lift++-- Libraries re-exported in their entirety, except for applicative+import Control.Applicative hiding (Const, WrappedMonad, WrappedArrow, ZipList)+import Control.DeepSeq+import Control.Monad+import Data.Maybe+import Data.Either+import Data.Typeable (Typeable)+import Text.ParserCombinators.Parsec.Expr++-- Libraries containing Re-exports+import qualified Control.Arrow as A+import qualified Data.Foldable as F+import qualified Data.List as L+import qualified Data.Ratio as R+import qualified Data.Tuple as T++-- Libraries containing utility functions used, but not exported directly+import Numeric (readInt, readHex, readDec)+import Data.Char (digitToInt)++-- combinators++-- | The I combinator. An alias for 'id'.+{-# INLINEABLE iComb #-}+iComb :: a -> a+iComb = id++-- | The K combinator. An alias for 'const'.+{-# INLINEABLE kComb #-}+kComb :: a -> b -> a+kComb = const++-- | The C combinator. An alias for 'flip'.+{-# INLINEABLE cComb #-}+cComb :: (a -> b -> c) -> b -> a -> c+cComb = flip++{-| + The W combinator. Takes a function of arity 2 and applies a single argument+ to it twice.+-}+{-# INLINEABLE wComb #-}+wComb :: (a -> a -> b) -> a -> b+wComb f x = f x x++-- | The FF combinator. An alias for the arrow combinator 'A.***'. +{-# INLINEABLE ffComb #-}+ffComb :: (a -> c) -> (b -> d) -> (a, b) -> (c, d)+ffComb = (A.***)++{-| + The monadic version of the FF combinator. An alias for the arrow combinator+ 'A.***' lifted for 'A.Kleisli' arrows.+-}+{-# INLINEABLE ffCombM #-}+ffCombM :: Monad m => (a -> m c) -> (b -> m d) -> (a, b) -> m (c, d)+ffCombM f g = A.runKleisli $ A.Kleisli f A.*** A.Kleisli g++{-|+ Promotes a function to a monad, but only for its first argument, i.e.+ + > liftM1 f a b === flip f b =<< a+-}+{-# INLINEABLE liftM1 #-}+liftM1 :: Monad m => (a -> b -> m c) -> m a -> b -> m c+liftM1 f a b = flip f b =<< a++-- pair basics++-- | Swaps the order of a pair. A re-export of 'T.swap'.+{-# INLINEABLE swap #-}+swap :: (a, b) -> (b, a)+swap = T.swap++-- | Applies a function to both elements of a pair using the 'A.***' operator.+{-# INLINEABLE pairMap #-}+pairMap :: (a -> b) -> (a, a) -> (b, b)+pairMap f = f A.*** f++-- | The monadic version of 'pairMap'.+{-# INLINEABLE pairMapM #-}+pairMapM :: Monad m => (a -> m b) -> (a, a) -> m (b, b)+pairMapM f = f `ffCombM` f++{-|+ Applies a function only to the first element of a pair. A re-export of + 'A.first'.+-}+{-# INLINEABLE first #-}+first :: (a -> c) -> (a, b) -> (c, b)+first = A.first++-- | A monadic version of 'first' lifted for 'A.Kleisli' arrows.+{-# INLINEABLE firstM #-}+firstM :: Monad m => (a -> m c) -> (a, b) -> m (c, b)+firstM = A.runKleisli . A.first . A.Kleisli++{-| + Applies a function only to the second element of a pair. A re-export of + 'A.second'.+-}+{-# INLINEABLE second #-}+second :: (b -> c) -> (a, b) -> (a, c)+second = A.second++-- | A monadic version of 'second' lifted for 'A.Kleisli' arrows.+{-# INLINEABLE secondM #-}+secondM :: Monad m => (b -> m c) -> (a, b) -> m (a, c)+secondM = A.runKleisli . A.second . A.Kleisli++-- list basics+{-| + A safe version of 'head'. Fails with 'Nothing' when trying to take the head+ of an empty list.+-}+tryHead :: [a] -> Maybe a+tryHead (x:_) = Just x+tryHead _ = Nothing++{-| + A safe version of 'tail'. Fails with 'Nothing' when trying to take the tail+ of an empty list.+-}+tryTail :: [a] -> Maybe [a]+tryTail (_:t) = Just t+tryTail _ = Nothing++{-|+ A safe version of 'init'. Fails with 'Nothing' when trying to drop the last+ element of an empty list.+-}+tryInit :: [a] -> Maybe [a]+tryInit (_:[]) = Just []+tryInit (x:xs) = do xs' <- tryInit xs+ return (x:xs')+tryInit _ = Nothing++-- | An alias to 'tryInit' for HOL users more familiar with this name.+{-# INLINEABLE butLast #-}+butLast :: [a] -> Maybe [a]+butLast = tryInit++{-| + A safe version of 'last'. Fails with 'Nothing' when trying to take the last+ element of an empty list.+-}+tryLast :: [a] -> Maybe a+tryLast (x:[]) = Just x+tryLast (_:xs) = tryLast xs+tryLast _ = Nothing++{-| + A safe version of 'index'. Fails with 'Nothing' if the selected index does+ not exist.+-}+tryIndex :: [a] -> Int -> Maybe a+tryIndex xs n+ | n >= 0 = tryHead $ drop n xs+ | otherwise = Nothing++{-|+ An alias to 'tryIndex' for HOL users more familiar with this name. Note that+ the order of the arguments is flipped.+-}+{-# INLINEABLE el #-}+el :: Int -> [a] -> Maybe a+el = flip tryIndex++-- | An alias to 'reverse' for HOL users more familiar with this name.+{-# INLINEABLE rev #-}+rev :: [a] -> [a]+rev = reverse++-- association lists+-- | An alias to 'lookup' for HOL users more familiar with this name.+{-# INLINEABLE assoc #-}+assoc :: Eq a => a -> [(a, b)] -> Maybe b+assoc = lookup++{-| + A version of 'lookup' where the search is performed against the second element+ of the pair instead of the first. Still fails with 'Nothing' if the desired+ value is not found.+-}+revLookup :: Eq a => a -> [(b, a)] -> Maybe b+revLookup _ [] = Nothing+revLookup x ((f, s):as)+ | x == s = Just f+ | otherwise = revLookup x as++-- | An alias to 'revLookup' for HOL users who are more familiar with this name.+{-# INLINEABLE revAssoc #-}+revAssoc :: Eq a => a -> [(b, a)] -> Maybe b+revAssoc = revLookup++-- | A version of 'lookup' that defaults to a provided value rather than fail.+lookupd :: Eq a => a -> [(a, b)] -> b -> b+lookupd x xs b = fromMaybe b $ lookup x xs++-- | An alias to 'lookupd' for HOL users who are more familiar with this name.+{-# INLINEABLE assocd #-}+assocd :: Eq a => a -> [(a, b)] -> b -> b+assocd = lookupd++{-| + A version of 'revLookup' that defaults to a provided value rather than fail.+-}+revLookupd :: Eq a => a -> [(b, a)] -> b -> b+revLookupd x xs b = fromMaybe b $ revLookup x xs++{-| + An alias to 'revLookupd' for HOL users who are more familiar with this name.+-}+{-# INLINEABLE revAssocd #-}+revAssocd :: Eq a => a -> [(b, a)] -> b -> b+revAssocd = revLookupd++-- error handling and checking+{-| + Returns a boolean value indicating whether a monadic computation succeeds or+ fails. The '<|>' operator is used for branching.+-}+can :: (Alternative m, Monad m) => (a -> m b) -> a -> m Bool+can f x = (f x >> return True) <|> return False++{-| + The opposite of 'can'. Functionally equivalent to ++ > \ f -> liftM not . can f+-}+canNot :: (Alternative m, Monad m) => (a -> m b) -> a -> m Bool+canNot f x = (f x >> return False) <|> return True++{-| + Checks if a predicate succeeds for a provided value, returning that value+ guarded by a 'Maybe' type if so.+-} +check :: (a -> Bool) -> a -> Maybe a+check p x+ | p x = Just x+ | otherwise = Nothing++-- | Takes a default error value to convert a 'Maybe' type to an 'Either' type.+note :: a -> Maybe b -> Either a b+note l Nothing = Left l+note _ (Just r) = Right r++{-| + Suppresses the error value of an 'Either' type to convert it to a 'Maybe' + type.+-}+hush :: Either a b -> Maybe b+hush (Left _) = Nothing+hush (Right r) = Just r++{-|+ An analogue of 'fromJust' for the 'Either' type. Fails with 'error' when+ provided a 'Left' value, so take care only to use it in cases where you know + you are working with a 'Right' value or are catching exceptions. +-}+fromRight :: Either err a -> a+fromRight (Right res) = res+fromRight _ = error "fromRight"++{-|+ A version of 'fromRight' that maps 'Left' values to 'mzero' rather than+ failing.+-}+fromRightM :: MonadPlus m => Either err a -> m a+fromRightM (Right res) = return res+fromRightM _ = mzero++{-|+ A version of 'fromJust' that maps 'Nothing' values to 'mzero' rather than+ failing.+-}+fromJustM :: MonadPlus m => Maybe a -> m a+fromJustM (Just res) = return res+fromJustM _ = mzero++infixr 1 #<<+infixl 4 <#>+{-|+ The 'LiftOption' class provides an infix operator to more cleanly apply the+ 'fromJustM' and 'fromRightM' methods to a value that will be passed to a+ monadic computation.+-}+class Monad m => LiftOption l m where+ {-| + Used to lift an option value, i.e. 'Maybe' or 'Either', so that it can be+ passed as an argument to a monadic computation.+ -}+ liftO :: l a -> m a++ -- | A version of '=<<' composed with 'liftO' for the right argument.+ (#<<) :: (a -> m b) -> l a -> m b+ l #<< r = l =<< liftO r++ -- | A version of '<=<' composed with 'liftO' for the right argument.+ (<#<) :: (b -> m c) -> (a -> l b) -> a -> m c+ (<#<) l r x = l =<< liftO (r x)++ -- | A version of 'liftM1' composed with 'liftO' for the right argument.+ (<#>) :: (a -> b -> m c) -> l a -> b -> m c+ l <#> r = liftM1 l $ liftO r++instance MonadPlus m => LiftOption Maybe m where+ liftO = fromJustM++instance MonadPlus m => LiftOption (Either a) m where+ liftO = fromRightM++infix 0 <?>+{-| + The 'Note' class provides an ad hoc way of tagging an error case with a+ string.+-}+class (Alternative m, Monad m) => Note m where+ {-| + Used to annotate more precise error messages. Replaces the '<|>' operator + in cases such as + + > ... <|> fail "..."+ -}+ (<?>) :: m a -> String -> m a++ {-|+ Replaces the common pattern ++ > m >>= \ cond -> if cond then fail "..."+ + The default case is defined in terms of 'empty' and '<?>'.+ -}+ failWhen :: m Bool -> String -> m ()+ failWhen m str =+ do cond <- m+ when cond empty <?> str++instance Note (Either String) where+ job <?> str = job <|> Left str++-- repetition of a functions+{-| + Repeatedly applies a function to an argument @n@ times. Rather than fail,+ the original argument is returned when @n<=0@.+-}+funpow :: Int -> (a -> a) -> a -> a+funpow n f x+ | n <= 0 = x+ | otherwise = funpow (n - 1) f (f x)++-- | The monadic version of 'funpow'.+funpowM :: Monad m => Int -> (a -> m a) -> a -> m a+funpowM n f x+ | n <= 0 = return x+ | otherwise = funpowM (n - 1) f =<< f x++{-| + Repeatedly applies a monadic computation to an argument until there is a + failure. The '<|>' operator is used for branching.+-}+repeatM :: (Alternative m, Monad m) => (a -> m a) -> a -> m a+repeatM f x = (repeatM f =<< f x) <|> return x+++{-| + A safe version of a list map for functions of arity 2. Fails with 'Nothing'+ if the two lists are of different lengths.+-}+map2 :: (a -> b -> c) -> [a] -> [b] -> Maybe [c]+map2 _ [] [] = Just []+map2 f (x:xs) (y:ys) =+ do zs <- map2 f xs ys+ return $! f x y : zs+map2 _ _ _ = Nothing++{-| + The monadic version of 'map2'. Fails with 'mzero' if the two lists are of+ different lengths.+-}+map2M :: (Monad m, MonadPlus m) => (a -> b -> m c) -> [a] -> [b] -> m [c]+map2M _ [] [] = return []+map2M f (x:xs) (y:ys) =+ do h <- f x y+ t <- map2M f xs ys+ return (h : t)+map2M _ _ _ = mzero++{-|+ Map a monadic function over a list, ignoring the results. A re-export of + 'mapM_'.+-}+doList :: Monad m => (a -> m b) -> [a] -> m ()+doList = mapM_++-- all pairs arrising from applying a function over two lists+{-|+ Produces a list containing the results of applying a function to all possible + combinations of arguments from two lists. Rather than failing if the lists+ are of different lengths, iteration is shortcutted to end when the left most+ list is null.+-}+allpairs :: (a -> b -> c) -> [a] -> [b] -> [c]+allpairs _ [] _ = []+allpairs f (h:t) l2 = foldr (\ x a -> f h x : a) (allpairs f t l2) l2++-- list iteration+{-| + An alias to 'foldr' for HOL users more familiar with this name. Note that the+ order of the list and base case arguments is flipped.+-}+{-# INLINEABLE itlist #-}+itlist :: (a -> b -> b) -> [a] -> b -> b+itlist f = flip (foldr f)++-- | The monadic version of 'itlist'.+itlistM :: (F.Foldable t, Monad m) => (a -> b -> m b) -> t a -> b -> m b+itlistM f = flip (F.foldrM f)++-- | The monadic version of 'foldr'. A re-export of 'F.foldrM'.+{-# INLINEABLE foldrM #-}+foldrM :: (F.Foldable t, Monad m) => (a -> b -> m b) -> b -> t a -> m b+foldrM = F.foldrM++{-|+ An alias to 'foldl' for HOL users more familiar with this name. Note that the+ order of the list and base case arguments is flipped, as is the order of the+ arguments to the function.+-}+{-# INLINEABLE revItlist #-}+revItlist :: (a -> b -> b) -> [a] -> b -> b+revItlist f = flip (foldl $ flip f)++-- | The monadic version of 'foldl'. A re-export of 'F.foldlM'.+{-# INLINEABLE foldlM #-}+foldlM :: (F.Foldable t, Monad m) => (a -> b -> m a) -> a -> t b -> m a+foldlM = F.foldlM++{-| + A safe version of 'foldr1'. Fails with 'Nothing' if an empty list is provided+ as an argument.+-}+tryFoldr1 :: (a -> a -> a) -> [a] -> Maybe a+tryFoldr1 _ [] = Nothing+tryFoldr1 _ (x:[]) = Just x+tryFoldr1 f (x:xs) = liftM (f x) $ tryFoldr1 f xs++-- | An alias to 'tryFoldr1' for HOL users more familiar with this name.+{-# INLINEABLE endItlist #-}+endItlist :: (a -> a -> a) -> [a] -> Maybe a+endItlist = tryFoldr1++{-| + The monadic version of 'foldr1'. Fails with 'mzero' if an empty list is+ provided as an argument.+-}+foldr1M :: (Monad m, MonadPlus m) => (a -> a -> m a) -> [a] -> m a+foldr1M _ [] = mzero+foldr1M _ [x] = return x+foldr1M f (h:t) = f h =<< foldr1M f t++{-| + A safe version of a right, list fold for functions of arity 2. Fails with+ 'Nothing' if the two lists are of different lengths.+-}+foldr2 :: (a -> b -> c -> c) -> c -> [a] -> [b] -> Maybe c+foldr2 _ b [] [] = Just b+foldr2 f b (x:xs) (y:ys) =+ do b' <- foldr2 f b xs ys+ return $! f x y b'+foldr2 _ _ _ _ = Nothing++{-|+ An alias to 'foldr2' for HOL users more familiar with this name. Note that+ the order of the two list arguments and the base case argument is flipped.+-}+{-# INLINE itlist2 #-}+itlist2 :: (a -> b -> c -> c) -> [a] -> [b] -> c -> Maybe c+itlist2 f xs ys b = foldr2 f b xs ys++{-|+ The monadic version of 'foldr2'. Fails with 'mzero' if the two lists are+ of different lengths.+-}+foldr2M :: (Monad m, MonadPlus m) => + (a -> b -> c -> m c) -> c -> [a] -> [b] -> m c+foldr2M _ b [] [] = return b+foldr2M f b (h1:t1) (h2:t2) = f h1 h2 =<< foldr2M f b t1 t2+foldr2M _ _ _ _ = mzero++{-|+ A safe version of a left, list fold for functions of arity 2. Fails with+ 'Nothing' if the two lists are of different lengths.+-}+foldl2 :: (c -> a -> b -> c) -> c -> [a] -> [b] -> Maybe c+foldl2 _ b [] [] = Just b+foldl2 f b (x:xs) (y:ys) =+ foldl2 f (f b x y) xs ys +foldl2 _ _ _ _ = Nothing++{-|+ An alias to 'foldl2' for HOL users more familiar with this name. Note that+ the order of the two list arguments and base case argument is flipped, as is+ the order of the arguments to the provided function.+-}+{-# INLINEABLE revItlist2 #-}+revItlist2 :: (a -> b -> c -> c) -> [a] -> [b] -> c -> Maybe c+revItlist2 f xs ys b = foldl2 (\ z x y -> f x y z) b xs ys++{-|+ The monadic version of 'foldl2'. Fails with 'mzero' if the two lists are+ of different lengths.+-}+foldl2M :: (Monad m, MonadPlus m) => + (c -> a -> b -> m c) -> c -> [a] -> [b] -> m c+foldl2M _ b [] [] = return b+foldl2M f b (h1:t1) (h2:t2) =+ do b' <- f b h1 h2+ foldl2M f b' t1 t2+foldl2M _ _ _ _ = mzero++-- sorting and merging of lists++{-|+ Sorts a list using a partitioning predicate to build an implied ordering.+ If @p@ is the predicate and @x \`p\` y@ and @not (y \`p\` x)@ are true then + @x@ will be in front of @y@ in the sorted list.+-}+sort :: Eq a => (a -> a -> Bool) -> [a] -> [a]+sort _ [] = []+sort f (piv:rest) =+ let (r, l) = partition (f piv) rest in+ sort f l ++ (piv : sort f r)++{-| + A more traditional sort using an 'Ordering' relationship between elements. A+ re-export of 'L.sortBy'.+-}+{-# INLINEABLE sortBy #-}+sortBy :: (a -> a -> Ordering) -> [a] -> [a]+sortBy = L.sortBy++{-|+ Merges two lists using a partitioning predicate to build an implied ordering.+ See 'sort' for more information on how the predicate affects the order of the+ resultant list.+-}+merge :: (a -> a -> Bool) -> [a] -> [a] -> [a]+merge _ [] l2 = l2+merge _ l1 [] = l1+merge ord l1@(x:xs) l2@(y:ys)+ | ord x y = x : merge ord xs l2+ | otherwise = y : merge ord l1 ys++{-|+ Sorts a list using a partitioning predicate to build an implied ordering;+ uses 'merge' internally. See 'sort' for more information on how the predicate+ affects the order of the resultant list.+-}+mergesort :: forall a. (a -> a -> Bool) -> [a] -> [a]+mergesort _ [] = []+mergesort ord l = mergepairs [] $ map (: []) l+ where mergepairs :: [[a]] -> [[a]] -> [a]+ mergepairs (x:[]) [] = x+ mergepairs xs [] = mergepairs [] xs+ mergepairs xs (y:[]) = mergepairs (y:xs) []+ mergepairs xs (y1:y2:ys) = mergepairs (merge ord y1 y2 : xs) ys++-- iterative term splitting and stripping via destructor+{-|+ Repeatedly applies a binary destructor function to a term until failure.+ + Application is forward, or left-associative, such that for a term of the form+ @x1 \`f\` x2 \`f\` b@ calling this function with a destructor for @f@ will+ produce the result @([x1, x2], b)@.+-}+splitList :: (b -> Maybe (a, b)) -> b -> ([a], b)+splitList f x = + case f x of+ Just (l, r) -> + let (ls, res) = splitList f r in+ (l:ls, res)+ Nothing -> ([], x)++-- | The monadic version of 'splitList'.+splitListM :: (Alternative m, Monad m) => (b -> m (a, b)) -> b -> m ([a], b)+splitListM f x = + (do (l, r) <- f x+ (ls, res) <- splitListM f r+ return (l:ls, res))+ <|> return ([], x)++{-|+ Repeatedly applies a binary destructor function to a term until failure.+ + Application is reverse, or right-associative, such that for a term of the form+ @x1 \`f\` x2 \`f\` b@ calling this function with a destructor for @f@ will+ produce the result @(f, [x1, x2 \`f\` b])@.+-}+revSplitList :: forall a. (a -> Maybe (a, a)) -> a -> (a, [a])+revSplitList f = recSplit []+ where recSplit :: [a] -> a -> (a, [a])+ recSplit ls y = + case f y of+ Just (l, r) -> recSplit (r:ls) l+ Nothing -> (y, ls)++-- | The monadic version of 'revSplitList'.+revSplitListM :: forall m b. (Alternative m, Monad m) => + (b -> m (b, b)) -> b -> m (b, [b])+revSplitListM f = rsplist []+ where rsplist :: [b] -> b -> m (b, [b])+ rsplist ls y = + (do (l, r) <- f y+ rsplist (r:ls) l)+ <|> return (y, ls)+ +{-|+ Repeatedly applies a binary destructor function to a term for every element+ in a provided list.+ + Application is reverse, or right-associative, such that for a term of the form+ @f x1 (f x2 ...(f xn b))@ calling this function with a destructor for @f@ and+ a list @l@ will produce the result @([x1 .. xk], f x(k+1) ...(f xn b))@ where + @k@ is the length of list @l@.+-}+nsplit :: (a -> Maybe (a, a)) -> [b] -> a -> Maybe ([a], a)+nsplit _ [] x = return ([], x)+nsplit dest (_:cs) x =+ do (l, r) <- dest x+ (ll, y) <- nsplit dest cs r+ return (l:ll, y)++-- | The monadic version of 'nsplit'.+nsplitM :: Monad m => (b -> m (b, b)) -> [c] -> b -> m ([b], b)+nsplitM _ [] x = return ([], x)+nsplitM dest (_:n) x = + do (l, r) <- dest x+ (ll, y) <- nsplitM dest n r+ return (l:ll, y)++{-|+ Repeatedly applies a binary destructor function to a term until failure.+ + Application is forward, or left-associative, such that for a term of the form+ @x1 \`f\` x2 \`f\` x3@ calling this function with a destructor for @f@ will+ produce the result @[x1, x2, x3]@.+-}+stripList :: forall a. (a -> Maybe (a, a)) -> a -> [a]+stripList dest x = strip x []+ where strip :: a -> [a] -> [a]+ strip x' acc =+ case dest x' of+ Just (l, r) -> strip l $ strip r acc+ Nothing -> x' : acc++-- | The monadic version of 'stripList'.+stripListM :: forall m a. (Alternative m, Monad m) => + (a -> m (a, a)) -> a -> m [a]+stripListM dest x = strip x []+ where strip :: a -> [a] -> m [a]+ strip x' acc =+ (do (l, r) <- dest x'+ strip l =<< strip r acc)+ <|> return (x' : acc)+++-- miscellaneous list methods++-- | An alias to 'all' for HOL users who are more familiar with this name.+{-# INLINEABLE forall #-}+forall :: (a -> Bool) -> [a] -> Bool+forall = all++{-| + A version of 'all' for predicates of arity 2. Iterates down two lists+ simultaneously with 'map2', using 'and' to combine the results.+-}+forall2 :: (a -> b -> Bool) -> [a] -> [b] -> Maybe Bool+forall2 f xs = liftM and . map2 f xs++-- | An alias to 'any' for HOL users who are more familiar with this name.+{-# INLINEABLE exists #-}+exists :: (a -> Bool) -> [a] -> Bool+exists = any++{-| + Separates a list of elements using a predicate. A re-export of 'L.partition'.+-}+{-# INLINEABLE partition #-}+partition :: (a -> Bool) -> [a] -> ([a], [a])+partition = L.partition++-- | An alias to 'mapMaybe' for HOL users more familiar with this name.+{-# INLINEABLE mapFilter #-}+mapFilter :: (a -> Maybe b) -> [a] -> [b]+mapFilter = mapMaybe++{-| + The monadic version of 'mapFilter'. The '(<|>)' operator is used for + branching.+-}+mapFilterM :: (Alternative m, Monad m) => (a -> m b) -> [a] -> m [b]+mapFilterM _ [] = return []+mapFilterM f (x:xs) =+ do xs' <- mapFilterM f xs+ (do x' <- f x+ return (x':xs'))+ <|> return xs'++-- | A re-export of 'L.find'.+{-# INLINEABLE find #-}+find :: (a -> Bool) -> [a] -> Maybe a+find = L.find++{-| + The monadic version of 'find'. Fails if the monadic predicate does. Also + fails with 'mzero' if an empty list is provided.+-}+findM :: (Monad m, MonadPlus m) => (a -> m Bool) -> [a] -> m a+findM _ [] = mzero+findM f (x:xs) =+ do b <- f x+ if b+ then return x+ else findM f xs++{-|+ An alternative monadic version of 'find' where the predicate is a monadic+ computation not necessarily of a boolean return type. Returns the result of+ the first successful application of the predicate to an element of the list.+ Fails with 'mzero' if called on an empty list. ++ Note that 'mplus' is used for branching instead of '<|>' to minimize the + constraint type; for the vast majority of monads these two functions should be+ identical anyway.+-}+tryFind :: (Monad m, MonadPlus m) => (a -> m b) -> [a] -> m b+tryFind _ [] = mzero+tryFind f (x:xs) = f x `mplus` tryFind f xs++-- | An alias to 'concat' for HOL users who are more familiar with this name.+{-# INLINEABLE flat #-}+flat :: [[a]] -> [a]+flat = concat++{-| + Drops elements from the end of a list while a predicate is true. A re-export+ of 'L.dropWhileEnd'.+-}+{-# INLINEABLE dropWhileEnd #-}+dropWhileEnd :: (a -> Bool) -> [a] -> [a]+dropWhileEnd = L.dropWhileEnd++{-| + Separates the first element of a list that satisfies a predicate. Fails with+ 'Nothing' if no such element is found.+-}+remove :: (a -> Bool) -> [a] -> Maybe (a, [a])+remove _ [] = Nothing+remove p (h:t)+ | p h = Just (h, t)+ | otherwise =+ do (y, n) <- remove p t+ return (y, h:n)++{-|+ A safe version of 'splitAt'. Fails with 'Nothing' if a split is attempted+ at an index that doesn't exist.+-}+trySplitAt :: Int -> [a] -> Maybe ([a], [a])+trySplitAt n l+ | n < 0 = Nothing+ | n == 0 = Just ([], l)+ | otherwise = + case l of+ [] -> Nothing+ (x:xs) -> do (m, l') <- trySplitAt (n - 1) xs+ return (x:m, l')++-- | An alias to 'trySplitAt' for HOL users more familiar with this name+{-# INLINEABLE chopList #-}+chopList :: Int -> [a] -> Maybe ([a], [a])+chopList = trySplitAt++{-|+ Returns the first index where an element appears in list. Fails with + 'Nothing' if no such element is found. A re-export of 'L.elemIndex'.+-}+{-# INLINEABLE elemIndex #-}+elemIndex :: Eq a => a -> [a] -> Maybe Int+elemIndex = L.elemIndex++-- | An alias to 'elemIndex' for HOL users more familiar with this name.+{-# INLINEABLE index #-}+index :: Eq a => a -> [a] -> Maybe Int+index = elemIndex++{-|+ Drops the given prefix from a list. Fails with 'Nothing' if there is no such+ prefix. A re-export of 'L.stripPrefix'.+-}+{-# INLINEABLE stripPrefix #-}+stripPrefix :: Eq a => [a] -> [a] -> Maybe [a]+stripPrefix = L.stripPrefix++-- | Removes adjacent, equal elements from a list.+uniq :: Eq a => [a] -> [a]+uniq (x:y:t) =+ let t' = uniq t in+ if x == y then t' else x : t'+uniq l = l++{-| + Partitions a list into a list of lists matching the structure of the first + argument. For example:+ @shareOut [[1, 2], [3], [4, 5]] \"abcde\" === [\"ab\", \"c\", \"de\"]@+-}+shareOut :: [[a]] -> [b] -> Maybe [[b]]+shareOut [] _ = Just []+shareOut (p:ps) bs = + do (l, r) <- chopList (length p) bs+ ls <- shareOut ps r+ return (l : ls)++-- set operations on lists+-- | An alias to 'elem' for HOL users who are more familiar with this name.+{-# INLINEABLE mem #-}+mem :: Eq a => a -> [a] -> Bool+mem = elem++{-| + Inserts an item into a list if it would be a unique element.++ Important note: This insert is unordered, unlike the 'L.insert' in the+ "Data.List" module.+-}+insert :: Eq a => a -> [a] -> [a]+insert x l+ | x `elem` l = l+ | otherwise = x : l++{-|+ Inserts, or updates, a key value pair in an association list.++ Note that this insert is unordered, but uniqueness preserving.+-}+insertMap :: Eq a => a -> b -> [(a, b)] -> [(a, b)]+insertMap key v [] = [(key, v)]+insertMap key v (x@(key', _):xs)+ | key == key' = (key, v) : xs+ | otherwise = x : insertMap key v xs++{-|+ Unions two list maintaining uniqueness of elements.++ Important note: This union is unordered, unlike the 'L.union' in the+ "Data.List" module.+-}+union :: Eq a => [a] -> [a] -> [a]+union l1 l2 = foldr insert l2 l1++-- | Unions a list of lists using 'union'.+unions :: Eq a => [[a]] -> [a]+unions = foldr union []++-- | Finds the intersection of two lists. A re-export of 'L.intersect'.+{-# INLINEABLE intersect #-}+intersect :: Eq a => [a] -> [a] -> [a]+intersect = L.intersect++-- | Removes an item from a list. A re-export of 'L.delete'.+{-# INLINEABLE delete #-}+delete :: Eq a => a -> [a] -> [a]+delete = L.delete ++-- | Subtracts one list from the other. A re-export of 'L.\\'.+{-# INLINEABLE (\\) #-}+(\\) :: Eq a => [a] -> [a] -> [a]+(\\) = (L.\\)++-- | Tests if the first list is a subset of the second.+subset :: Eq a => [a] -> [a] -> Bool+subset xs ys = all (`elem` ys) xs++-- | A test for set equality using 'subset'.+setEq :: Eq a => [a] -> [a] -> Bool+setEq l1 l2 = subset l1 l2 && subset l2 l1++-- | Converts a list to a set by removing duplicates. A re-export of 'L.nub'.+{-# INLINEABLE nub #-}+nub :: Eq a => [a] -> [a]+nub = L.nub++-- | An alias to 'nub' for HOL users more familiar with this name.+{-# INLINEABLE setify #-}+setify :: Eq a => [a] -> [a]+setify = nub++-- set operations parameterized by equality+{-|+ A version of 'mem' where the membership test is an explicit predicate, rather+ than a strict equality test.+-}+mem' :: (a -> a -> Bool) -> a -> [a] -> Bool+mem' _ _ [] = False+mem' eq a (x:xs) = eq a x || mem' eq a xs++{-|+ A version of 'insert' where the uniqueness test is an explicit predicate, + rather than a strict equality test.+-}+insert' :: (a -> a -> Bool) -> a -> [a] -> [a]+insert' eq x xs+ | mem' eq x xs = xs+ | otherwise = x : xs++{-|+ A version of 'union' where the uniqueness test is an explicit predicate, + rather than a strict equality test.+-}+union' :: (a -> a -> Bool) -> [a] -> [a] -> [a]+union' eq xs ys = foldr (insert' eq) ys xs++{-|+ A version of 'unions' where the uniqueness test is an explicit predicate, + rather than a strict equality test.+-}+unions' :: (a -> a -> Bool) -> [[a]] -> [a]+unions' eq = foldr (union' eq) []++{-|+ A version of 'subtract' where the uniqueness test is an explicit predicate, + rather than a strict equality test.+-}+subtract' :: (a -> a -> Bool) -> [a] -> [a] -> [a]+subtract' eq xs ys = filter (\ x -> not $ mem' eq x ys) xs++{-|+ Groups neighbors in a list together based on a predicate. A re-export of+ 'L.groupBy'.+-}+{-# INLINEABLE group' #-}+group' :: (a -> a -> Bool) -> [a] -> [[a]]+group' = L.groupBy++-- | A version of 'uniq' that eliminates elements based on a provided predicate.+uniq' :: Eq a => (a -> a -> Bool) -> [a] -> [a]+uniq' eq l@(x:t@(y:_)) =+ let t' = uniq' eq t in+ if x `eq` y then t'+ else if t' == t then l else x:t'+uniq' _ l = l++{-| + A version of 'setify' that eliminates elements based on a provided predicate.+-}+setify' :: Eq a => (a -> a -> Bool) -> (a -> a -> Bool) -> [a] -> [a]+setify' le eq xs = uniq' eq $ sort le xs++-- some useful functions on "num" types+{-$NumAliases++ The following are aliases to frequently used values and functions for+ arbitrary-precision integers. Typically, they are used when converting to and+ from numbers in the implementation language and the logic language. The+ aliases clarify the intended use and saves us from having lots of explicit+ type annotations to force 'Integer' values.+-}+-- | > 0 :: Integer+{-# INLINEABLE num0 #-}+num0 :: Integer+num0 = 0++-- | > 1 :: Integer+{-# INLINEABLE num1 #-}+num1 :: Integer+num1 = 1++-- | > 2 :: Integer+{-# INLINEABLE num2 #-}+num2 :: Integer+num2 = 2++-- | > 10 :: Integer+{-# INLINEABLE num10 #-}+num10 :: Integer+num10 = 10++-- | > x ^ (2 :: Integer)+{-# INLINEABLE pow2 #-}+pow2 :: Integer -> Integer+pow2 x = x ^ (2 :: Integer)++-- | > x ^ (10 :: Integer)+{-# INLINEABLE pow10 #-}+pow10 :: Integer -> Integer+pow10 x = x ^ (10 :: Integer)++{-| + Converts a real number to a rational representation. + An alias to 'toRational' for HOL users more familiar with this name.+-}+{-# INLINEABLE numdom #-}+numdom :: Real a => a -> Rational+numdom = toRational++-- | Returns the numerator of a rational number. A re-export of 'R.numerator'.+{-# INLINEABLE numerator #-}+numerator :: Rational -> Integer+numerator = R.numerator++{-| + Returns the denominator of a rational number. A re-export of 'R.denominator'.+-}+{-# INLINEABLE denominator #-}+denominator :: Rational -> Integer+denominator = R.denominator++{-| + Finds the least common denominator between two numbers. An alias to 'gcd' for+ HOL users more familiar with this name.+-}+{-# INLINEABLE gcdNum #-}+gcdNum :: Integer -> Integer -> Integer+gcdNum = gcd++{-|+ Finds the least common multiplier between two numbers. An alias to 'lcm' for+ HOL users more familiar with this name.+-}+{-# INLINEABLE lcmNum #-}+lcmNum :: Integer -> Integer -> Integer+lcmNum = lcm++{-|+ Converts a string representation of a number to an appropriate instance of+ the 'Num' class. Fails with 'Nothing' if the conversion cannot be performed.++ Note: The following prefixes are valid:++ * @0x@ - number read as a hexidecimal value++ * @0b@ - number read as a binary value++ * Any other prefix causes the number to be read as a decimal value+-}+numOfString :: forall a. (Eq a, Num a) => String -> Maybe a+numOfString s =+ case res of+ [(x, "")] -> Just x+ _ -> Nothing+ where res :: [(a, String)]+ res = case s of+ ('0':'x':s') -> readHex s'+ ('0':'b':s') -> readInt 2 (`elem` "01") digitToInt s'+ _ -> readDec s++-- language type classes+{-$LangClasses+ The following two classes are used as an ad hoc mechanism for sharing+ \"language\" operations between different types. For example, both tactics+ and conversions share a number of the same operations. Rather than having + multiple functions, such as @thenTac@ and @thenConv@, we've found it easier to+ have a single, polymorphic function to use, '_THEN'.++ The sequencing operations are seperated in their own class, 'LangSeq', because+ their tactic instances have a reliance on the boolean logic theory. Rather + than unecessarily propogate this prerequisite for all members of the 'Lang' + class, we elected to separate them.+-}+{-| + The 'Lang' class defines common language operations and combinators not based+ on sequencing.+-}+class Lang a where+ {-| + A primitive language operation that always fails. Typically this is+ written using 'throw'.+ -}+ _FAIL :: String -> a+ -- | An instance of '_FAIL' with a fixed failure string.+ _NO :: a+ -- | A primitive language operation that always succeeds.+ _ALL :: a+ {-| + A language combinator for branching based on failure. The language+ equivalent of the '<|>' operator.+ -}+ _ORELSE :: a -> a -> a+ -- | A language combinator that performs the first operation in a list.+ _FIRST :: [a] -> a+ {-| + A language combinator that fails if the wrapped operation doesn't invoke+ some change, i.e. a tactic fails to change the goal state.+ -}+ _CHANGED :: a -> a+ {-| + A language combinator that prevents the wrapped operation from having an+ effect if it fails. The language equivalent of the backtracking 'try' + operator.+ -}+ _TRY :: a -> a++{-|+ The 'LangSeq' class defines common language operations and combinators based+ on sequencing. See the note at the top of this section for more details as+ to why these are separated on their own.+-}+class LangSeq a where+ -- | A language combinator that sequences operations.+ _THEN :: a -> a -> a+ {-| + A language combinator that repeatedly applies a language operation until + failure.+ -}+ _REPEAT :: a -> a+ {-| + A language combinator that performs every operation in a list + sequentially.+ -}+ _EVERY :: [a] -> a++-- Not currently part of the Parsec library, so we define it here+-- both orphan instances+deriving instance Eq Assoc++deriveLift ''Assoc
+ src/HaskHOL/Core/Lib/Lift.hs view
@@ -0,0 +1,158 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE FlexibleInstances, MagicHash, OverlappingInstances,+ TemplateHaskell, TypeSynonymInstances #-}++{-|+ Module: HaskHOL.Core.Lib.Lift+ Copyright: (c) Ian Lynagh 2006+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module is a re-export of the th-lift library originally written by Ian+ Lynagh and maintained by Mathieu Boespflug. A very minor change was made by+ Evan Austin in order to facilitate derivation of lift instances for quantified+ type constructors.++ The decision to include this source as part of the HaskHOL system, rather than+ import the original library, was made to facilitate the above change and to+ sever HaskHOL's only dependence on a non-Haskell Platform library.+-}++{-+ The original copyright is included in its entirety below, as required by BSD3:++ Copyright (c) Ian Lynagh.+ All rights reserved.++ Redistribution and use in source and binary forms, with or without+ modification, are permitted provided that the following conditions+ are met:+ 1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+ 2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.+ 3. The names of the author may not be used to endorse or promote+ products derived from this software without specific prior written+ permission.++ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+ SUCH DAMAGE.+-}++module HaskHOL.Core.Lib.Lift + ( deriveLift' -- :: Info -> Q [Dec]+ , deriveLift -- :: Name -> Q [Dec]+ , deriveLiftMany -- :: [Name] -> Q [Dec]+ , module TH {-|+ Re-exports 'Lift' for the purpose of writing type signatures external to+ this module.+ -}+ ) where++import GHC.Exts+import Language.Haskell.TH+import Language.Haskell.TH.Syntax+import qualified Language.Haskell.TH.Syntax as TH (Lift)+import Control.Monad ((<=<))+++-- | Derive Lift instances for the given datatype.+deriveLift :: Name -> Q [Dec]+deriveLift = deriveLift' <=< reify++-- | Derive Lift instances for many datatypes.+deriveLiftMany :: [Name] -> Q [Dec]+deriveLiftMany = deriveLiftMany' <=< mapM reify++-- | Obtain Info values through a custom reification function. This is useful+-- when generating instances for datatypes that have not yet been declared.+deriveLift' :: Info -> Q [Dec]+deriveLift' = fmap (:[]) . deriveLiftOne++deriveLiftMany' :: [Info] -> Q [Dec]+deriveLiftMany' = mapM deriveLiftOne++deriveLiftOne :: Info -> Q Dec+deriveLiftOne i =+ case i of+ TyConI (DataD dcx n vsk cons _) ->+ liftInstance dcx n (map unTyVarBndr vsk) (map doCons cons)+ TyConI (NewtypeD dcx n vsk con _) ->+ liftInstance dcx n (map unTyVarBndr vsk) [doCons con]+ _ -> fail ("deriveLift: unhandled: " ++ pprint i)+ where liftInstance dcx n vs cases =+ instanceD (ctxt dcx vs) (conT ''Lift `appT` typ n vs) [funD 'lift cases]+ typ n = foldl appT (conT n) . map varT+ ctxt dcx = fmap (dcx ++) . cxt . map liftPred+ unTyVarBndr (PlainTV v) = v+ unTyVarBndr (KindedTV v _) = v+ liftPred n = classP ''Lift [varT n]++doCons :: Con -> Q Clause+doCons (NormalC c sts) = do+ let ns = zipWith (\_ i -> 'x' : show i) sts [(0::Integer)..]+ con = [| conE c |]+ args = [ [| lift $(varE (mkName n)) |] | n <- ns ]+ e = foldl (\e1 e2 -> [| appE $e1 $e2 |]) con args+ clause [conP c (map (varP . mkName) ns)] (normalB e) []+doCons (RecC c sts) = doCons $ NormalC c [(s, t) | (_, s, t) <- sts]+doCons (InfixC sty1 c sty2) = do+ let con = [| conE c |]+ left = [| lift $(varE (mkName "x0")) |]+ right = [| lift $(varE (mkName "x1")) |]+ e = [| infixApp $left $con $right |]+ clause [infixP (varP (mkName "x0")) c (varP (mkName "x1"))] (normalB e) []+-- ECA+doCons (ForallC _ _ con) = doCons con++instance Lift Name where+ lift (Name occName nameFlavour) = [| Name occName nameFlavour |]++instance Lift OccName where+ lift n = [| mkOccName $(lift $ occString n) |]++instance Lift PkgName where+ lift n = [| mkPkgName $(lift $ pkgString n) |]++instance Lift ModName where+ lift n = [| mkModName $(lift $ modString n) |]++instance Lift NameFlavour where+ lift NameS = [| NameS |]+ lift (NameQ moduleName) = [| NameQ moduleName |]+ lift (NameU i) = [| case $( lift (I# i) ) of+ I# i' -> NameU i' |]+ lift (NameL i) = [| case $( lift (I# i) ) of+ I# i' -> NameL i' |]+ lift (NameG nameSpace pkgName moduleName)+ = [| NameG nameSpace pkgName moduleName |]++instance Lift NameSpace where+ lift VarName = [| VarName |]+ lift DataName = [| DataName |]+ lift TcClsName = [| TcClsName |]++-- These instances should really go in the template-haskell package.++instance Lift () where+ lift _ = [| () |]++instance Lift Rational where+ lift x = return (LitE (RationalL x))++--ECA+instance Lift String where+ lift = liftString
+ src/HaskHOL/Core/Parser.hs view
@@ -0,0 +1,92 @@+{-|+ Module: HaskHOL.Core.Parser+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module defines the parsers for 'HOLType's and 'HOLTerm's.++ It also re-exports the related benign flags, theory extension mechanisms, + and type/term elaborators.++ For examples of the parsers and elaborators in use see the + "HaskHOL.Core.TermRep" module.+-}+module HaskHOL.Core.Parser+ ( -- * Parser Data Types+ PreTerm+ , PreType+ -- * Type Elaboration Flags+ , FlagIgnoreConstVarstruct(..)+ , FlagTyInvWarning(..)+ , FlagTyOpInvWarning(..)+ , FlagAddTyAppsAuto(..)+ -- * Extensible Parser Operators+ , parseAsBinder -- :: String -> HOL Theory thry ()+ , parseAsTyBinder -- :: String -> HOL Theory thry ()+ , parseAsPrefix -- :: String -> HOL Theory thry ()+ , parseAsInfix -- :: (String, (Int, Assoc)) -> HOL Theory thry ()+ , unparseAsBinder -- :: String -> HOL Theory thry ()+ , unparseAsTyBinder -- :: String -> HOL Theory thry ()+ , unparseAsPrefix -- :: String -> HOL Theory thry ()+ , unparseAsInfix -- :: String -> HOL Theory thry ()+ , binders -- :: HOLContext thry -> [String]+ , tyBinders -- :: HOLContext thry -> [String]+ , prefixes -- :: HOLContext thry -> [String]+ , infixes -- :: HOLContext thry -> [(String, (Int, Assoc))]+ , parsesAsBinder -- :: String -> HOLContext thry -> Bool+ , parsesAsTyBinder -- :: String -> HOLContext thry -> Bool+ , isPrefix -- :: String -> HOLContext thry -> Bool+ , getInfixStatus -- :: String -> HOLContext thry -> + -- Maybe (Int, Assoc)+ -- * Overloading and Interface Mapping+ , makeOverloadable -- :: String -> HOLType -> HOL Theory thry ()+ , removeInterface -- :: String -> HOL Theory thry ()+ , reduceInterface -- :: String -> HOLTerm -> HOL Theory thry ()+ , overrideInterface -- :: String -> HOLTerm -> HOL Theory thry ()+ , overloadInterface -- :: String -> HOLTerm -> HOL Theory thry ()+ , prioritizeOverload -- :: HOLType -> HOL Theory thry ()+ , getInterface -- :: HOLContext thry -> [(String, (String, HOLType))]+ , getOverloads -- :: HOLContext thry -> [(String, HOLType)]+ -- * Type Abbreviations+ , newTypeAbbrev -- :: String -> HOLType -> HOL Theory thry ()+ , removeTypeAbbrev -- :: String -> HOL Theory thry ()+ , typeAbbrevs -- :: HOLContext thry -> [(String, HOLType)]+ -- * Hidden Constant Mapping + , hideConstant -- :: String -> HOL Theory thry ()+ , unhideConstant -- :: String -> HOL Theory thry ()+ , getHidden -- :: HOLContext thry -> [String]+ -- * Elaboration Functions+ , tyElab -- :: PreType -> HOL cls thry HOLTerm+ , elab -- :: PreTerm -> HOL cls thry HOLTerm+ -- * Parsing Functions+ , holTypeParser -- :: String -> HOLContext thry -> Either ParseError PreType+ , holTermParser -- :: String -> HOLContext thry -> Either ParseError PreTerm+ -- * Type/Term Representation Conversions+ , HOLTypeRep(..)+ , HOLTermRep(..)+ ) where++import HaskHOL.Core.State++import HaskHOL.Core.Parser.Lib+import HaskHOL.Core.Parser.TypeParser+import HaskHOL.Core.Parser.TermParser+import HaskHOL.Core.Parser.Elab+import HaskHOL.Core.Parser.Rep++runHOLParser :: MyParser thry a -> String -> HOLContext thry -> + Either ParseError a+runHOLParser parser input ctxt =+ runParser parser (ctxt, []) "" input++-- | Parser for 'HOLTerm's.+holTermParser :: String -> HOLContext thry -> Either ParseError PreTerm+holTermParser = runHOLParser pterm++-- | Parser for 'HOLType's.+holTypeParser :: String -> HOLContext thry -> Either ParseError PreType+holTypeParser = runHOLParser ptype
+ src/HaskHOL/Core/Parser.hs-boot view
@@ -0,0 +1,9 @@+module HaskHOL.Core.Parser where++import HaskHOL.Core.State++import HaskHOL.Core.Parser.Lib++holTermParser :: String -> HOLContext thry -> Either ParseError PreTerm++holTypeParser :: String -> HOLContext thry -> Either ParseError PreType
+ src/HaskHOL/Core/Parser/Elab.hs view
@@ -0,0 +1,564 @@+{-|+ Module: HaskHOL.Core.Parser.Elab+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module defines the elaborators for types and terms. These elaborators+ convert 'PreType's and 'PreTerm's, as produced by the parsers, into + 'HOLType's and 'HOLTerm's accordingly. This conversion includes local type+ inference for terms.+-}+module HaskHOL.Core.Parser.Elab + ( tyElab -- :: PreType -> HOL cls thry HOLTerm+ , elab -- :: PreTerm -> HOL cls thry HOLTerm+ ) where++import HaskHOL.Core.Lib+import HaskHOL.Core.Kernel+import HaskHOL.Core.State+import HaskHOL.Core.Basics++import HaskHOL.Core.Parser.Lib hiding ((<?>))++{- + PEnv is an environment that carries an association between system type+ variables and their unifiable type. It is defined as a type synonym in case+ we ever need to change its implementation for performance reasons.+-}+type PEnv = [(Int, PreType)]++-- utility functions+destSTV :: PreType -> Maybe Int+destSTV (STyVar n) = Just n+destSTV _ = Nothing++destUTy :: PreType -> Maybe String+destUTy (UTyVar _ x _) = Just x+destUTy _ = Nothing++destPUTy :: PreType -> Maybe (PreType, PreType)+destPUTy (PUTy tv ty) = Just (tv, ty)+destPUTy _ = Nothing++freeSTVS0 :: PreType -> [Int]+freeSTVS0 PTyCon{} = []+freeSTVS0 UTyVar{} = []+freeSTVS0 (STyVar n) = [n]+freeSTVS0 (PTyComb _ args) =+ foldr (\ x y -> freeSTVS0 x `union` y) [] args+freeSTVS0 (PUTy _ tbody) = freeSTVS0 tbody++utyvars :: PreType -> [PreType]+utyvars PTyCon{} = []+utyvars tv@UTyVar{} = [tv]+utyvars STyVar{} = []+utyvars (PTyComb t args) = foldr (union . utyvars) [] $ t : args+utyvars (PUTy tv tbody) = utyvars tbody \\ [tv]++catUTyVars :: [PreType] -> [PreType]+catUTyVars = foldr (union . utyvars) []++variantUTyVar :: [PreType] -> PreType -> PreType+variantUTyVar avoid tv@(UTyVar f name n)+ | tv `elem` avoid = variantUTyVar avoid $ UTyVar f (name ++ "'") n+ | otherwise = tv+variantUTyVar _ tv = tv++mkFunPTy :: PreType -> PreType -> PreType+mkFunPTy pty1 pty2 = PTyComb (PTyCon "fun") [pty1, pty2]++{- + Used to construct a constant that has one or more instantiations provided for+ its type operator variables.+-}+mkTIConst :: String -> HOLType -> HOLTypeEnv -> HOL cls thry HOLTerm+mkTIConst c ty subs =+ (do cty' <- liftM (typeSubst subs) $ getConstType c+ let (mat1Tys, opTys, opOps) = fromJust $ typeMatch cty' ty ([], [], [])+ tys' = map (second $ typeSubst mat1Tys) subs ++ mat1Tys+ mat2 = (tys', opTys, opOps)+ con <- mkConstFull c mat2+ if typeOf con == ty+ then return con+ else mzero)+ <?> "mkTIConst"++-- Constructs a PreTerm representation of a provided integer.+pmkNumeral :: Integer -> PreTerm+pmkNumeral = PComb numeral . pmkNumeralRec + where numPTy :: PreType+ numPTy = PTyComb (PTyCon "num") []+ + numeral, bit0, bit1, t0 :: PreTerm+ numeral = PConst "NUMERAL" $ mkFunPTy numPTy numPTy+ bit0 = PConst "BIT0" $ mkFunPTy numPTy numPTy+ bit1 = PConst "BIT1" $ mkFunPTy numPTy numPTy+ t0 = PConst "_0" numPTy++ pmkNumeralRec :: Integer -> PreTerm+ pmkNumeralRec 0 = t0+ pmkNumeralRec n = + let op = if n `mod` 2 == num0 then bit0 else bit1 in+ PComb op . pmkNumeralRec $ n `div` 1++{- + Checks if a unification for a system type variable is trivial, i.e. unifying+ with itself or a unification between two system type variables already stored+ in the environment.++ Also used to check for cyclic occurances, i.e. a system type variable exists+ as a sub-term of the provided term.+-}+istrivial :: PEnv -> Int -> PreType -> Maybe Bool+istrivial _ _ PTyCon{} = Just False+istrivial env x (STyVar y)+ | y == x = Just True+ | otherwise = + (istrivial env x =<< lookup y env) <|> return False+istrivial _ _ UTyVar{} = Just False+istrivial env x (PTyComb f args) =+ do ys' <- mapM (istrivial env x) $ f : args+ if or ys'+ then Nothing+ else return False+istrivial env x (PUTy _ tbody) =+ do tbody' <- istrivial env x tbody+ if tbody'+ then Nothing+ else return False++-- system type generation+newTypeVar :: HOL cls thry PreType+newTypeVar = return STyVar <*> tickTypeCounter++-- Turn UType term into a non-UType by adding type applications+addTyApps :: PreTerm -> PreType -> HOL cls thry PreTerm+addTyApps tm ty@(PUTy tv tb) =+ do ntv <- newTypeVar+ addTyApps (TyPComb tm ty ntv) $ pretypeSubst [(ntv, tv)] tb+addTyApps tm _ = return tm++-- Check where unification of ty with a UType is potentially possible+unifiableWithUType :: PreType -> PEnv -> Bool+unifiableWithUType ty env+ | ty == dpty = False+ | otherwise = + let tys = solve env ty in+ isJust (destPUTy tys) || isJust (destSTV tys)++-- Get a new instance of a constant's generic type modulo interface+getGenericType :: String -> HOL cls thry HOLType+getGenericType cname =+ do ctxt <- get+ case filter (\ (x, _) -> x == cname) $ getInterface ctxt of+ ((_, (_, ty)):[]) -> return ty+ (_:_:_) -> liftMaybe "getGenericType" . assoc cname $ getOverloads ctxt+ _ -> getConstType cname++{- + Pretype substitution based on HOLType substitution.+ Note that the PTyCon case is comparatively simpler given the extra work done + in the parser.+-}+pretypeSubst :: [(PreType, PreType)] -> PreType -> PreType+pretypeSubst _ ty@PTyCon{} = ty+pretypeSubst tyenv ty@UTyVar{} = revAssocd ty tyenv ty+pretypeSubst _ ty@STyVar{} = ty+pretypeSubst tyenv ty@(PTyComb tyop args) =+ let tyop' = pretypeSubst tyenv tyop + args' = map (pretypeSubst tyenv) args in+ if tyop == tyop' && args == args' then ty+ else case tyop' of+ PUTy{} -> let (rtvs, rtbody) = splitList destPUTy tyop' in+ pretypeSubst (zip args' rtvs) rtbody+ _ -> PTyComb tyop' args'+pretypeSubst tyenv ty@(PUTy tv tbody) =+ let tyenv' = filter (\ (_, x) -> x /= tv) tyenv in+ if null tyenv' then ty+ else if any (\ (t, x) -> tv `elem` utyvars t && + x `elem` utyvars tbody) tyenv'+ then let tvbody = utyvars tbody+ tvpatts = map snd tyenv'+ tvrepls = catUTyVars . map (\ x -> revAssocd x tyenv' x) $ + intersect tvbody tvpatts + tv' = variantUTyVar ((tvbody \\ tvpatts) `union` tvrepls) tv in+ PUTy tv' $ pretypeSubst ((tv', tv):tyenv') tbody+ else PUTy tv $ pretypeSubst tyenv' tbody++-- Apply type unifications+solve :: PEnv -> PreType -> PreType+solve _ pty@PTyCon{} = pty +solve _ pty@UTyVar{} = pty+solve env pty@(STyVar i) =+ fromMaybe pty . liftM (solve env) $ lookup i env+solve env (PTyComb f args) = + PTyComb (solve env f) $ map (solve env) args+solve env (PUTy tv tbod) =+ PUTy tv $ solve env tbod+ +-- Final type checker for preterms+{- + Local reference that tracks if we invent system type (operator) variables.+ Also used to track the smallness constraints of system type variables.+-}+type TypingState = HOLRef TypingRefs+data TypingRefs = TypingRefs+ { stvsTrans :: !Bool+ , stovsTrans :: !Bool+ , smallSTVS :: ![Int]+ }++-- Post typechecking elaboration+tyElabRef :: TypingState -> PreType -> HOL cls thry HOLType+tyElabRef _ (PTyCon s) = mkType s []+tyElabRef ref (PTyComb STyVar{} []) =+ do modifyHOLRef ref $ \ st -> st { stvsTrans = True }+ fail $ "tyElab: system type variable present in type application of " +++ "null arity"+tyElabRef ref (PTyComb (STyVar n) args) =+ do modifyHOLRef ref $ \ st -> st { stovsTrans = True }+ mkType ('?' : show n) =<< mapM (tyElabRef ref) args+tyElabRef ref (PTyComb (UTyVar _ v _) args) =+ mkType v =<< mapM (tyElabRef ref) args+tyElabRef ref (PTyComb (PTyCon s) args) =+ mkType s =<< mapM (tyElabRef ref) args+tyElabRef _ PTyComb{} =+ fail "tyElab: unexpected first argument to type combination"+tyElabRef ref (PUTy (UTyVar _ s 0) tbody) =+ do tbody' <- tyElabRef ref tbody+ liftEither "tyElab: universal type" $+ liftM1 mkUType (mkSmall $ mkVarType s) tbody'+tyElabRef ref (PUTy STyVar{} _) =+ do modifyHOLRef ref $ \ st -> st { stvsTrans = True }+ fail "tyElab: system type variable in universal type."+-- Alternative way to handle this if we don't want to make it an error, +-- per HOL2P:+{-+tyElabRef ref (PUTy tv@(STyVar n) tbody) =+ do modifyHOLRef ref $ \ st -> st { stvsTrans = True }+ warn True "tyElab: system type variable in universal type."+ let tv' = '?' : show n+ tbody' <- tyElabRef ref $ pretypeSubst [(UTyVar True tv' 0, tv)] tbody+ liftMaybe "tyElab: universal type - system type variable case" $+ do tv'' <- mkSmall $ mkVarType tv'+ mkUType tv'' tbody'+-}+tyElabRef _ PUTy{} =+ fail "tyElab: invalid universal type construction."+tyElabRef ref (STyVar n) =+ do modifyHOLRef ref $ \ st -> st { stvsTrans = True }+ let tv = mkVarType $ '?' : show n+ st <- readHOLRef ref+ if n `elem` smallSTVS st+ then liftEither "tyElab: small system type variable" $ mkSmall tv+ else return tv+tyElabRef _ (UTyVar True v _) = + liftEither "tyElab: small user type variable" . mkSmall $ mkVarType v+tyElabRef _ (UTyVar False v _) = return $! mkVarType v++tmElab :: TypingState -> PreTerm -> HOL cls thry HOLTerm+tmElab ref ptm =+ do modifyHOLRef ref $ \ st -> st { stvsTrans = False, stovsTrans = False }+ tm <- tmElabRec ptm+ st <- readHOLRef ref+ flag1 <- getBenignFlag FlagTyInvWarning+ flag2 <- getBenignFlag FlagTyOpInvWarning+ warn (stvsTrans st && flag1) + "warning: inventing type variables"+ warn (stovsTrans st && flag2) + "warning: inventing type operator variables"+ return tm+ where tmElabRec :: PreTerm -> HOL cls thry HOLTerm+ tmElabRec PApp{} =+ fail $ "tmElab: type application present outside of type " +++ "combination term"+ tmElabRec (PVar s pty) = + liftM (mkVar s) $ tyElabRef ref pty+ tmElabRec (PConst s pty) = + mkMConst s =<< tyElabRef ref pty+ tmElabRec (PInst tvis (PConst c pty)) =+ do tvis' <- mapM (\ (ty, x) -> do ty' <- tyElabRef ref ty+ return (mkVarType x, ty')) tvis+ pty' <- tyElabRef ref pty+ mkTIConst c pty' tvis'+ tmElabRec PInst{} =+ fail "tmElab: body of TYINST not a constant."+ tmElabRec (PComb l r) =+ do (l', r') <- pairMapM tmElabRec (l, r)+ liftEither "tmElab" $ mkComb l' r'+ tmElabRec (PAbs v bod) =+ do (v', bod') <- pairMapM tmElabRec (v, bod)+ mkGAbs v' bod'+ tmElabRec (TyPAbs tv t) =+ do tv' <- tyElabRef ref tv+ t' <- tmElabRec t+ liftEither "tmElab" $ mkTyAbs tv' t'+ tmElabRec (TyPComb t _ ti) =+ do t' <- tmElabRec t+ ti' <- tyElabRef ref ti+ liftEither "tmElab" $ mkTyComb t' ti'+ tmElabRec (PAs tm _) = tmElabRec tm++-- retypecheck+preTypeOf :: TypingState -> [(String, PreType)] -> PreTerm -> + HOL cls thry PreTerm+preTypeOf ref senv pretm = + do ty <- newTypeVar + modifyHOLRef ref $ \ st -> st { smallSTVS = [] }+ (tm', _, env) <- typify ty (pretm, senv, []) <?> + "typechecking: initial type assignment"+ env' <- resolveInterface tm' return env <?> + "typechecking: overload resolution"+ solvePreterm env' tm'+ where typify :: PreType -> (PreTerm, [(String, PreType)], PEnv) -> + HOL cls thry (PreTerm, [(String, PreType)], PEnv)+ typify ty (PVar s _, venv, uenv) =+ case lookup s venv of+ Just ty' -> + do flag <- getBenignFlag FlagAddTyAppsAuto+ let tys = if flag then solve uenv ty' else ty'+ if flag && isJust (destPUTy tys) && + not (unifiableWithUType ty uenv)+ then do tys' <- addTyApps (PVar s tys) tys+ typify ty (tys', venv, uenv)+ else do uenv' <- unify uenv (tys, ty)+ return (PVar s tys, [], uenv')+ Nothing -> + case numOfString s of+ Just s' -> + let t = pmkNumeral s' in+ do uenv' <- unify uenv (PTyComb (PTyCon "num") [], ty)+ return (t, [], uenv')+ Nothing ->+ (do s' <- getGenericType s+ hidden <- gets getHidden+ if s `notElem` hidden+ then do flag <- getBenignFlag FlagAddTyAppsAuto+ tys <- pretypeInstance s'+ if flag && isJust (destPUTy tys) &&+ not (unifiableWithUType ty uenv)+ then do ty' <- addTyApps (PVar s tys) tys+ typify ty (ty', venv, uenv)+ else do uenv' <- unify uenv (tys, ty)+ return (PConst s tys, [], uenv')+ else mzero)+ <|> return (PVar s ty, [(s, ty)], uenv)+ typify ty (PInst tvis (PVar c _), _, uenv) =+ do c' <- getGenericType c <?> ("typify: TYINST can only be " ++ + "applied to a constant: " ++ c)+ let cty = pretypeOfType c'+ let tvs = map snd tvis+ rtvs = filter (\ x -> case x of+ UTyVar _ x' _ -> x' `elem` tvs+ _ -> False) $ utyvars cty+ if length rtvs < length tvs+ then let rtvNames = fromJust $ mapM destUTy rtvs+ (missing:_) = filter (`notElem` rtvNames) tvs in+ fail $ "typify: TYINST: type does not contain " ++ + "tyvar " ++ missing+ else let subs = fromJust $ mapM (\ tv -> + do x <- destUTy tv+ x' <- revAssoc x tvis+ return (x', tv)) rtvs+ tvis' = fromJust $ mapM (\ (t, tv) ->+ do x <- destUTy tv+ return (t, x)) subs in+ do ctyrep <- replaceUtvsWithStvs tvs cty+ let cty' = pretypeSubst subs ctyrep+ uenv' <- unify uenv (cty', ty)+ return (PInst tvis' (PConst c cty'), [], uenv')+ typify ty (PComb t (PApp ti), venv, uenv) =+ do ntv <- newTypeVar+ (t', venv1, uenv1) <- typify ntv (t, venv, uenv)+ case solve uenv1 ntv of+ ty'@(PUTy ty1 ty2) -> + do uenv1' <- unify uenv1 + (ty, pretypeSubst [(ti, ty1)] ty2)+ return (TyPComb t' ty' ti, venv1, uenv1')+ _ -> fail $ "typify: Type application argument maybe not " +++ "of universal type: " ++ show t+ typify ty (PComb f x, venv, uenv) =+ do ty'' <- newTypeVar+ let ty' = mkFunPTy ty'' ty+ (f', venv1, uenv1) <- typify ty' (f, venv, uenv)+ (x', venv2, uenv2) <- typify (solve uenv1 ty'') + (x, venv1 ++ venv, uenv1)+ return (PComb f' x', venv1 ++ venv2, uenv2)+ typify ty (ptm@(PAs tm pty), venv, uenv) =+ do flag <- getBenignFlag FlagAddTyAppsAuto+ if flag && isJust (destPUTy pty) && + not (unifiableWithUType ty uenv)+ then do ty' <- addTyApps ptm pty+ typify ty (ty', venv, uenv)+ else do uenv' <- unify uenv (ty, pty)+ typify ty (tm, venv, uenv')+ typify ty (PAbs v bod, venv, uenv) =+ do (ty', ty'') <- case ty of+ PTyComb (PTyCon "fun") [ty', ty''] -> + return (ty', ty'')+ _ -> do ty1 <- newTypeVar+ ty2 <- newTypeVar+ return (ty1, ty2)+ uenv0 <- unify uenv (mkFunPTy ty' ty'', ty)+ (v', venv1, uenv1) <- do (v', venv1, uenv1) <- typify ty' + (v, [], uenv0)+ flag <- getBenignFlag + FlagIgnoreConstVarstruct+ case v' of+ PConst s _ -> return $! + if flag+ then (PVar s ty', [(s, ty')], uenv0)+ else (v', venv1, uenv1)+ _ -> return (v', venv1, uenv1)+ (bod', venv2, uenv2) <- typify ty'' (bod, venv1 ++ venv, uenv1)+ return (PAbs v' bod', venv2, uenv2)+ typify ty (TyPAbs tv bod, venv, uenv) =+ do ntv <- newTypeVar+ (bod', venv1, uenv1) <- typify ntv (bod, venv, uenv)+ uenv2 <- unify uenv1 (PUTy tv $ solve uenv1 ntv, ty)+ return (TyPAbs tv bod', venv1, uenv2)+ typify ty (TyPComb t (PUTy ty1 ty2) ti, venv, uenv) =+ do uenv0 <- unify uenv (pretypeSubst [(ti, ty1)] ty2, ty)+ (t', venv1, uenv1) <- typify (PUTy ty1 ty2) (t, venv, uenv0)+ return (TyPComb t' (PUTy ty1 ty2) ti, venv1, uenv1)+ typify _ (ptm, _, _) =+ fail $ "typify: unexpected preterm at this stage: " ++ show ptm++-- Give system type vars for all free type vars, except those in tys+ replaceUtvsWithStvs :: [String] -> PreType -> HOL cls thry PreType+ replaceUtvsWithStvs tys pty =+ do tyvs' <- mapM subsf $ utyvars pty+ return $! pretypeSubst tyvs' pty + where subsf :: PreType -> HOL cls thry (PreType, PreType)+ subsf tv@(UTyVar f s _) =+ if s `elem` tys then return (tv, tv)+ else do tv'@(STyVar n) <- newTypeVar+ when f $ modifyHOLRef ref + (\ st -> st { smallSTVS = n : smallSTVS st})+ return (tv', tv)+ subsf _ = fail "replaceUtvsWithStvs"++ pretypeInstance :: HOLType -> HOL cls thry PreType+ pretypeInstance = replaceUtvsWithStvs [] . pretypeOfType++-- Type constraint specialization by resolving overloadings+ resolveInterface :: PreTerm -> (PEnv -> HOL cls thry PEnv) -> PEnv -> + HOL cls thry PEnv+ resolveInterface PApp{} _ _ =+ fail "resolveInterface: type application"+ resolveInterface (PComb f x) cont env =+ resolveInterface f (resolveInterface x cont) env+ resolveInterface (PAbs v bod) cont env =+ resolveInterface v (resolveInterface bod cont) env+ resolveInterface (TyPAbs _ bod) cont env =+ resolveInterface bod cont env+ resolveInterface (TyPComb t _ _) cont env =+ resolveInterface t cont env+ resolveInterface PAs{} _ _ =+ fail "resolveInterface: type ascription"+ resolveInterface (PInst _ bod) cont env =+ resolveInterface bod cont env+ resolveInterface PVar{} cont env =+ cont env+ resolveInterface (PConst s ty) cont env =+ do iface <- gets getInterface+ let maps = filter (\ (s', _) -> s' == s) iface+ if null maps + then cont env+ else tryFind (\ (_, (_, ty')) -> + do ty'' <- pretypeInstance ty'+ cont =<< unify env (ty'', ty)) maps++-- Push specialization throughout a preterm+ solvePreterm :: PEnv -> PreTerm -> HOL cls thry PreTerm+ solvePreterm _ PApp{} =+ fail "solvePreterm: type application"+ solvePreterm env (PVar s ty) = return . PVar s $ solve env ty+ solvePreterm env (PComb f x) =+ do (f', x') <- pairMapM (solvePreterm env) (f, x)+ return $! PComb f' x'+ solvePreterm env (PAbs v bod) =+ do (v', bod') <- pairMapM (solvePreterm env) (v, bod)+ return $! PAbs v' bod'+ solvePreterm env (TyPAbs tv bod) =+ liftM (TyPAbs tv) $ solvePreterm env bod+ solvePreterm env (TyPComb t ty ti) =+ let ti' = solve env ti in+ do modifyHOLRef ref $+ \ st -> st { smallSTVS = smallSTVS st `union` freeSTVS0 ti' }+ t' <- solvePreterm env t+ return $! TyPComb t' (solve env ty) ti'+ solvePreterm env (PInst tys bod) =+ liftM (PInst tys) $ solvePreterm env bod+ solvePreterm _ PAs{} =+ fail "solvePreterm: type ascription"+ solvePreterm env (PConst s ty) =+ let tys = solve env ty in+ (do iface <- gets getInterface+ c' <- tryFind (\ (s', (c', ty')) ->+ if s == s'+ then do ty'' <- pretypeInstance ty'+ _ <- unify env (ty'', ty)+ return c'+ else mzero) iface+ pmkCV c' tys)+ <|> (return $! PConst s tys)+ where pmkCV :: String -> PreType -> HOL cls thry PreTerm+ pmkCV name pty = + do cond <- can getConstType name+ return $! if cond then PConst name pty + else PVar name pty++-- Unification of types+ unify :: PEnv -> (PreType, PreType) -> HOL cls thry PEnv+ unify env (ty1, ty2)+ | ty1 == ty2 = return env+ | otherwise = + case (ty1, ty2) of+ (PTyComb f@STyVar{} fargs, PTyComb g gargs) ->+ if length fargs == length gargs+ then foldrM (flip unify) env $ (f, g) : zip fargs gargs+ else fail $ "unify: " ++ show f ++ " WITH " ++ show g+ (PTyComb{}, PTyComb STyVar{} _) ->+ unify env (ty2, ty1)+ (PTyComb f fargs, PTyComb g gargs) ->+ if f == g && length fargs == length gargs+ then foldrM (flip unify) env $ zip fargs gargs+ else fail $ "unify: " ++ show f ++ " WITH " ++ show g+ (PUTy tv1@UTyVar{} tbody1, PUTy tv2@UTyVar{} tbody2) ->+ if tv1 == tv2 then unify env (tbody1, tbody2)+ else let tv = variantUTyVar (utyvars tbody1 `union` + utyvars tbody2) tv1 in+ unify env (pretypeSubst [(tv, tv1)] tbody1, + pretypeSubst [(tv, tv2)] tbody2)+ (STyVar x, t) -> handleSTVS x t+ (t, STyVar x) -> handleSTVS x t+ _ -> fail $ "unify: " ++ show ty1 ++ " WITH " ++ show ty2+ where handleSTVS :: Int -> PreType -> HOL cls thry PEnv+ handleSTVS x t =+ case lookup x env of+ Just x' -> unify env (x', t)+ _ -> if istrivial env x t == Just True+ then return env+ else do st <- readHOLRef ref+ let t' = destSTV t+ when (isJust t' && x `elem` smallSTVS st) $+ modifyHOLRef ref $ \ s -> + s { smallSTVS = fromJust t' : smallSTVS s }+ return $! insertMap x t env++-- | Elaborator for 'PreType's.+tyElab :: PreType -> HOL cls thry HOLType+tyElab pty =+ do ref <- newHOLRef $ TypingRefs False False []+ tyElabRef ref pty++-- | Elaborator and type inference for 'PreTerm's.+elab :: PreTerm -> HOL cls thry HOLTerm+elab ptm = + do ref <- newHOLRef $ TypingRefs False False []+ tmElab ref =<< preTypeOf ref [] ptm
+ src/HaskHOL/Core/Parser/Lib.hs view
@@ -0,0 +1,526 @@+{-# LANGUAGE DeriveDataTypeable, TemplateHaskell, ViewPatterns #-}++{-|+ Module: HaskHOL.Core.Parser.Lib+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module defines or re-exports common utility functions, type classes, + and auxilliary data types used in HaskHOL's parsers. These primarily fall+ three classes of objects:++ * Types and functions used by the parsers.++ * Benign flag and state extensions used by the parsers.++ * Predicates and modifiers for state extensions used by the parsers.++ Note that, because these state extensions were designed to be used with the+ parser, the accessor and predicate functions are written to use 'getExtCtxt' + rather than 'getExt' for convenience.++ To see what is actually exported to the user, see the module + "HaskHOL.Core.Parser".+-}++module HaskHOL.Core.Parser.Lib+ ( -- * Parser Utilities and Types+ PreType(..)+ , PreTerm(..)+ , dpty -- :: PreType+ , pretypeOfType -- :: HOLType -> PreType+ , MyParser+ , myparens+ , mybraces+ , mybrackets+ , mycommaSep1+ , mysemiSep+ , myreserved+ , myidentifier+ , myinteger+ , myoperator+ , myreservedOp+ , choiceOp+ , mywhiteSpace+ -- * Type Elaboration Flags+ , FlagIgnoreConstVarstruct(..)+ , FlagTyInvWarning(..)+ , FlagTyOpInvWarning(..)+ , FlagAddTyAppsAuto(..)+ -- * Extensible Parser Operators+ , parseAsBinder -- :: String -> HOL Theory thry ()+ , parseAsTyBinder -- :: String -> HOL Theory thry ()+ , parseAsPrefix -- :: String -> HOL Theory thry ()+ , parseAsInfix -- :: (String, (Int, Assoc)) -> HOL Theory thry ()+ , unparseAsBinder -- :: String -> HOL Theory thry ()+ , unparseAsTyBinder -- :: String -> HOL Theory thry ()+ , unparseAsPrefix -- :: String -> HOL Theory thry ()+ , unparseAsInfix -- :: String -> HOL Theory thry ()+ , binders -- :: HOLContext thry -> [String]+ , tyBinders -- :: HOLContext thry -> [String]+ , prefixes -- :: HOLContext thry -> [String]+ , infixes -- :: HOLContext thry -> [(String, (Int, Assoc))]+ , parsesAsBinder -- :: String -> HOLContext thry -> Bool+ , parsesAsTyBinder -- :: String -> HOLContext thry -> Bool+ , isPrefix -- :: String -> HOLContext thry -> Bool+ , getInfixStatus -- :: String -> HOLContext thry -> + -- Maybe (Int, Assoc)+ -- * Overloading and Interface Mapping+ , makeOverloadable -- :: String -> HOLType -> HOL Theory thry ()+ , removeInterface -- :: String -> HOL Theory thry ()+ , reduceInterface -- :: String -> HOLTerm -> HOL Theory thry ()+ , overrideInterface -- :: String -> HOLTerm -> HOL Theory thry ()+ , overloadInterface -- :: String -> HOLTerm -> HOL Theory thry ()+ , prioritizeOverload -- :: HOLType -> HOL Theory thry ()+ , getInterface -- :: HOLContext thry -> [(String, (String, HOLType))]+ , getOverloads -- :: HOLContext thry -> [(String, HOLType)]+ -- * Type Abbreviations+ , newTypeAbbrev -- :: String -> HOLType -> HOL Theory thry ()+ , removeTypeAbbrev -- :: String -> HOL Theory thry ()+ , typeAbbrevs -- :: HOLContext thry -> [(String, HOLType)]+ -- * Hidden Constant Mapping + , hideConstant -- :: String -> HOL Theory thry ()+ , unhideConstant -- :: String -> HOL Theory thry ()+ , getHidden -- :: HOLContext thry -> [String]+ -- * Re-export Parsec for convenience reasons+ , module Text.ParserCombinators.Parsec+ ) where++import HaskHOL.Core.Lib+import HaskHOL.Core.Kernel+import HaskHOL.Core.State+import HaskHOL.Core.Basics++import Text.ParserCombinators.Parsec hiding ((<|>))+import Text.ParserCombinators.Parsec.Token+import Text.ParserCombinators.Parsec.Language (emptyDef)++-- new flags and extensions+{-| + Flag to say whether to treat a constant varstruct, i.e. @\\ const . bod@, as+ variable.+-}+newFlag "FlagIgnoreConstVarstruct" True++{-|+ Flag indicating that the user should be warned if a type variable was invented+ during parsing.+-}+newFlag "FlagTyInvWarning" True++{-|+ Flag indicating that the user should be warned if a type operator variable was+ invented during parsing.+-}+newFlag "FlagTyOpInvWarning" True++{-|+ Flag to say whether implicit type applications are to be added during parsing.+-}+newFlag "FlagAddTyAppsAuto" True++newExtension "BinderOps" [| ["\\"] :: [String] |]++newExtension "TyBinderOps" [| ["\\\\"] :: [String] |]++newExtension "PrefixOps" [| [] :: [String] |]++newExtension "InfixOps" + [| [("=", (12, AssocRight))] :: [(String, (Int, Assoc))] |]++newExtension "Interface" [| [] :: [(String, (String, HOLType))] |]++newExtension "Overload" [| [] :: [(String, HOLType)] |]++newExtension "TypeAbbreviations" [| [] :: [(String, HOLType)] |]++newExtension "Hidden" [| [] :: [String] |]++-- | Parsed, but pre-elaborated HOL types.+data PreType+ = PTyCon String+ | UTyVar Bool String Int+ | STyVar Int+ | PTyComb PreType [PreType]+ | PUTy PreType PreType+ deriving (Eq, Show)++-- | Parsed, but pre-elaborated HOL terms.+data PreTerm+ = PVar String PreType+ | PConst String PreType+ | PComb PreTerm PreTerm+ | PAbs PreTerm PreTerm+ | PAs PreTerm PreType+ | PInst [(PreType, String)] PreTerm+ | PApp PreType+ | TyPAbs PreType PreTerm+ | TyPComb PreTerm PreType PreType+ deriving Show++-- | The default 'PreType' to be used as a blank for the type inference engine.+dpty :: PreType+dpty = PTyComb (PTyCon "") []++-- | Converts a 'HOLType' to 'PreType'+pretypeOfType :: HOLType -> PreType+pretypeOfType (view -> TyVar f v) = UTyVar f v 0+pretypeOfType (view -> TyApp tyop args) =+ let (s, n) = destTypeOp tyop+ tyop' = if n == -1 then UTyVar False s (length args) else PTyCon s in+ PTyComb tyop' $ map pretypeOfType args+pretypeOfType (view -> UType tv tb) = + PUTy (pretypeOfType tv) $ pretypeOfType tb+pretypeOfType _ = error "pretypeOfType: incomplete view pattern"++{-| + An alias to a stateful 'CharParser' that carries a 'HOLContext' and list of+ known type operator variables with their arity. This second list is used+ to guarantee that all instances of a type operator variable in a term have+ the same arity.+-}+type MyParser thry a = CharParser (HOLContext thry, [(String, Int)]) a++-- used internally by the numerous parser combinators seen below.+lexer :: TokenParser (HOLContext thry, [(String, Int)])+lexer = makeTokenParser+ (emptyDef+ { reservedNames = ["TYINST", "let", "and", "in", "if", "then", "else"]+ , reservedOpNames = ["%", "_", "'", "->", "+", "#", "^", ":", ";"]+ })++-- | A version of 'parens' for our language.+myparens :: MyParser thry a -> MyParser thry a+myparens = parens lexer++-- | A version of 'braces' for our language.+mybraces :: MyParser thry a -> MyParser thry a+mybraces = braces lexer++-- | A version of 'brackets' for our language.+mybrackets :: MyParser thry a -> MyParser thry a+mybrackets = brackets lexer++-- | A version of 'commaSep1' for our language.+mycommaSep1 :: MyParser thry a -> MyParser thry [a]+mycommaSep1 = commaSep1 lexer++-- | A version of 'semiSep' for our language.+mysemiSep :: MyParser thry a -> MyParser thry [a]+mysemiSep = semiSep lexer++-- | A version of 'reserved' for our language.+myreserved :: String -> MyParser thry ()+myreserved = reserved lexer++-- | A version of 'identifier' for our language.+myidentifier :: MyParser thry String+myidentifier = identifier lexer++-- | A version of 'integer' for our language.+myinteger :: MyParser thry Integer+myinteger = integer lexer++-- | A version of 'operator' for our language.+myoperator :: MyParser thry String+myoperator = operator lexer++-- | A version of 'reservedOp' for our language.+myreservedOp :: String -> MyParser thry ()+myreservedOp = reservedOp lexer++-- | Selects the first matching reserved operator.+choiceOp :: [String] -> MyParser thry String+choiceOp ops = + choice $ map (\ name -> do myreservedOp name+ return name) ops++-- | A version of 'whiteSpace' for our language.+mywhiteSpace :: MyParser thry ()+mywhiteSpace = whiteSpace lexer++-- State Extensions+-- Operators+-- | Specifies a 'String' to be recognized as a term binder by the parser.+parseAsBinder :: String -> HOL Theory thry ()+parseAsBinder op =+ modifyExt (\ (BinderOps ops) -> BinderOps $ op `insert` ops)++-- | Specifies a 'String' to be recognized as a type binder by the parser.+parseAsTyBinder :: String -> HOL Theory thry ()+parseAsTyBinder op =+ modifyExt (\ (TyBinderOps ops) -> TyBinderOps $ op `insert` ops)++-- | Specifies a 'String' to be recognized as a prefix operator by the parser.+parseAsPrefix :: String -> HOL Theory thry ()+parseAsPrefix op =+ modifyExt (\ (PrefixOps ops) -> PrefixOps $ op `insert` ops)++{-| + Specifies a 'String' to be recognized as an infix operator by the parser with+ a given precedence level and associativity.+-}+parseAsInfix :: (String, (Int, Assoc)) -> HOL Theory thry ()+parseAsInfix op =+ modifyExt (\ (InfixOps ops) -> InfixOps $ op `insertInfix` ops)+ where insertInfix :: (String, (Int, Assoc)) -> [(String, (Int, Assoc))] ->+ [(String, (Int, Assoc))]+ insertInfix i@(n, _) is =+ case find (\ (n', _) -> n == n') is of+ Just _ -> is+ _ -> sortBy (\ (_, (x, _)) (_, (y, _)) -> y `compare` x) $ i:is++-- | Specifies a 'String' for the parser to stop recognizing as a term binder.+unparseAsBinder :: String -> HOL Theory thry ()+unparseAsBinder op =+ modifyExt (\ (BinderOps ops) -> BinderOps $ op `delete` ops)++-- | Specifies a 'String' for the parser to stop recognizing as a type binder.+unparseAsTyBinder :: String -> HOL Theory thry ()+unparseAsTyBinder op =+ modifyExt (\ (TyBinderOps ops) -> TyBinderOps $ op `delete` ops)++{-| + Specifies a 'String' for the parser to stop recognizing as a prefix operator.+-}+unparseAsPrefix :: String -> HOL Theory thry ()+unparseAsPrefix op =+ modifyExt (\ (PrefixOps ops) -> PrefixOps $ op `delete` ops)++{-| + Specifies a 'String' for the parser to stop recognizing as an infix operator.+-}+unparseAsInfix :: String -> HOL Theory thry ()+unparseAsInfix op =+ modifyExt (\ (InfixOps ops) -> + InfixOps $ filter (\ (x, _) -> x == op) ops)++-- | Returns all 'String's recognized as term binders by the parser.+binders :: HOLContext thry -> [String]+binders ctxt =+ let (BinderOps ops) = getExtCtxt ctxt in+ ops++-- | Returns all 'String's recognized as type binders by the parser.+tyBinders :: HOLContext thry -> [String]+tyBinders ctxt =+ let (TyBinderOps ops) = getExtCtxt ctxt in+ ops++-- | Returns all 'String's recognized as prefix operators by the parser.+prefixes :: HOLContext thry -> [String]+prefixes ctxt =+ let (PrefixOps ops) = getExtCtxt ctxt in+ ops++{-| + Returns all 'String's recognized as infix operators by the parser along with+ their precedence and associativity pairs.+-} +infixes :: HOLContext thry -> [(String, (Int, Assoc))]+infixes ctxt =+ let (InfixOps ops) = getExtCtxt ctxt in+ ops++-- | Predicate for 'String's recognized as term binders by the parser.+parsesAsBinder :: String -> HOLContext thry -> Bool+parsesAsBinder op = elem op . binders++-- | Predicate for 'String's recognized as term binders by the parser.+parsesAsTyBinder :: String -> HOLContext thry -> Bool+parsesAsTyBinder op = elem op . tyBinders++-- | Predicate for 'String's recognized as prefix operators by the parser.+isPrefix :: String -> HOLContext thry -> Bool+isPrefix op = elem op . prefixes++{-| + Predicate for 'String's recognized as infix operators by the parser. Returns+ a precidence and associativity pair guarded by 'Maybe'.+-}+getInfixStatus :: String -> HOLContext thry -> Maybe (Int, Assoc)+getInfixStatus op = lookup op . infixes++-- Interface+{-|+ Specifies a 'String' that can act as an overloadable identifier within the+ parser. The provided type is the most general type that instances of this+ symbol may have. Throws a 'HOLException' if the given symbol has already been+ declared as overloadable with a different type.++ Note that defining a symbol as overloadable will erase any interface overloads+ that were previously introduced via 'overrideInterface' in order to guarantee+ that all overloads are matchable with their most general type.+-}+makeOverloadable :: String -> HOLType -> HOL Theory thry ()+makeOverloadable s gty =+ do (Overload overs) <- getExt+ case lookup s overs of+ Just ty+ | gty == ty -> return ()+ | otherwise -> + fail "makeOverloadable: differs from existing skeleton"+ _ -> do putExt $ Overload ((s, gty):overs)+ modifyExt (\ (Interface iface) -> + Interface $ filter (\ (x, _) -> x /= s) iface)++-- | Removes all instances of an overloaded symbol from the interface.+removeInterface :: String -> HOL Theory thry ()+removeInterface sym =+ modifyExt (\ (Interface iface) -> Interface $ + filter (\ (x, _) -> x /= sym) iface)++{-| + Removes a specific instance of an overloaded symbol from the interface. + Throws a 'HOLException' if the provided term is not a constant or varible term+ representing an instance of the overloaded symbol.+-}+reduceInterface :: String -> HOLTerm -> HOL Theory thry ()+reduceInterface sym tm =+ do namty <- liftMaybe "reduceInterface: term not a constant or variable" $ + destConst tm <|> destVar tm+ modifyExt (\ (Interface iface) -> Interface $ + (sym, namty) `delete` iface)++{-|+ Removes all existing overloads for a given symbol and replaces them with a+ single, specific instance. Throws a 'HOLException' if the provided term is+ not a constant or variable term representing an instance of the overloaded+ symbol.++ Note that because 'overrideInterface' can introduce at most one overload for+ a symbol it does not have to be previously defined as overloadable via + 'makeOverloadable'. However, if the symbol is defined as overloadable then + the provided term must have a type that is matchable with the symbol's most+ general type.+-}+overrideInterface :: String -> HOLTerm -> HOL Theory thry ()+overrideInterface sym tm =+ do namty <- liftMaybe "overrideInterface: term not a constant or variable" $+ destConst tm <|> destVar tm+ let m = modifyExt (\ (Interface iface) -> + let iface' = filter (\ (x, _) -> x /= sym) iface in+ Interface $ (sym, namty) : iface')+ (Overload overs) <- getExt+ case sym `lookup` overs of+ Just gty -> if isNothing $ typeMatch gty (snd namty) ([], [], [])+ then fail $ "overrideInterface: " +++ "not an instance of type skeleton"+ else m+ _ -> m++{-|+ Introduces a new overload for a given symbol. Throws a 'HOLException' in the+ following cases:++ * The symbol has not previously been defined as overloadable via + 'makeOverloadable'.+ + * The provided term is not a constant or variable term representing a + specific instance of the overloaded symbol.++ * The provided term does not have a type that is matchable with the+ overloadable symbol's specified most general type.++ Note that specifying an overload that already exists will move it to the front+ of the interface list, effectively prioritizing it. This behavior is utilized+ by 'prioritizeOverload'.+-}+overloadInterface :: String -> HOLTerm -> HOL Theory thry ()+overloadInterface sym tm =+ do (Overload overs) <- getExt+ gty <- liftMaybe ("overloadInstace: symbol " ++ sym ++ + " is not overloadable.") $ lookup sym overs+ namty <- liftMaybe "overloadInstance: term not a constant or variable" $ + destConst tm <|> destVar tm+ if isNothing $ typeMatch gty (snd namty) ([], [], [])+ then fail "overloadInstance: not an instance of type skeleton"+ else modifyExt (\ (Interface iface) -> + let iface' = (sym, namty) `delete` iface in+ Interface $ (sym, namty) : iface')++{-|+ Specifies a type to prioritize when the interface is used to overload a + symbol. Note that this applies to all overloads in the system whose match+ with the specified most general type involves the provided type. + Prioritization is done by redefining overloads via 'overloadInterface'.+-}+prioritizeOverload :: HOLType -> HOL Theory thry ()+prioritizeOverload ty =+ do (Overload overs) <- getExt+ mapM_ (\ (s, gty) -> + (do (Interface iface) <- getExt+ let (n, t') = fromJust $ + tryFind (\ (s', x@(_, t)) ->+ if s' /= s then Nothing+ else do (tys, _, _) <- typeMatch gty t+ ([], [], [])+ _ <- ty `revLookup` tys+ return x) iface+ overloadInterface s $ mkVar n t')+ <|> return ()) overs++-- | Returns the list of all currently defined interface overloads.+getInterface :: HOLContext thry -> [(String, (String, HOLType))]+getInterface ctxt =+ let (Interface iface) = getExtCtxt ctxt in iface++{-| + Returns the list of all overloadable symbols paired with their most generic + types.+-}+getOverloads :: HOLContext thry -> [(String, HOLType)]+getOverloads ctxt =+ let (Overload overs) = getExtCtxt ctxt in overs++-- Type Abbreviations+{-| + Specifies a 'String' to act as an abbreviation for a given type in the parser.+ Upon recognizing the abbreviation the parser will replace it with the + 'PreType' value for it's associated 'HOLType' such that the elaborator can+ infer the correct type for polymorphic abbreviations.+-}+newTypeAbbrev :: String -> HOLType -> HOL Theory thry ()+newTypeAbbrev s ty =+ modifyExt (\ (TypeAbbreviations abvs) -> + TypeAbbreviations $ insertMap s ty abvs)++{-| + Specifies a 'String' for the parser to stop recognizing as a type + abbreviation.+-}+removeTypeAbbrev :: String -> HOL Theory thry ()+removeTypeAbbrev s =+ modifyExt (\ (TypeAbbreviations abvs) ->+ TypeAbbreviations $ filter (\ (s', _) -> s' /= s) abvs)++{-| + Returns all 'String's currently acting as type abbreviations in the parser+ paired with their associated types.+-}+typeAbbrevs :: HOLContext thry -> [(String, HOLType)]+typeAbbrevs ctxt =+ let (TypeAbbreviations abvs) = getExtCtxt ctxt in abvs++-- Hidden Constant Mapping+-- | Specifies a 'String' for the parser to stop recognizing as a constant.+hideConstant :: String -> HOL Theory thry ()+hideConstant c = modifyExt (\ (Hidden hcs) -> Hidden $ c `insert` hcs)++-- | Specifies a 'String' for the parser to resume recognizing as a constant.+unhideConstant :: String -> HOL Theory thry ()+unhideConstant c = modifyExt (\ (Hidden hcs) -> Hidden $ c `delete` hcs)++-- | Returns all 'String's currently acting as constants hidden from the parser.+getHidden :: HOLContext thry -> [String]+getHidden ctxt = + let (Hidden hidden) = getExtCtxt ctxt in hidden++deriveLiftMany [ ''BinderOps, ''TyBinderOps + , ''PrefixOps, ''InfixOps+ , ''Interface, ''Overload + , ''TypeAbbreviations, ''Hidden ]
+ src/HaskHOL/Core/Parser/Rep.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, + TypeSynonymInstances, UndecidableInstances #-}++{-|+ Module: HaskHOL.Core.Parser.Rep+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module defines conversions for alternative type and term representations+ via the 'HOLTermRep' and 'HOLTypeRep' classes.++ The most commonly used alternative representations are strings and protected+ terms/types as produced by the "HaskHOL.Core.Ext" module.+-}+module HaskHOL.Core.Parser.Rep+ ( HOLTypeRep(..)+ , HOLTermRep(..)+ ) where++import HaskHOL.Core.Kernel+import HaskHOL.Core.State++import HaskHOL.Core.Parser.Lib+import HaskHOL.Core.Parser.Elab+import {-# SOURCE #-} HaskHOL.Core.Parser (holTermParser, holTypeParser)++{-|+ The 'HOLTypeRep' class provides a conversion from an alternative + representation of types to 'HOLType' within the 'HOL' monad.++ The first parameter is the type of the alternative representation.+ + The second parameter is the tag for the last checkpoint of the + current working theory. This enables us to have a conversion from + representations that are theory dependent without running into type + matchability issues.+-}+class HOLTypeRep a thry | a -> thry where+ -- | Conversion from alternative type @a@ to 'HOLType'.+ toHTy :: a -> HOL cls thry HOLType++instance HOLTypeRep String a where+ toHTy x = + do ctxt <- get+ tyElab =<< liftEither "toHTy" (holTypeParser x ctxt)++instance HOLTypeRep PreType a where+ toHTy = tyElab++instance HOLTypeRep HOLType a where+ toHTy = return++{-|+ The 'HOLTermRep' class provides a conversion from an alternative + representation of terms to 'HOLTerm' within the 'HOL' monad.++ The first parameter is the type of the alternative representation.+ + The second parameter is the tag for the last checkpoint of the + current working theory. This enables us to have a conversion from + representations that are theory dependent, i.e. 'PTerm', without running into+ type matchability issues.+-}+class HOLTermRep a thry | a -> thry where+ -- | Conversion from alternative type @a@ to 'HOLTerm'.+ toHTm :: a -> HOL cls thry HOLTerm++instance HOLTermRep String a where+ toHTm x = + do ctxt <- get+ elab =<< liftEither "toHTm" (holTermParser x ctxt)+ +instance HOLTermRep PreTerm a where+ toHTm = elab++instance HOLTermRep HOLTerm a where+ toHTm = return
+ src/HaskHOL/Core/Parser/TermParser.hs view
@@ -0,0 +1,225 @@+{-|+ Module: HaskHOL.Core.Parser.TermParser+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module defines the parser for 'HOLTerm's that satisfies the following BNF+ grammar:++@+ PRETERM :: APPL_PRETERM binop APPL_PRETERM + | APPL_PRETERM + + APPL_PRETERM :: BINDER_PRETERM+ + | BINDER_PRETERM : type + + BINDER_PRETERM :: tybinder small-type-variables . PRETERM + | binder VARSTRUCT_PRETERM+ . PRETERM + | let PRETERM and ... and PRETERM in PRETERM + | TYPED_PRETERM + + TYPED_PRETERM :: TYINST (tyop-var : PRETYPE)+ ATOMIC_PRETERM + | ATOMIC_PRETERM + + VARSTRUCT_PRETERM :: ATOMIC_PRETERM : type + | ATOMIC_PRETERM + + ATOMIC_PRETERM :: ( PRETERM ) + | [: type] + | [ PRETERM; .. ; PRETERM ] + | if PRETERM then PRETERM else PRETERM + | identifier +@ + + Note that arbitrary atomic preterms, typed or untyped, are allowed as + varstructs in order to simplify parsing. We do not make the same + simplification for @TYINST@ terms in order to avoid the mixing of terms, + types, and type operators. ++ Also note that a number of advanced HOL term features, mostly relating to sets+ and patterns, are not currently supported by the parser. These will be added+ in as the relevant logic libraries are added to the system.++ As a heads up, the error messages thrown by this parser leave much to be+ desired.+-}+module HaskHOL.Core.Parser.TermParser+ ( pterm+ ) where++import HaskHOL.Core.Lib hiding (many)+import HaskHOL.Core.State++import HaskHOL.Core.Parser.Lib+import HaskHOL.Core.Parser.TypeParser++-- | Parser for HOL terms.+pterm :: MyParser thry PreTerm+pterm = + do mywhiteSpace+ ctxt <- getState+ buildExpressionParser (partitionOps ctxt) pappl++pappl :: MyParser thry PreTerm+pappl = + try (do prefs <- pprefixes+ if null prefs+ then fail "pappl: no prefix found"+ else do bod <- pappl+ return $! foldr PComb bod prefs)+ <|> (do tms <- many1 pbinder+ let tm = mkPComb tms+ pas tm <|> return tm)++pbinder :: MyParser thry PreTerm+pbinder = + (do myreserved "let"+ tms <- pterm `sepBy1` myreserved "and"+ myreserved "in"+ bod <- pterm+ case mkLet tms bod of+ Nothing -> fail "pterm: invalid let construction"+ Just tm -> return tm)+ <|> do (ctxt, _) <- getState+ bind <- choiceOp $ binders ctxt+ (do vars <- many1 pvar+ myreservedOp "."+ bod <- pterm+ return $! mkBinders bind vars bod)+ <|> (return $! PVar bind dpty)+ <|> do (ctxt, _) <- getState+ bind <- choiceOp $ tyBinders ctxt+ (do vars <- many1 psmall+ myreservedOp "."+ bod <- pterm+ return $! mkTyBinders bind vars bod)+ <|> (return $! PVar bind dpty)+ <|> ptyped+ where ++ptyped :: MyParser thry PreTerm+ptyped = + (do myreserved "TYINST"+ vars <- many1 pinst+ tm <- patomic+ return $! PInst vars tm)+ <|> patomic+ where pinst :: MyParser thry (PreType, String)+ pinst = myparens $ do myreservedOp "_"+ x <- myidentifier+ myreservedOp ":"+ ty <- ptype+ return (ty, x)+ ++pvar :: MyParser thry PreTerm+pvar =+ do tm <- patomic+ pas tm <|> return tm++pas :: PreTerm -> MyParser thry PreTerm+pas ptm =+ do myreservedOp ":"+ ty <- ptype+ return $! PAs ptm ty++patomic :: MyParser thry PreTerm+patomic = + myparens (pterm <|> (do x <- myoperator+ return $! PVar x dpty))+ <|> mybrackets + ((do myreservedOp ":"+ ty <- ptype+ return $! PApp ty)+ <|> (do tms <- mysemiSep pterm+ return (foldr (\ x y -> PVar "CONS" dpty `PComb` + x `PComb` y)+ (PVar "NILS" dpty) tms)))+ {- <|> mybraces+ ((do tms <- mycommaSep pterm+ return $! foldr (\ x y -> PComb (PComb (PVar "INSERT" dpty) x) y)+ (PVar "EMPTY" dpty) tms)+ <|> (do tms <- mypipeSep pterm+ if length tms == 2+ then -- setabs+ else if length tms == 3+ then -- setcompr))+ -}+ <|> (do myreserved "if"+ c <- pterm+ myreserved "then"+ t <- pterm+ myreserved "else"+ e <- pterm+ return $! PComb (PComb (PComb (PVar "COND" dpty) c) t) e)+ <|> (do x <- myidentifier+ return $! PVar x dpty)++pprefixes :: MyParser thry [PreTerm]+pprefixes =+ do (ctxt, _) <- getState+ pref <- myoperator+ let prefOps = sortBy (\ x y -> compare (length y) (length x)) $ + prefixes ctxt+ return $! splitPref pref prefOps []+ where splitPref :: String -> [String] -> [PreTerm] -> [PreTerm]+ splitPref _ [] acc = acc+ splitPref ops prefs@(p:ps) acc =+ case stripPrefix p ops of+ Nothing -> splitPref ops ps acc+ Just ops' -> + let acc' = acc ++ [PVar p dpty] in+ if null ops' then acc'+ else splitPref ops' prefs acc'+ ++-- helper functions+mkPComb :: [PreTerm] -> PreTerm+mkPComb (tm:[]) = tm+mkPComb (tm:tms) = foldr (flip PComb) tm (reverse tms)+mkPComb _ = error "parser: mkPComb used without many1 parser combinator"++pdestEq :: PreTerm -> Maybe (PreTerm, PreTerm)+pdestEq (PComb (PComb (PVar "=" _) l) r) = Just (l, r)+pdestEq (PComb (PComb (PVar "<=>" _) l) r) = Just (l, r)+pdestEq _ = Nothing++mkLet :: [PreTerm] -> PreTerm -> Maybe PreTerm+mkLet binds bod = case length tms of+ 0 -> Nothing+ _ -> Just $ foldl PComb letstart tms+ where (vars, tms) = unzip $ mapMaybe pdestEq binds+ letend = PComb (PVar "LET_END" dpty) bod+ ab = foldr PAbs letend vars+ letstart = PComb (PVar "LET" dpty) ab++mkBinder :: String -> PreTerm -> PreTerm -> PreTerm+mkBinder "\\" v bod = PAbs v bod+mkBinder n v bod = PComb (PVar n dpty) $ PAbs v bod++mkBinders :: String -> [PreTerm] -> PreTerm -> PreTerm+mkBinders bind vars bod = foldr (mkBinder bind) bod vars++mkTyBinder :: String -> PreType -> PreTerm -> PreTerm+mkTyBinder "\\\\" v bod = TyPAbs v bod+mkTyBinder n v bod = PComb (PVar n dpty) $ TyPAbs v bod++mkTyBinders :: String -> [PreType] -> PreTerm -> PreTerm+mkTyBinders bind vars bod = foldr (mkTyBinder bind) bod vars++-- build op table for expression parser from context+-- Note: prefix operators are handled separately in pprefixes+partitionOps :: (HOLContext thry, [(String, Int)]) ->+ OperatorTable Char (HOLContext thry, [(String, Int)]) PreTerm+partitionOps (ctxt, _) = map (map mkOp) . + group' (\ (_, (x, _)) (_, (y, _)) -> x == y) $ + infixes ctxt+ where mkOp :: (String, (Int, Assoc)) -> + Operator Char (HOLContext thry, [(String, Int)]) PreTerm+ mkOp (name, (_, a)) = + Infix (do myreservedOp name+ return (\ x y -> PComb (PComb (PVar name dpty) x) y)) a
+ src/HaskHOL/Core/Parser/TypeParser.hs view
@@ -0,0 +1,176 @@+{-|+ Module: HaskHOL.Core.Parser.TypeParser+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module defines the parser for 'HOLType's that satisfies the following BNF+ grammar:++@+ TYPE :: % small-type-variables . TYPE + | SUMTYPE -> TYPE + | SUMTYPE + + SUMTYPE :: PRODTYPE + SUMTYPE + | PRODTYPE + + PRODTYPE :: POWTYPE # PRODTYPE + | POWTYPE ++ POWTYPE :: APPTYPE ^ POWTYPE+ | POWTYPE + + APPTYPE :: ( TYPELIST ) type-constructor [Provided arity matches] + | ( TYPELIST ) tyop-var [Provided arity matches or fresh] + | small-type-variables+ tyop-var [Special case of above]+ | ( TYPE ) + | ATOMICTYPE + + ATOMICTYPE :: type-constructor [Provided arity zero] + | tyop-var [Provided arity zero or fresh] + | type-variable [Large or Small] + + TYPELIST :: TYPE , TYPELIST + | TYPE +@++ Note that this module also exposes a parser for small type variables to be+ used by the term parser. ++ As a heads up, the error messages thrown by this parser leave much to be+ desired.+-}+module HaskHOL.Core.Parser.TypeParser + ( ptype+ , psmall+ ) where++import HaskHOL.Core.Lib+import HaskHOL.Core.State++import HaskHOL.Core.Parser.Lib++-- | Parser for HOL types.+ptype :: MyParser thry PreType+ptype = + mywhiteSpace >> (putype <|> pbinty "->" "fun" psumty ptype)++-- | Parser for small type variables.+psmall :: MyParser thry PreType+psmall =+ do myreservedOp "'"+ x <- myidentifier+ return $! UTyVar True x 0++popvar :: MyParser thry (Either PreType PreType)+popvar =+ do myreservedOp "_"+ x <- myidentifier+ {-+ Tracks introduction of type operator variables to make sure that all+ tyopvars of the same name in a term are of the same arity.+ Left is fresh.+ Right is existing.+ -}+ let x' = '_':x+ (_, opvars) <- getState+ case lookup x' opvars of+ Nothing -> return . Left $ UTyVar False x' 0+ Just n -> return . Right $ UTyVar False x' n++pbinty :: String -> String -> MyParser thry PreType -> MyParser thry PreType -> + MyParser thry PreType+pbinty op name pty1 pty2 =+ do ty1 <- pty1+ (do myreservedOp op+ ty2 <- pty2+ return $! PTyComb (PTyCon name) [ty1, ty2]) + <|> return ty1++putype :: MyParser thry PreType+putype = + do myreservedOp "%"+ tvs <- many1 psmall+ myreservedOp "."+ ty <- ptype+ return $! foldr PUTy ty tvs++psumty :: MyParser thry PreType+psumty = pbinty "+" "sum" pprodty psumty++pprodty :: MyParser thry PreType+pprodty = pbinty "#" "prod" ppowty pprodty++ppowty :: MyParser thry PreType+ppowty = pbinty "^" "cart" pappty ppowty++pappty :: MyParser thry PreType+pappty =+ do tys <- myparens $ mycommaSep1 ptype+ (do c <- popvar+ case c of+ Left (UTyVar _ s _) ->+ -- fresh ty op var so add it to state+ let n = length tys in+ do updateState $ second ((:) (s, n))+ let c' = UTyVar False s n+ return $! PTyComb c' tys+ Right c'@(UTyVar _ _ n) ->+ -- existing ty op var so check arity+ if n == length tys+ then return $! PTyComb c' tys+ else fail "type parser: bad arity for type application"+ _ -> fail $ "type parser: unrecognized case for type operator " +++ "variable")+ <|> ((do x <- myidentifier+ (ctxt, _) <- getState+ case getTypeArityCtxt ctxt x of+ Nothing -> fail $ "type parser: unsupported type " ++ + "variable application"+ Just n ->+ if n == length tys+ then return $! PTyComb (PTyCon x) tys+ else fail "type parser: bad arity for type application")+ <|> (case tys of+ (ty:[]) -> return ty+ _ -> fail "type parser: unexpected list of types"))+ <|> try (do tys <- many1 psmall+ c <- popvar+ case c of+ Left (UTyVar _ s _) ->+ let n = length tys in+ do updateState $ second ((:) (s, n))+ return $! PTyComb (UTyVar False s n) tys+ Right c'@(UTyVar _ _ n) ->+ if n == length tys+ then return $! PTyComb c' tys+ else fail "type parser: bad type operator application."+ _ -> fail "type parser: unrecognized case for type operator.")+ <|> patomty++patomty :: MyParser thry PreType+patomty = + psmall+ <|> (do c <- popvar+ case c of+ Left c'@(UTyVar _ s 0) ->+ -- fresh ty-op of zero arity+ do updateState $ second ((:) (s, 0))+ return $! PTyComb c' []+ Right c'@(UTyVar _ _ 0) ->+ return $! PTyComb c' []+ _ -> fail $ "type parser: type operator variable of non-zero " +++ "arity outside of application")+ <|> (do x <- myidentifier <|> liftM show myinteger+ (ctxt, _) <- getState+ case x `lookup` typeAbbrevs ctxt of+ Just ty -> return $! pretypeOfType ty+ Nothing -> case getTypeArityCtxt ctxt x of+ Nothing -> return $! UTyVar False x 0+ Just 0 -> return $! PTyComb (PTyCon x) []+ _ -> fail "type parser: bad type construction")+
+ src/HaskHOL/Core/Printer.hs view
@@ -0,0 +1,467 @@+{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses,+ OverlappingInstances, TemplateHaskell, UndecidableInstances, + ViewPatterns #-}++{-|+ Module: HaskHOL.Core.Printer+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module defines pretty printers for 'HOLType's, 'HOLTerm's and 'HOLThm's. + Note that the printers for terms and theorems are context dependent as they + rely on the same theory extensions that the parsers utilize. ++ To make printing these objects easier within HOL computations, this module+ also defines the 'showHOL' and 'printHOL' methods which will automatically+ retrieve the current working theory to use for pretty printing. Because the + pretty printer for 'HOLType's is not context dependent it has definitions for + both 'show' and 'showHOL'.++ Note that, like the parser, there are a number of HOL term forms that the+ printer does not currently support. Again, these are mainly related to sets+ and patterns and will be added in when the HaskHOL system has libraries for+ them.+-}+module HaskHOL.Core.Printer+ ( -- * Pretty Printer Flags+ FlagRevInterface(..)+ , FlagPrintAllThm(..)+ -- * Extensible Printer Operators+ , addUnspacedBinop -- :: String -> HOL Theory thry ()+ , addPrebrokenBinop -- :: String -> HOL Theory thry ()+ , removeUnspacedBinop -- :: String -> HOL Theory thry ()+ , removePrebrokenBinop -- :: String -> HOL Theory thry ()+ , getUnspacedBinops -- :: HOLContext thry -> [String]+ , getPrebrokenBinops -- :: HOLContext thry -> [String]+ -- * Pretty Printers+ , ppType -- :: HOLType -> String+ , ppTerm -- :: HOLContext thry -> HOLTerm -> String+ , ppThm -- :: HOLContext thry -> HOLThm -> String+ -- * Printing in the 'HOL' Monad+ , ShowHOL(..)+ , printHOL -- :: ShowHOL a => a -> HOL cls thry ()+ ) where++import HaskHOL.Core.Lib hiding (empty, lefts, rights)+import HaskHOL.Core.Kernel+import HaskHOL.Core.State+import HaskHOL.Core.Basics+import HaskHOL.Core.Parser++{- + Used for a number of pretty-printing primitives that don't really need to be+ exposed to the rest of the system. Although no harm would come should we+ elect to move this to be re-exported by Core.Lib.+-}+import Text.PrettyPrint++-- new flags and extensions+-- | Flag to indicate whether the interface should be reversed on printing.+newFlag "FlagRevInterface" True++{-| + Flag to indicate if the entirety of a theorem should be printed, as opposed+ to just the conclusion term.+-}+newFlag "FlagPrintAllThm" True++newExtension "UnspacedBinops" [| [",", "..", "$"] :: [String] |]++newExtension "PrebrokenBinops" [| ["==>"] :: [String] |]++{-| + Specifies a symbol to be recognized as an unspaced, binary operator by the+ printer. Applications involving these operators will be built with the '<>'+ combinator as opposed to '<+>'.++ Note that technically this method should be considered benign, however, for+ simplicity of implementation it is defined using 'modifyExt' and thus must be+ tagged a 'Theory' computation.+-}+addUnspacedBinop :: String -> HOL Theory thry ()+addUnspacedBinop op =+ modifyExt (\ (UnspacedBinops ops) -> UnspacedBinops $ op `insert` ops)++{-| + Specifies a symbol to be recognized as a prebroken, binary operator by the+ printer. Applications involving these operators will have their right-hand+ side argument printed on the next line using the 'hang' combinator.++ Note that technically this method should be considered benign, however, for+ simplicity of implementation it is defined using 'modifyExt' and thus must be+ tagged a 'Theory' computation.+-}+addPrebrokenBinop :: String -> HOL Theory thry ()+addPrebrokenBinop op =+ modifyExt (\ (PrebrokenBinops ops) -> PrebrokenBinops $ op `insert` ops)++{-| + Specifies a symbol to stop being recognized as an unspaced, binary operator + by the printer.++ Note that technically this method should be considered benign, however, for+ simplicity of implementation it is defined using 'modifyExt' and thus must be+ tagged a 'Theory' computation.+-}+removeUnspacedBinop :: String -> HOL Theory thry ()+removeUnspacedBinop op =+ modifyExt (\ (UnspacedBinops ops) -> UnspacedBinops $ ops \\ [op])++{-| + Specifies a symbol to stop being recognized as an prebroken, binary operator + by the printer.++ Note that technically this method should be considered benign, however, for+ simplicity of implementation it is defined using 'modifyExt' and thus must be+ tagged a 'Theory' computation.+-}+removePrebrokenBinop :: String -> HOL Theory thry ()+removePrebrokenBinop op =+ modifyExt (\ (PrebrokenBinops ops) -> PrebrokenBinops $ ops \\ [op])++{-| + Returns the list of all symbols current recognized as unspaced, binary+ operators by the printer.+-}+getUnspacedBinops :: HOLContext thry -> [String]+getUnspacedBinops ctxt =+ let (UnspacedBinops ops) = getExtCtxt ctxt in ops++{-| + Returns the list of all symbols current recognized as prebroken, binary+ operators by the printer.+-}+getPrebrokenBinops :: HOLContext thry -> [String]+getPrebrokenBinops ctxt =+ let (PrebrokenBinops ops) = getExtCtxt ctxt in ops++-- | Pretty printer for 'HOLType's.+ppType :: HOLType -> String+ppType = render . ppTypeRec 0+ where ppTypeRec :: Int -> HOLType -> Doc+ ppTypeRec _ (view -> TyVar False x) = text x+ ppTypeRec _ (view -> TyVar True x) = text $ '\'' : x+ ppTypeRec prec ty =+ case destUTypes ty of+ Just (tvs, bod) -> + let tvs' = foldr (\ x acc -> ppTypeRec prec x <+> acc) + empty tvs in+ parens $ char '%' <+> tvs' <+> char '.' <+> + ppTypeRec prec bod+ Nothing -> + case do (op, tys) <- destType ty + let (name, ar) = destTypeOp op+ name' = if ar < 0 then '_':name else name+ return (name', tys) of+ Just (op, []) -> text op+ Just ("fun", ty1:ty2:[]) ->+ ppTypeApp "->" (prec > 0) [ ppTypeRec 1 ty1+ , ppTypeRec 0 ty2]+ Just ("sum", ty1:ty2:[]) -> + ppTypeApp "+" (prec > 2) [ ppTypeRec 3 ty1+ , ppTypeRec 2 ty2]+ Just ("prod", ty1:ty2:[]) -> + ppTypeApp "#" (prec > 4) [ ppTypeRec 5 ty1+ , ppTypeRec 4 ty2]+ Just ("cart", ty1:ty2:[]) -> + ppTypeApp "^" (prec > 6) [ ppTypeRec 6 ty1+ , ppTypeRec 7 ty2]+ Just (bin, args) -> + ppTypeApp "," True (map (ppTypeRec 0) args) <+> text bin+ _ -> text "ppType: printer error - unrecognized type"+ + ppTypeApp :: String -> Bool -> [Doc] -> Doc+ ppTypeApp sepr flag ds =+ case tryFoldr1 (\ x y -> x <+> text sepr <+> y) ds of+ Nothing -> empty+ Just bod -> if flag then parens bod else bod++-- Printer for Terms+-- | Pretty printer for 'HOLTerm's.+ppTerm :: HOLContext thry -> HOLTerm -> String+ppTerm ctxt = render . ppTermRec 0+ where ppTermRec :: Int -> HOLTerm -> Doc+ ppTermRec prec tm =+-- List case+ case destList tm of+ Just tms -> brackets $ ppTermSeq ";" 0 tms+ Nothing ->+-- Type combination case+ case destTyComb tm of+ Just (t, ty) -> + let base = ppTermRec 999 t <+> + brackets (char ':' <> text (ppType ty)) in+ if prec == 1000 then parens base else base+ Nothing ->+-- Let case+ case destLet tm of+ Just (eq:eqs, bod) ->+ let ppLet x = case uncurry primMkEq x of+ Right x' -> ppTermRec 0 x'+ _ -> text "<*bad let binding*>"+ base = hang+ (text "let" <+> + foldr (\ eq' acc -> acc <+> text "and" <+> + ppLet eq') (ppLet eq) eqs <+>+ text "in") 2 $ ppTermRec 0 bod in+ if prec == 0 then base else parens base + _ ->+ let (hop, args) = stripComb tm in+-- Base term abstraction case+ if isAbs hop && null args + then ppBinder prec "\\" False hop+-- Base type abstraction case+ else + if isTyAbs hop && null args+ then ppBinder prec "\\\\" True hop+-- Reverse interface for other cases+ else let s0 = case view hop of+ Var x _ -> x+ Const x _ _ -> x+ _ -> ""+ ty0 = typeOf hop+ s = reverseInterface s0 ty0 in+-- General abstraction case+ if s == "GABS"+ then case destGAbs tm of+ Nothing -> text "ppTerm: printer error - GAbs case"+ Just (vs, bod) ->+ let base = char '\\' <+> ppTermRec 999 vs <+> + char '.' <+> ppTermRec 0 bod in+ if prec == 0 then base else parens base+-- Conditional case+ else + if s == "COND" && length args == 3+ then let (c:t:e:_) = args+ base = text "if" <+> ppTermRec 0 c <+> + text "then" <+> ppTermRec 0 t <+> + text "else" <+> ppTermRec 0 e in+ if prec == 0 then base else parens base+-- Prefix operator case + else + if s `elem` prefix && + length args == 1+ then let base = text s <+> ppTermRec 999 (head args) in+ if prec == 1000 then parens base else base+-- Non-lambda term binder case+ else + if s `elem` binds && + length args == 1 && + isGAbs (head args)+ then ppBinder prec s False tm+-- Non-lambda type binder case+ else+ if s `elem` tybinds && + length args == 1 &&+ isTyAbs (head args)+ then ppBinder prec s True tm+-- Infix operator case+ else + let getRight = s `lookup` rights+ getLeft = s `lookup` lefts in+ if (isJust getRight || isJust getLeft) &&+ length args == 2+ then let (barg:bargs) = + if isJust getRight+ then let (tms, tmt) = + splitList (destBinaryTm hop) tm in+ tms ++ [tmt]+ else let (tmt, tms) = + revSplitList (destBinaryTm hop) tm in+ tmt:tms + newprec = fromMaybe 0 (getRight <|> getLeft)+ wrapper = if newprec <= prec then parens else id+ sepr = + if s `elem` getUnspacedBinops ctxt + then (\ x y -> cat [x, y]) + else (\ x y -> sep [x, y])+ hanger = + if s `elem` getPrebrokenBinops ctxt+ then (\ x y -> x `sepr` (text s <+> y))+ else (\ x y -> (x <+> text s) `sepr` y) in+ wrapper $ + foldr (\ x acc -> acc `hanger` + ppTermRec newprec x)+ (ppTermRec newprec barg) $ reverse bargs+-- Base constant or variable case+ else + if null args && (isConst hop || isVar hop)+ then if s `elem` binds || + s `elem` tybinds ||+ isJust (s `lookup` rights) || + isJust (s `lookup` lefts) ||+ s `elem` prefix+ then parens $ text s+ else text s+-- Base combination case + else case destComb tm of+ Just (l, r) ->+ let base = ppTermRec 999 l <+> ppTermRec 1000 r in+ if prec == 1000 then parens base else base+ _ -> text "ppTerm: printer error - unrecognized term"++ grabInfix :: Assoc -> [(String, (Int, Assoc))] -> [(String, Int)]+ grabInfix a = + mapMaybe (\ (x, (n, a')) -> if a == a' + then Just (x, n)+ else Nothing)+ binds :: [String]+ binds = binders ctxt++ tybinds :: [String]+ tybinds = tyBinders ctxt++ prefix :: [String]+ prefix = prefixes ctxt++ lefts :: [(String, Int)]+ lefts = grabInfix AssocLeft $ infixes ctxt++ rights :: [(String, Int)]+ rights = grabInfix AssocRight $ infixes ctxt++ ppTermSeq :: String -> Int -> [HOLTerm] -> Doc+ ppTermSeq sepr prec = ppTermSeqRec+ where ppTermSeqRec [] = empty+ ppTermSeqRec (x:[]) = ppTermRec prec x+ ppTermSeqRec (x:xs) =+ ppTermRec prec x <+> text sepr <+> ppTermSeqRec xs++ ppBinder :: Int -> String -> Bool -> HOLTerm -> Doc+ ppBinder prec prep f tm =+ let (vs, bod) = if f then stripTy ([], tm) else stripTm ([], tm)+ base = let bvs = text prep <> + foldr (\ x acc -> acc <+> text x) empty vs <>+ char '.'+ indent = min (1 + length (render bvs)) 5 in+ cat [ bvs+ , nest indent $ ppTermRec prec bod+ ] in+ if prec == 0 then base else parens base+ where stripTm :: ([String], HOLTerm) -> ([String], HOLTerm)+ stripTm (acc, view -> Abs (view -> Var bv _) bod) = + stripTm (bv:acc, bod)+ stripTm pat@(acc, view -> Comb (view -> Const s _ _) + (view -> Abs (view -> Var bv _) bod))+ | s == prep = stripTm (bv:acc, bod)+ | otherwise = pat+ stripTm pat = pat+ + stripTy :: ([String], HOLTerm) -> ([String], HOLTerm)+ stripTy (acc, view -> TyAbs (view -> TyVar _ bv) bod) =+ stripTy (('\'':bv):acc, bod)+ stripTy pat@(acc, view -> Comb (view -> Const s _ _)+ (view -> TyAbs (view -> TyVar _ bv) bod))+ | s == prep = stripTy (('\'':bv):acc, bod)+ | otherwise = pat+ stripTy pat = pat++ destBinaryTm :: HOLTerm -> HOLTerm -> Maybe (HOLTerm, HOLTerm)+ destBinaryTm c tm =+ do (il, r) <- destComb tm+ (i, l) <- destComb il+ if i == c+ then do i' <- destConst i+ c' <- destConst c+ if uncurry reverseInterface i' == + uncurry reverseInterface c'+ then Just (l, r)+ else Nothing+ else Nothing++ reverseInterface :: String -> HOLType -> String+ reverseInterface s0 ty0+ | not (getBenignFlagCtxt FlagRevInterface ctxt) = s0+ | otherwise = fromMaybe s0 . liftM fst .+ find (\ (_, (s', ty)) -> s' == s0 && + isJust (typeMatch ty ty0 ([], [], []))) $+ getInterface ctxt++-- Printer for Theorems+ +-- | Pretty printer for 'HOLTheorem's. +ppThm :: HOLContext thry -> HOLThm -> String+ppThm ctxt (view -> Thm asl c) = render ppThmRec+ where ppThmRec :: Doc+ ppThmRec = + let c' = text $ ppTerm ctxt c+ asl'+ | null asl = [empty]+ | not (getBenignFlagCtxt FlagPrintAllThm ctxt) = [text "..."]+ | otherwise = showHOLListRec comma $ map (ppTerm ctxt) asl in+ sep (asl' ++ [text "|-" <+> c'])++{-| + The @ShowHOL@ class is functionally equivalent to 'show' lifted to the 'HOL'+ monad. It is used to retrieve the current working theory to be used with the+ context sensitive pretty printers for 'HOLTerm's and 'HOLType's.+-}+class ShowHOL a thry where+ {-| + A version of 'show' lifted to the 'HOL' monad for context sensitive pretty+ printers.+ -}+ showHOL :: a -> HOL cls thry String+ +instance ShowHOL String thry where+ showHOL = return++instance ShowHOL a thry => ShowHOL [a] thry where+ showHOL = liftM (showHOLList brackets comma) . mapM showHOL++instance (ShowHOL a thry, ShowHOL b thry) => ShowHOL (a, b) thry where+ showHOL (a, b) = liftM (showHOLList parens comma) . sequence $ + [showHOL a, showHOL b]++instance (ShowHOL a thry, ShowHOL b thry, ShowHOL c thry) => + ShowHOL (a, b, c) thry where+ showHOL (a, b, c) = liftM (showHOLList parens comma) . sequence $ + [showHOL a, showHOL b, showHOL c]++instance (ShowHOL a thry, ShowHOL b thry, ShowHOL c thry, ShowHOL d thry) => + ShowHOL (a, b, c, d) thry where+ showHOL (a, b, c, d) = liftM (showHOLList parens comma) . sequence $ + [showHOL a, showHOL b, showHOL c, showHOL d]++-- Prints a list of strings provided a wrapper function and seperator document.+showHOLList :: (Doc -> Doc) -> Doc -> [String] -> String+showHOLList wrap sepr = render . wrap . sep . showHOLListRec sepr+ +-- Useful to have at top level for ppThm.+showHOLListRec :: Doc -> [String] -> [Doc]+showHOLListRec _ [] = [empty]+showHOLListRec _ (x:[]) = [text x]+showHOLListRec sepr (x:xs) = (text x <> sepr <> space) : showHOLListRec sepr xs++-- orphan instances+instance ShowHOL Assoc thry where+ showHOL (AssocNone) = return "None" + showHOL (AssocLeft) = return "Left"+ showHOL (AssocRight) = return "Right"++instance ShowHOL TypeOp thry where+ showHOL = return . show++instance ShowHOL HOLType thry where+ showHOL ty = return $ ':' : ppType ty++instance ShowHOL HOLTerm thry where+ showHOL tm = do ctxt <- get+ return $! ppTerm ctxt tm++instance ShowHOL HOLThm thry where+ showHOL thm = do ctxt <- get+ return $! ppThm ctxt thm++{-| + Prints a HOL object with a new line. A composition of 'putStrLnHOL' and+ 'showHOL'.+-}+printHOL :: ShowHOL a thry => a -> HOL cls thry ()+printHOL = putStrLnHOL <=< showHOL++deriveLiftMany [''UnspacedBinops, ''PrebrokenBinops]
+ src/HaskHOL/Core/State.hs view
@@ -0,0 +1,390 @@+{-# LANGUAGE DeriveDataTypeable, TemplateHaskell, ViewPatterns #-}++{-|+ 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 -- :: HOL cls thry [(String, TypeOp)]+ , getTypeArityCtxt -- :: HOLContext thry -> String -> Maybe Int+ , getTypeArity -- :: String -> HOL cls thry Int+ , newType -- :: String -> Int -> HOL Theory thry ()+ , mkType -- :: String -> [HOLType] -> HOL cls thry HOLType+ , mkFunTy -- :: HOLType -> HOLType -> HOL cls thry HOLType+ -- * Stateful Term Primitives+ , constants -- :: HOL cls thry [(String, HOLTerm)]+ , getConstType -- :: String -> HOL cls thry HOLType+ , newConstant -- :: String -> HOLType -> HOL Theory thry ()+ , mkConst -- :: TypeSubst l r => + -- String -> [(l, r)] -> HOL cls thry HOLTerm+ , mkConstFull -- :: String -> SubstTrip -> HOL cls thry HOLTerm+ , mkEq -- :: HOLTerm -> HOLTerm -> HOL cls thry HOLTerm+ -- * Stateful Theory Extension Primitives+ , axioms -- :: HOL cls thry [(String, HOLThm)]+ , getAxiom -- :: String -> HOL cls thry HOLThm+ , newAxiom -- :: String -> HOLTerm -> HOL Theory thry HOLThm+ , definitions -- :: HOL cls thry [HOLThm]+ , newBasicDefinition -- :: HOLTerm -> HOL Theory thry HOLThm+ , newBasicTypeDefinition -- :: String -> String -> String -> HOLThm -> + -- HOL Theory thry (HOLThm, HOLThm)+ -- * Primitive Debugging System+ , FlagDebug(..)+ , warn -- :: Bool -> String -> HOL cls thry ()+ , printDebugLn -- :: String -> HOL cls thry a -> HOL cls thry a+ , printDebug -- :: String -> HOL cls thry a -> HOL cls thry a+ -- * 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++newExtension "TypeConstants" + [| [("bool", tyOpBool), ("fun", tyOpFun)] :: [(String, TypeOp)] |]++newExtension "TermConstants" [| [("=", tmEq tyA)] :: [(String, HOLTerm)] |]++newExtension "TheAxioms" [| [] :: [(String, HOLThm)] |]++{- + Extensible state type for term definitions introduced via + newBasicDefinition.+-}+newExtension "TheCoreDefinitions" [| [] :: [HOLThm] |]++-- Stateful HOL Light Type Primitives+{-|+ Retrieves the list of type constants from the current working theory. The+ list contains pairs of strings recognized by the parser and the associated+ type operator value, i.e. ++ > ("bool", tyOpBool)+-}+types :: HOL cls thry [(String, TypeOp)]+types =+ do (TypeConstants tys) <- getExt+ return tys++-- needed for parser+{-| + Retrieves the arity of a given type constant. Fails with 'Nothing' if the+ provided type constant name is not defined in the provided context.++ Note that this function takes a 'HOLContext' argument such that it can be+ used outside of 'HOL' computations; for example, in the parser.+-}+getTypeArityCtxt :: HOLContext thry -> String -> Maybe Int+getTypeArityCtxt ctx name =+ let (TypeConstants tys) = getExtCtxt ctx in+ do tyOp <- lookup name tys+ return . snd $ destTypeOp tyOp++{-|+ A version of 'getTypeArityCtxt' that operates over the current working theory+ of a 'HOL' computation. Throws a 'HOLException' if the provided type constant+ name is not defined.+-}+getTypeArity :: String -> HOL cls thry Int+getTypeArity name =+ do ctxt <- get+ liftMaybe ("getTypeArity: type " ++ name ++ " has not been defined.") $+ getTypeArityCtxt ctxt name++{- + Primitive type constant construction function. Used by newType and + newBasicTypeDefinition. Not exposed to the user.+-}+newType' :: String -> TypeOp -> HOL Theory thry ()+newType' name tyop =+ do failWhen (can getTypeArity name) $+ "newType: type " ++ name ++ " has already been declared."+ modifyExt $ \ (TypeConstants consts) -> + TypeConstants $ (name, tyop) : consts++{-| + 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 :: String -> Int -> HOL Theory thry ()+newType name arity = + newType' name $ newPrimTypeOp 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 :: String -> [HOLType] -> HOL cls thry HOLType+mkType name args =+ do (TypeConstants consts) <- getExt+ case lookup name consts of+ Just tyOp -> liftEither "mkType: type constructor application failed" $+ tyApp tyOp args+ Nothing -> + {- This seemed to be the easiest way to supress superfluous warnings+ when parsing type operators. -}+ do name' <- case name of+ '_':x -> return x+ _ -> printDebugLn + ("warning - mkType: type " ++ name ++ " has " +++ "not been defined. Defaulting to type " ++ + "operator variable.") $ + return name+ failWhen (return $ null args)+ "mkType: type operator applied to zero args."+ liftEither "mkType: type operator variable application failed" $ + tyApp (mkTypeOpVar name') args++{-|+ 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 list of term constants from the current working theory. The+ list contains pairs of strings recognized by the parser and the associated+ term constant value, i.e. ++ > ("=", tmEq tyA)+-}+constants :: HOL cls thry [(String, HOLTerm)]+constants =+ do (TermConstants consts) <- getExt+ return consts++{-|+ Retrieves the type of a given term constant. Throws a 'HOLException' if the+ provided term constant name is not defined.+-}+getConstType :: String -> HOL cls thry HOLType+getConstType name =+ do (TermConstants consts) <- getExt+ tm <- liftMaybe "getConstType: not a constant name" $+ lookup name consts+ return $! typeOf tm++{-+ Primitive term constant construction function. Used by newConstant,+ newBasicDefinition, and newBasicTypeDefinition.+-}+newConstant' :: String -> HOLTerm -> HOL Theory thry ()+newConstant' name c =+ do failWhen (can getConstType name) $+ "newConstant: constant " ++ name ++ " has already been declared."+ modifyExt $ \ (TermConstants consts) -> + TermConstants $ (name, c) : consts++{-|+ 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 :: String -> HOLType -> HOL Theory thry ()+newConstant name ty =+ newConstant' name $ newPrimConst 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 => String -> [(l, r)] -> HOL cls thry HOLTerm+mkConst name tyenv =+ do (TermConstants consts) <- getExt+ tm <- liftMaybe "mkConst: not a constant name" $ + lookup 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 :: String -> SubstTrip -> HOL cls thry HOLTerm+mkConstFull name pat =+ do (TermConstants consts) <- getExt+ tm <- liftMaybe "mkConstFull: not a constant name" $+ lookup 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)]+ liftEither "mkEq" $+ liftM1 mkComb (mkComb eq l) r++-- 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 [(String, HOLThm)]+axioms = + do (TheAxioms thms) <- getExt+ return thms++{-| + Retrieves a specific axiom by name. Throws a 'HOLException' if there is no+ axiom with the provided name in the current working theory.+-}+getAxiom :: String -> HOL cls thry HOLThm+getAxiom lbl =+ do (TheAxioms thms) <- getExt+ liftMaybe "getAxiom: axiom name not found" $+ lookup lbl thms++{-| + 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 :: String -> HOLTerm -> HOL Theory thry HOLThm+newAxiom name tm+ | typeOf tm /= tyBool = fail "newAxiom: Not a proposition."+ | otherwise =+ do failWhen (can getAxiom name) $ "newAxiom: axiom with name " ++ + name ++ " has already been declared."+ let th = axiomThm tm + modifyExt $ \ (TheAxioms axs) -> TheAxioms $ (name, th) : axs+ return th++-- 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 (TheCoreDefinitions defs) <- getExt+ return defs++{-|+ 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 :: HOLTerm -> HOL Theory thry HOLThm+newBasicDefinition tm =+ do (c@(view -> Const name _ _), dth) <- liftEither "newBasicDefinition" $+ newDefinedConst tm+ newConstant' name c+ modifyExt $ \ (TheCoreDefinitions defs) -> + TheCoreDefinitions $ dth : defs+ return dth++{-|+ 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 :: String -> String -> String -> HOLThm -> + HOL Theory thry (HOLThm, HOLThm)+newBasicTypeDefinition tyname absname repname dth =+ do failWhen (return or <*> mapM (can getConstType) [absname, repname]) $+ "newBasicTypeDefinition: Constant(s) " ++ absname ++ ", " ++ repname +++ " already in use."+ (atyop, a, r, dth1, dth2) <- liftEither "newBasicTypeDefinition" $+ newDefinedTypeOp tyname absname repname dth+ failWhen (canNot (newType' tyname) atyop) $+ "newBasicTypeDefinition: Type " ++ tyname ++ " already defined."+ newConstant' absname a+ newConstant' repname r+ return (dth1, dth2)+++-- 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++deriveLiftMany [ ''TypeConstants, ''TermConstants+ , ''TheAxioms, ''TheCoreDefinitions ]
+ src/HaskHOL/Core/State/Monad.hs view
@@ -0,0 +1,673 @@+{-# LANGUAGE DeriveDataTypeable, EmptyDataDecls, ExistentialQuantification, + MultiParamTypeClasses, ScopedTypeVariables, TemplateHaskell #-}++{-|+ Module: HaskHOL.Core.State.Monad+ Copyright: (c) The University of Kansas 2013+ LICENSE: BSD3++ Maintainer: ecaustin@ittc.ku.edu+ Stability: unstable+ Portability: unknown++ This module exports the primitive types and combinators for the 'HOL' + computational monad. At a high level this monad is a flattened stack of a+ 'State' monad transformer and a limited 'IO' monad.++ For higher level monadic combinators see the "HaskHOL.Core.State" and+ "HaskHOL.Core.Basics" modules.+-}+module HaskHOL.Core.State.Monad+ ( -- * The HOL Monad+ HOL+ , Theory+ , Proof+ , runHOLCtxt -- :: HOLContext thry -> IO (a, HOLContext thry) + , evalHOLCtxt -- :: HOL cls thry a -> HOLContext thry -> IO a+ , execHOLCtxt -- :: HOL cls thry a -> HOLContext thry -> + -- IO (HOLContext thry)+ -- * State Methods+ , get -- :: HOL cls thry (HOLContext thry)+ , gets -- :: (HOLContext thry -> a) -> HOL cls thry a+ -- * Text Output Methods+ , putStrHOL -- :: String -> HOL cls thry ()+ , putStrLnHOL -- :: String -> HOL cls thry ()+ -- * Exception Handling Methods+ , HOLException(..)+ , throwHOL -- :: Exception e => e -> HOL cls thry a+ , catchHOL -- :: Exception e => HOL cls thry a -> (e -> HOL cls thry a) + -- -> HOL cls thry a+ , liftMaybe -- :: String -> Maybe a -> HOL cls thry a+ , liftEither -- :: Show err => String -> Either err a -> HOL cls thry a+ -- * Local Reference Methods+ , HOLRef+ , newHOLRef -- :: a -> HOL cls thry (HOLRef a)+ , readHOLRef -- :: IORef a -> HOL cls thry a+ , writeHOLRef -- :: IORef a -> a -> HOL cls thry ()+ , modifyHOLRef -- :: IORef a -> (a -> a) -> HOL cls thry ()+ -- * Benign Flag Methods+ , BenignFlag(..)+ , setBenignFlag+ , unsetBenignFlag+ , getBenignFlagCtxt+ , getBenignFlag+ -- * Methods Related to Fresh Name Generation+ , tickTermCounter -- :: HOL cls thry Int+ , tickTypeCounter -- :: HOL cls thry Int+ -- * Extensible State Methods+ -- $ExtState+ , ExtClass(..)+ , ExtState+ , putExt -- :: ExtClass a => a -> HOL Theory thry ()+ , getExtCtxt -- :: forall a thry. ExtClass a => HOLContext thry -> Maybe a+ , getExt -- :: forall cls thry a. ExtClass a => HOL cls thry a+ , modifyExt -- :: ExtClass a => (a -> a) -> HOL Theory thry ()+ -- * Implementation of Theory Contexts+ , HOLContext+ , ctxtBase -- :: HOLContext BaseThry+ , ExtThry(..)+ , BaseThry(..)+ , BaseCtxt+ -- * Template Haskell Assistance for Flags/Extensions+ , newFlag -- :: String -> Bool -> Q [Dec]+ , newExtension -- :: String -> ExpQ -> Q [Dec]+ -- * Re-export for Extensible Exceptions+ , Exception+ ) where++import HaskHOL.Core.Lib++import Control.Exception (Exception)+import qualified Control.Exception as E++import Data.IORef+ +import Data.Typeable (cast, typeOf)+import Language.Haskell.TH+import Language.Haskell.TH.Syntax (Lift(..))+++-- Monad+-- HOL method types++{-|+ The 'HOL' monad structures computations in the HaskHOL system at the stateful+ layer and above. The type parameters are used as such:++ * @cls@ - 'HOL' computations are split into two classes, those that extend the+ current working theory and those that are \"pure\"-ly used for+ proof. The @cls@ parameter is used to indicate the classification+ of a computation. It is a phantom type variable that is inhabited+ by one of two empty data types, 'Theory' and 'Proof'.++ * @thry@ - Carries a tag indicating the most recent checkpoint of the current+ working theory, i.e. the last library loaded. Again, it is phantom+ type variable that is inhabited by an empty data type. A unique+ tag is created for each library by linerearly extending the tag+ starting from a base value. For example, the tag + @ExtThry EqualThry BaseThry@ would indicate a current working+ theory consisting of the base and equality logic theories.++ Note that typically this value is left polymorphic and is+ constrained by a class related to a library. For example, the+ following type indicates a computation that can only be ran by+ using a theory context value that has the equality logic library+ loaded: @EqualCtxt thry => HOL cls thry a@++ * @a@ - The return type of a 'HOL' computation.++ Note that the 'HOL' monad is effectively a flattened stack of a limited+ 'IO' monad and a 'State' monad. We say limited as we restrict the possible+ IO-like computations to the ones shown in this module, rather than allowing+ arbitrary computations through a mechanism like 'MonadIO'. This prevents a+ number of soundness issues.++ For more information regarding the contents of a theory context see the+ documentation for 'HOLContext'.+-}++newtype HOL cls thry a = + HOL { {-| + Evaluates a 'HOL' computation with a provided theory context.+ Returns the result paired with an updated theory context.+ -}+ runHOLCtxt :: HOLContext thry -> IO (a, HOLContext thry) + }++-- | The classification tag for theory extension computations.+data Theory+-- | The classification tag for proof computations.+data Proof++instance Functor (HOL cls thry) where+ fmap = liftM+ +instance Monad (HOL cls thry) where+ return x = HOL $ \ s -> + return (x, s)+ {-# INLINEABLE (>>=) #-}+ m >>= k = HOL $ \ s ->+ do (b, s') <- runHOLCtxt m s+ runHOLCtxt (k b) s'+ fail = throwHOL . HOLException++instance MonadPlus (HOL cls thry) where+ mzero = fail "mzero - HOL"+ mplus = (<||>)++instance Applicative (HOL cls thry) where+ pure = return+ (<*>) = ap++instance Alternative (HOL cls thry) where+ empty = fail "empty - HOL"+ (<|>) = (<||>)++instance Note (HOL cls thry) where+ job <?> str = job <|> throwHOL (HOLException str)++-- | A version of 'runHOLCtxt' that returns only the resultant value.+evalHOLCtxt :: HOL cls thry a -> HOLContext thry -> IO a+evalHOLCtxt m ctxt = return fst <*> runHOLCtxt m ctxt++-- | A version of 'runHOLCtxt' that returns only the theory context.+execHOLCtxt :: HOL cls thry a -> HOLContext thry -> IO (HOLContext thry)+execHOLCtxt m ctxt = return snd <*> runHOLCtxt m ctxt++{- + We define our own versions of state functions instead of deriving MonadState + so that we can control where they are exported. Note that put is not expose+ to the user.+-}+put :: HOLContext thry -> HOL cls thry ()+put s = HOL $ \ _ -> return ((), s)++{-| + Equivalent to 'Control.Monad.State.get' for the 'HOL' monad. Note that we+ define our own version of this function, rather than define an instance of+ 'MonadState' so that we can control where the morphisms are exported.++ This is done in the name of soundness given that a user can inject an unsound+ theory context into a proof using a @put@ morphism. This is analogous to the+ issue behind defining an instance of 'MonadIO' given 'liftIO' can be used to+ inject arbitrary computations into the 'HOL' monad, including ones containing+ unsound contexts.+-}+get :: HOL cls thry (HOLContext thry)+get = HOL $ \ s -> return (s, s)++{-| + A version of 'get' that applies a function to the state before returning the+ result.+-}+gets :: (HOLContext thry -> a) -> HOL cls thry a+gets f = liftM f get++-- See the above notes. Not exported to the user.+modify :: (HOLContext thry -> HOLContext thry) -> HOL cls thry ()+modify = put <=< gets++-- define own versions of IO functions so they can be used external to kernel+-- | A version of 'putStr' lifted to the 'HOL' monad.+putStrHOL :: String -> HOL cls thry ()+putStrHOL str = HOL $ \ s -> putStr str >> return ((), s)++-- | A version of 'putStrLn' lifted to the 'HOL' monad.+putStrLnHOL :: String -> HOL cls thry ()+putStrLnHOL str = HOL $ \ s -> putStrLn str >> return ((), s)++-- Errors++-- the basic HOL exception type+-- | The data type for generic errors in HaskHOL. Carries a 'String' message.+newtype HOLException = HOLException String deriving (Show, Typeable)+instance Exception HOLException++{-| + A version of 'throwIO' lifted to the 'HOL' monad. ++ Note that the following functions for the 'HOL' type rely on 'throwHOL':+ + * 'fail' - Equivalent to ++ > throwHOL . HOLException++ * 'mzero' - Equivalent to ++ > fail "mzero - HOL"++ * 'empty' - Equivalent to ++ > fail "empty - HOL"+-}+throwHOL :: Exception e => e -> HOL cls thry a+throwHOL e = HOL $ \ _ -> E.throwIO e++{-| + A version of 'E.catch' lifted to the 'HOL' monad.++ Note that 'mplus' and '<|>' are defined in terms of catching a + 'E.SomeException' with 'catchHOL' and then ignoring it to run an alternative+ computation instead.+-}+catchHOL :: Exception e => HOL cls thry a -> (e -> HOL cls thry a) -> + HOL cls thry a+catchHOL job errcase = HOL $ \ s ->+ runHOLCtxt job s `E.catch` \ e -> runHOLCtxt (errcase e) s++-- Used to define mplus and (<|>) for the HOL monad. Not exposed to the user.+(<||>) :: HOL cls thry a -> HOL cls thry a -> HOL cls thry a+job <||> alt = HOL $ \ s ->+ runHOLCtxt job s `E.catch` \ (_ :: E.SomeException) -> runHOLCtxt alt s++{-| + Lifts a 'Maybe' value into the 'HOL' monad mapping 'Just's to 'return's and+ 'Nothing's to 'fail's with the provided 'String'.+-}+{-# INLINEABLE liftMaybe #-}+liftMaybe :: String -> Maybe a -> HOL cls thry a+liftMaybe _ (Just x) = return x+liftMaybe str _ = fail str ++{-|+ Lifts an 'Either' value into the 'HOL' monad mapping 'Right's to 'return's+ and 'Left's to 'fail's. ++ Note that the value inside the 'Left' must have an instance of the 'Show' + class such that 'show' can be used to construct a string to be used with+ 'fail'.+-}+{-# INLINEABLE liftEither #-}+liftEither :: Show err => String -> Either err a -> HOL cls thry a+liftEither _ (Right res) = return res+liftEither str1 (Left str2) = fail $ str1 ++ " - " ++ show str2++-- Local vars+-- | A type synonym for 'IORef'.+type HOLRef = IORef++{-| + Creates a new 'HOLRef' from a given starting value. Functionally equivalent+ to 'newIORef' lifted to the 'HOL' monad.+-}+newHOLRef :: a -> HOL cls thry (HOLRef a)+newHOLRef x = HOL $ \ s ->+ do ref <- newIORef x+ return (ref, s)++{-|+ Reads a 'HOLRef' returning the stored value. Functionally equivalent to + 'readIORef' lifted to the 'HOL' monad.+-}+readHOLRef :: IORef a -> HOL cls thry a+readHOLRef ref = HOL $ \ s ->+ do res <- readIORef ref+ return (res, s)++{-|+ Writes a value to a 'HOLRef'. Functionally equivalent to 'writeHOLRef' lifted+ to the 'HOL' monad.+-}+writeHOLRef :: IORef a -> a -> HOL cls thry ()+writeHOLRef ref x = HOL $ \ s -> writeIORef ref x >> return ((), s)++{-|+ Applies a given function to a 'HOLRef', modifying the stored value.+ Functionally equivalent to 'modifyHOLRef' lifted to the 'HOL' monad.+-}+modifyHOLRef :: IORef a -> (a -> a) -> HOL cls thry ()+modifyHOLRef ref f = HOL $ \ s -> modifyIORef ref f >> return ((), s)+++-- Context+{-|+ The 'ExtClass' type class is the heart of HaskHOL's extensible state+ mechanism. It serves a number of purposes:++ * It provides the polymorphic type for heterogenous structures of type + 'ExtState'.++ * It introduces the 'Typeable' constraint that enables the mechanism for+ selecting specific state extensions based on their type. See 'getExt' for+ more details.++ * It defines an initial value for state extensions to use if they have not + been introduced to the context by a computation yet.++ For more information see the documentation for 'HOLContext', 'getExtCtxt', and+ 'putExt'.+-}+class (Lift a, Typeable a) => ExtClass a where+ {-| + The intial value for an extensible state type. The value returned when+ attempting to retrieve a type that is not yet defined in the context.+ -}+ initValue :: a++{-| + Used to build heterogenous structures that hold state extensions. See+ 'ExtClass' for more details.+-}+data ExtState = forall a. ExtClass a => ExtState a++{-|+ HOL systems typically use a large number of boolean flags in order to direct+ system behavior, i.e. debug flags, warning flags, parser/printer flags, etc.+ These flags don't affect the underlying proof computations, hence their+ classification as benign, so we'd like to be able to toggle them on and off+ at will. Unfortunately, if we store them in the extensible state and use + 'putExt' or 'modifyExt' we're limited to only being able to change them in+ 'Theory' computations. ++ Instead, we include them in a separate part of the theory context where we + can interact with them in any way we want without sacrificing the safety of + the extensible state portion of the context.++ The 'BenignFlag' class works very similarly to the 'ExtClass' class with the+ obvious exception that initial values are restricted to boolean values.++ See 'HOLContext', 'getBenignFlagCtxt', and 'setBenignFlag' for more details.+-}+class Typeable a => BenignFlag a where+ {-| + The intial value for a benign flag. The value returned when attempting to+ retrieve a flag that is not yet defined in the context.+ -}+ initFlagValue :: a -> Bool++{-|+ The state type for the 'HOL' monad. A newtype wrapper to the following quad:++ * An association 'List' of @('String', 'Bool')@ pairs that models HaskHOL's+ extensible benign flag system. The first field is a 'String' representation+ of the type of a benign flag and the second field is that flag's current+ value.++ * An 'Int' counter that is used for fresh name generation for type variables.++ * An 'Int' counter that is used for fresh name generation for term variables.++ * An association 'List' of @('String', 'ExtState')@ pairs that models + HaskHOL's extensible state. The first field is a 'String' representation of + the type of a state extension and the second field is a wrapping of that + type that has an instance of the 'ExtClass' class.++ See 'putExt' and 'getExtCtxt' for more details on how to interact with the+ extensible state and see 'setBenignFlag' and 'getBenignFlag' for more details+ on how to interact with benign flags.+-}+newtype HOLContext thry = + HCtxt ([(String, Bool)], Int, Int, [(String, ExtState)]) + deriving Typeable++-- manually derived to avoid needing lift instance for phantoms+instance Lift (HOLContext thry) where+ lift (HCtxt x) = conE 'HCtxt `appE` lift x++instance Show (HOLContext thry) where+ show (HCtxt (_, _, _, xs)) = show $ map fst xs++-- Benign Flag methods+-- used internally by set/unsetBenignFlag+modBenignFlag :: BenignFlag a => Bool -> a -> HOL cls thry ()+modBenignFlag val flag =+ modify (\ (HCtxt (flags, tm, ty, m)) ->+ HCtxt (insertMap (show $ typeOf flag) val flags, tm, ty, m))++{-|+ Adds a new, or modifies an existing, benign flag to be 'True'. Benign flags + in the context are stored as a list of @('String', 'Bool')@ pairs. The first + field in this pair is a term-level reificatino of a benign flag's type, + produced via a composition of 'show' and 'typeOf'. The second field is simply+ the current boolean value of the flag.++ Numerous usage examples can be found in both the "HaskHOL.Core.Parser.Lib" and+ "HaskHOL.Core.Printer" modules where flags are used to direct the behavior+ of the parsers and printers accordingly.++ Note that since the retrieval and storage of benign flags are driven by types,+ it is in the best interest of library implementors to guarantee that the types+ of their flags are unique. The easiest way to do this is to create a unique+ @data@ type for each flag. The type doesn't need to carry a payload, but it+ does need to provide a witness to the flag type. As such, it can either be+ a nullary, punned data declaration, i.e. @data X = X@, or an empty data + declaration with a type annotated instance of 'undefined' acting as the+ ness, i.e. @undefined :: X@.++ Example:++ > setBenignFlag FlagDebug++ would set the debugging flag equal to 'True'.++ Alternatively, the 'newFlag' splice can be used to automatically construct a + new extension given a name and initial value. See that function's + documentation for more information.+-}+setBenignFlag :: BenignFlag a => a -> HOL cls thry ()+setBenignFlag = modBenignFlag True++-- | Unsets a benign flag making it 'False'.+unsetBenignFlag :: BenignFlag a => a -> HOL cls thry ()+unsetBenignFlag = modBenignFlag False++{-|+ Retrieves the value of a benign flag from a theory context. This function is+ typically used external to 'HOL' computations, such as in the parser and + printer.++ Note that retrieval of the value requires a witness to the desired flag's+ type, i.e.++ > getBenignFlag FlagDebug++ or++ > getBenignFlag (undefined :: FlagDebug)++ In the event that the flag is not found then the 'initFlagValue' for that type+ is returned. Thus, this function never fails.+-}+getBenignFlagCtxt :: forall a thry. BenignFlag a => + a -> HOLContext thry -> Bool+getBenignFlagCtxt flag (HCtxt (flags, _, _, _)) =+ fromMaybe (initFlagValue flag) $ + lookup (show $ typeOf flag) flags++{-|+ A version of 'getBenignFlagCtxt' that can be used with theory contexts passed+ implicitly as part of a 'HOL' computation.+ + Never fails.+-}+getBenignFlag :: BenignFlag a => a -> HOL cls thry Bool+getBenignFlag = gets . getBenignFlagCtxt++-- Fresh Name Generation+{-| + Increments the term counter stored in the context, returning the new value.+ Can be used to guarantee the freshness of term names within a single + computation.+-}+tickTermCounter :: HOL cls thry Int+tickTermCounter =+ do (HCtxt (f, tm, ty, s)) <- get+ let tm' = succ tm+ put $ HCtxt (f, tm', ty, s)+ return tm'++{-|+ Increments the type counter stored in the context, returning the new value.+ Can be used to gurantee the freshness of type names within a single+ computation.+-}+tickTypeCounter :: HOL cls thry Int+tickTypeCounter =+ do (HCtxt (f, tm, ty, s)) <- get+ let ty' = succ ty+ put $ HCtxt (f, tm, ty', s)+ return ty'++-- Context: Extensible State+{- $ExtState+ HaskHOL's extensible state mechanism is based closely on the implementation + of extensible state found in XMonad.++ In the event that the relevant documentation from 'ExtClass', 'putExt', and+ 'getExtCtxt' is confusing or not sufficient, it may be helpful to review the+ documentation contained in the "XMonad.Util.ExtensibleState" module.+-}++{-|+ Adds a new, or modifies an existing, state extension. State extensions in the+ context are stored as a list of @('String', 'ExtState')@ pairs. The first + field in this pair is a term-level reification of a state extension's type, + produced via a composition of 'show' and 'typeOf'. The second field is simply+ a wrapping of the extension's value with 'ExtState' to facilitate + heterogeneous structures.++ Numerous usage examples can be found in the "HaskHOL.Core.Parser.Lib" module+ where extensible state is used to store the list of operators, as well as+ other information, required by the parser.++ Note that since the retrieval and storage of state extensions are driven by + types, it is in the best interest of library implementors to guarantee that+ the type of their extensions are unique. The easiest way to do this is to+ create a @newtype@ wrapper for your extension and hide the internal+ constructor to prevent unintended modification. Again, see + "HaskHOL.Core.Parser.Lib" for usage examples.++ Alternatively, the 'newExtension' splice can be used to automatically+ construct a new extension given a name and initial value. See that function's+ documentation for more information.+-}+putExt :: ExtClass a => a -> HOL Theory thry ()+putExt val = + modify (\ (HCtxt (b, tm, ty, m)) -> + HCtxt (b, tm, ty, insertMap (show . typeOf $ val) (ExtState val) m))++{-|+ Retrives a state extension from a theory context. This function is typically + used external to 'HOL' computations, such as in the parser, where+ a theory context is passed explicitly as a value.++ Note that the selection of the extension is driven by the return type of this + function. Thus when binding the result of this function, the type must be + fixed either via explicit type annotation or through the presence of a unique + constructor.++ In order to provide the correct result type, this function relies on the+ type-safe 'cast' operation. In the event that either this cast fails or the + state extension is not found then the 'initValue' for that type is returned.+ Thus, this function never fails.+-}+getExtCtxt :: forall a thry. ExtClass a => HOLContext thry -> a+getExtCtxt (HCtxt (_, _, _, ctxt)) =+ fromMaybe initValue $+ do (ExtState val) <- lookup (show $ typeOf (undefined :: a)) ctxt+ cast val+ +{-|+ A version of 'getExtCtxt' that can be used with theory contexts passed+ implicitly as part of a 'HOL' computation.++ Never fails.+-} +getExt :: ExtClass a => HOL cls thry a+getExt = gets getExtCtxt+ +{-| + Modifies the value of a state extension. Functionally equivalent to the+ composition ++ > \ f -> putExt . f =<< getExt+-}+modifyExt :: ExtClass a => (a -> a) -> HOL Theory thry ()+modifyExt f = putExt . f =<< getExt++-- Initial Context+-- | The 'BaseThry' type is the type of the initial working theory.+data BaseThry = BaseThry deriving Typeable+{-| + The 'ExtThry' type is the type of a linear theory extension, i.e. a cons-like+ operation for theory types. See the module "HaskHOL.Lib.Equal.Context" for+ an example of how to correctly define theory types and contexts for a library.+-}+data ExtThry a b = ExtThry a b deriving Typeable++{-|+ The 'BaseCtxt' class is the context name associated with the 'BaseThry' type,+ i.e. the constraint to be used to guarantee that the stateful kernel has been+ loaded. This should always be true.+-}+class BaseCtxt a+instance BaseCtxt BaseThry+instance BaseCtxt b => BaseCtxt (ExtThry a b)++{-| + The initial working theory value: debugging is on, the counters are at zero + and the extensible state is empty.+-}+ctxtBase :: HOLContext BaseThry+ctxtBase = HCtxt ([], 0, 0, [])++-- Some TH wizardry+{-|+ The 'newFlag' splice can be used to automatically construct a new benign flag+ given a name and an initial flag value.++ Example:++ > newFlag "FlagDebug" True++ will construct the following Haskell code:++ > data FlagDebug = FlagDebug deriving Typeable+ > instance BenignFlag FlagDebug where+ > initFlagValue _ = True+-}+newFlag :: String -> Bool -> Q [Dec]+newFlag flag val =+ do val' <- lift val+ let name = mkName flag+ ty = DataD [] name [] [NormalC name []] [''Typeable]+ cls = InstanceD [] (AppT (ConT ''BenignFlag) (ConT name)) + [FunD 'initFlagValue [Clause [WildP] (NormalB val') []]]+ return [ty, cls]++{-|+ The 'newExtension' splice can be used to automatically construct a new state+ extension given a name and a quoted, type annotated, initial value. The type+ annotation is required as many initial values, such as an empty list, are too+ polymorphic to infer the correct type on its own.++ Example:++ > newExtension "TheCoreDefinitions" [| [] :: [HOLThm] |]++ will construct the following Haskell code:++ > newtype TheCoreDefinitions = TheCoreDefinitions [HOLThm] deriving Typeable+ > instance ExtClass TheCoreDefinitions where+ > initValue = TheCoreDefinitions []++ Note that, due to limitations with the current version of Template Haskell,+ 'Lift' instances should be derived external to this splice via 'deriveLift' or+ 'deriveLiftMany'.+-}+newExtension :: String -> ExpQ -> Q [Dec]+newExtension ext val =+ do val' <- val+ case val' of+ SigE e eTy -> + let name = mkName ext+ ty = NewtypeD [] name [] + (NormalC name [(NotStrict, eTy)]) [''Typeable]+ extCls = InstanceD [] (ConT ''ExtClass `AppT` ConT name)+ [ValD (VarP 'initValue) (NormalB $+ ConE name `AppE` e) []] in+ return [ty, extCls]+ _ -> fail "newExtension: provided value must be annotated with a type."++-- lift derivations+deriveLift ''ExtState