Download src/Assertica/Core/Assertion.hs from Snapkitty/assertica: direct link, hf CLI and curl.
- Browser
- Download file 14.4 kB
-
https://huggingface.co/Snapkitty/assertica/resolve/main/src/Assertica/Core/Assertion.hs
- Command line
-
hf download hf://Snapkitty/assertica/src/Assertica/Core/Assertion.hs
-
curl -L -o Assertion.hs https://huggingface.co/Snapkitty/assertica/resolve/main/src/Assertica/Core/Assertion.hs
14.4 kB
| {-| | |
| Module : Assertica.Core.Assertion | |
| Description : Explicit assertion system for mathematical obligations | |
| Copyright : (c) 2026 Ahmad Ali Parr | |
| License : BSL-1.0 | |
| The assertion system represents mathematical obligations as first-class program entities. | |
| An assertion is an explicit proof goal that must be discharged by the proof-term checker. | |
| Key design principles: | |
| 1. Explicit representation: Every assertion is a data structure, not a string or comment. | |
| 2. Fail-closed: An unknown or malformed assertion is UNPROVEN, not silently accepted. | |
| 3. Proof obligation boundary: The assertion clearly identifies what must be proven. | |
| 4. Composable propositions: Build complex propositions from simpler ones. | |
| 5. No hidden theorem proving: Assertions state obligations; the proof checker discharges them. | |
| Integration: | |
| - Input: Elaborator (Agent 3A) produces surface assertions | |
| - Processing: This module represents obligations | |
| - Output: Proof-term checker (Agent 2B) discharges proof obligations | |
| -} | |
| module Assertica.Core.Assertion | |
| ( -- * Core types | |
| Assertion(..) | |
| , ProofObligation(..) | |
| , ObligationState(..) | |
| , ObligationStore | |
| -- * Assertion construction and querying | |
| , mkAssertion | |
| , assertionName | |
| , assertionProp | |
| , assertionLoc | |
| , getObligations | |
| , lookupObligation | |
| , countByState | |
| -- * Proposition utilities | |
| , propFreeVariables | |
| , propBoundVariables | |
| , propSubstitute | |
| , propAlphaRename | |
| , prettyPrintProposition | |
| -- * Obligation tracking | |
| , emptyStore | |
| , insertObligation | |
| , updateObligation | |
| , markProven | |
| , markUnproven | |
| , markRejected | |
| , allProven | |
| , unprovenObligations | |
| -- * Elaboration interface | |
| , SurfaceAssertion(..) | |
| , elaborateAssertion | |
| -- * Re-exports from AST | |
| , Proposition(..) | |
| , Term(..) | |
| , Var(..) | |
| , SourceLoc(..) | |
| ) where | |
| import qualified Data.Map.Strict as Map | |
| import qualified Data.Set as Set | |
| import Data.Typeable (Typeable) | |
| import GHC.Generics (Generic) | |
| import Data.List (intercalate) | |
| import Assertica.Core.AST | |
| ( Proposition(..) | |
| , Term(..) | |
| , Var(..) | |
| , SourceLoc(..) | |
| , freeVars | |
| , substitute | |
| , alphaRename | |
| ) | |
| -- ============================================================================ | |
| -- Core Assertion Type | |
| -- ============================================================================ | |
| -- | An assertion is a named proof obligation with a source location | |
| data Assertion = Assertion | |
| { assertionName :: String | |
| -- ^ Unique name for the assertion | |
| , assertionProp :: Proposition | |
| -- ^ The proposition to be proven | |
| , assertionLoc :: SourceLoc | |
| -- ^ Source location for error reporting | |
| } | |
| deriving (Eq, Show, Generic, Typeable) | |
| -- | Proof obligation state tracking | |
| data ObligationState | |
| = Asserted -- ^ The obligation has been stated but not yet proven | |
| | Proven -- ^ A valid proof has been accepted | |
| | Unproven -- ^ The proof attempt was invalid or no proof provided | |
| | Rejected -- ^ The obligation was explicitly rejected | |
| deriving (Eq, Show, Ord, Generic, Typeable) | |
| -- | A proof obligation with its current state | |
| data ProofObligation = ProofObligation | |
| { obligationAssertion :: Assertion | |
| , obligationState :: ObligationState | |
| , obligationProofTerm :: Maybe String | |
| -- ^ Optional reference to the proof term that discharges this obligation | |
| } | |
| deriving (Eq, Show, Generic, Typeable) | |
| -- | Storage for proof obligations | |
| -- Maps assertion names to their obligations | |
| type ObligationStore = Map.Map String ProofObligation | |
| -- ============================================================================ | |
| -- Assertion Construction | |
| -- ============================================================================ | |
| -- | Create an assertion with default Asserted state | |
| mkAssertion :: String -> Proposition -> SourceLoc -> Assertion | |
| mkAssertion name prop loc = Assertion name prop loc | |
| -- ============================================================================ | |
| -- Obligation Store Operations | |
| -- ============================================================================ | |
| -- | Create an empty obligation store | |
| emptyStore :: ObligationStore | |
| emptyStore = Map.empty | |
| -- | Insert a new obligation into the store | |
| insertObligation :: Assertion -> ObligationStore -> ObligationStore | |
| insertObligation assertion store = | |
| let obligation = ProofObligation assertion Asserted Nothing | |
| in Map.insert (assertionName assertion) obligation store | |
| -- | Look up an obligation by assertion name | |
| lookupObligation :: String -> ObligationStore -> Maybe ProofObligation | |
| lookupObligation = Map.lookup | |
| -- | Get all obligations from the store | |
| getObligations :: ObligationStore -> [ProofObligation] | |
| getObligations = Map.elems | |
| -- | Update an obligation's state | |
| updateObligation :: String -> ObligationState -> Maybe String -> ObligationStore -> Either String ObligationStore | |
| updateObligation name state proofTerm store = | |
| case Map.lookup name store of | |
| Nothing -> Left $ "Obligation not found: " ++ name | |
| Just obligation -> | |
| let updated = obligation { obligationState = state, obligationProofTerm = proofTerm } | |
| in Right $ Map.insert name updated store | |
| -- | Mark an obligation as proven | |
| markProven :: String -> Maybe String -> ObligationStore -> Either String ObligationStore | |
| markProven name proofTerm = updateObligation name Proven proofTerm | |
| -- | Mark an obligation as unproven | |
| markUnproven :: String -> ObligationStore -> Either String ObligationStore | |
| markUnproven name store = updateObligation name Unproven Nothing store | |
| -- | Mark an obligation as rejected | |
| markRejected :: String -> ObligationStore -> Either String ObligationStore | |
| markRejected name store = updateObligation name Rejected Nothing store | |
| -- | Check if all obligations are proven | |
| allProven :: ObligationStore -> Bool | |
| allProven store = all (\ob -> obligationState ob == Proven) (getObligations store) | |
| -- | Get all unproven obligations | |
| unprovenObligations :: ObligationStore -> [ProofObligation] | |
| unprovenObligations store = | |
| filter (\ob -> obligationState ob /= Proven) (getObligations store) | |
| -- | Count obligations by state | |
| countByState :: ObligationState -> ObligationStore -> Int | |
| countByState state store = | |
| length $ filter (\ob -> obligationState ob == state) (getObligations store) | |
| -- ============================================================================ | |
| -- Proposition Utilities | |
| -- ============================================================================ | |
| -- | Free variables in a proposition | |
| propFreeVariables :: Proposition -> Set.Set Var | |
| propFreeVariables (EqualityProp t1 t2) = Set.union (freeVars t1) (freeVars t2) | |
| propFreeVariables (UniversalQuantProp v prop) = | |
| Set.delete v (propFreeVariables prop) | |
| propFreeVariables (ImplicationProp p q) = | |
| Set.union (propFreeVariables p) (propFreeVariables q) | |
| propFreeVariables (ConjunctionProp p q) = | |
| Set.union (propFreeVariables p) (propFreeVariables q) | |
| propFreeVariables (TypingProp t _) = freeVars t | |
| propFreeVariables (PredicateApp _ terms) = | |
| Set.unions $ map freeVars terms | |
| propFreeVariables (NegationProp p) = propFreeVariables p | |
| propFreeVariables (DisjunctionProp p q) = | |
| Set.union (propFreeVariables p) (propFreeVariables q) | |
| -- | Bound variables in a proposition | |
| propBoundVariables :: Proposition -> Set.Set Var | |
| propBoundVariables (EqualityProp t1 t2) = Set.empty -- Terms have their own binding | |
| propBoundVariables (UniversalQuantProp v prop) = | |
| Set.insert v (propBoundVariables prop) | |
| propBoundVariables (ImplicationProp p q) = | |
| Set.union (propBoundVariables p) (propBoundVariables q) | |
| propBoundVariables (ConjunctionProp p q) = | |
| Set.union (propBoundVariables p) (propBoundVariables q) | |
| propBoundVariables (TypingProp _ _) = Set.empty | |
| propBoundVariables (PredicateApp _ _) = Set.empty | |
| propBoundVariables (NegationProp p) = propBoundVariables p | |
| propBoundVariables (DisjunctionProp p q) = | |
| Set.union (propBoundVariables p) (propBoundVariables q) | |
| -- | Substitute a variable in a proposition | |
| -- Must handle binding correctly to avoid variable capture | |
| propSubstitute :: Var -> Term -> Proposition -> Proposition | |
| propSubstitute var replacement prop = go prop | |
| where | |
| go (EqualityProp t1 t2) = | |
| EqualityProp (substitute var replacement t1) (substitute var replacement t2) | |
| go (UniversalQuantProp v p) | |
| | v == var = UniversalQuantProp v p -- Bound variable shadows the substitution | |
| | v `Set.member` freeVars replacement = | |
| -- Need to rename v to avoid capture | |
| let freshVar = freshenPropVar v prop replacement | |
| in UniversalQuantProp freshVar (propSubstitute var replacement (propAlphaRename v freshVar p)) | |
| | otherwise = UniversalQuantProp v (go p) | |
| go (ImplicationProp p q) = | |
| ImplicationProp (go p) (go q) | |
| go (ConjunctionProp p q) = | |
| ConjunctionProp (go p) (go q) | |
| go (TypingProp t ty) = | |
| TypingProp (substitute var replacement t) ty | |
| go (PredicateApp name terms) = | |
| PredicateApp name (map (substitute var replacement) terms) | |
| go (NegationProp p) = | |
| NegationProp (go p) | |
| go (DisjunctionProp p q) = | |
| DisjunctionProp (go p) (go q) | |
| -- | Alpha-rename a variable in a proposition | |
| propAlphaRename :: Var -> Var -> Proposition -> Proposition | |
| propAlphaRename oldVar newVar = go | |
| where | |
| go (EqualityProp t1 t2) = | |
| EqualityProp (alphaRename oldVar newVar t1) (alphaRename oldVar newVar t2) | |
| go (UniversalQuantProp v p) | |
| | v == oldVar = UniversalQuantProp newVar (go p) | |
| | otherwise = UniversalQuantProp v (go p) | |
| go (ImplicationProp p q) = ImplicationProp (go p) (go q) | |
| go (ConjunctionProp p q) = ConjunctionProp (go p) (go q) | |
| go (TypingProp t ty) = TypingProp (alphaRename oldVar newVar t) ty | |
| go (PredicateApp name terms) = | |
| PredicateApp name (map (alphaRename oldVar newVar) terms) | |
| go (NegationProp p) = NegationProp (go p) | |
| go (DisjunctionProp p q) = DisjunctionProp (go p) (go q) | |
| -- | Generate a fresh variable for propositions | |
| freshenPropVar :: Var -> Proposition -> Term -> Var | |
| freshenPropVar v prop replacement = | |
| let occupied = Set.union (propFreeVariables prop) (freeVars replacement) | |
| Var varName = v | |
| candidates = map (\n -> Var (varName ++ "_" ++ show n)) [1..] | |
| in head [c | c <- candidates, c `Set.notMember` occupied] | |
| -- ============================================================================ | |
| -- Pretty Printing | |
| -- ============================================================================ | |
| -- | Pretty-print a proposition for human-readable output | |
| prettyPrintProposition :: Proposition -> String | |
| prettyPrintProposition (EqualityProp t1 t2) = | |
| prettyTerm t1 ++ " ≡ " ++ prettyTerm t2 | |
| prettyPrintProposition (UniversalQuantProp v prop) = | |
| "∀ " ++ prettyVar v ++ ". " ++ prettyPrintProposition prop | |
| prettyPrintProposition (ImplicationProp p q) = | |
| "(" ++ prettyPrintProposition p ++ " → " ++ prettyPrintProposition q ++ ")" | |
| prettyPrintProposition (ConjunctionProp p q) = | |
| "(" ++ prettyPrintProposition p ++ " ∧ " ++ prettyPrintProposition q ++ ")" | |
| prettyPrintProposition (TypingProp t ty) = | |
| prettyTerm t ++ " : " ++ ty | |
| prettyPrintProposition (PredicateApp name terms) = | |
| name ++ "(" ++ intercalate ", " (map prettyTerm terms) ++ ")" | |
| prettyPrintProposition (NegationProp p) = | |
| "¬(" ++ prettyPrintProposition p ++ ")" | |
| prettyPrintProposition (DisjunctionProp p q) = | |
| "(" ++ prettyPrintProposition p ++ " ∨ " ++ prettyPrintProposition q ++ ")" | |
| -- | Pretty-print a term (minimal) | |
| prettyTerm :: Term -> String | |
| prettyTerm (TVar v) = prettyVar v | |
| prettyTerm (TAbs v body) = "(λ" ++ prettyVar v ++ ". " ++ prettyTerm body ++ ")" | |
| prettyTerm (TApp f x) = "(" ++ prettyTerm f ++ " " ++ prettyTerm x ++ ")" | |
| prettyTerm (TConst c) = c | |
| prettyTerm (TLet v val body) = "(let " ++ prettyVar v ++ " = " ++ prettyTerm val ++ " in " ++ prettyTerm body ++ ")" | |
| prettyTerm (TPair a b) = "(" ++ prettyTerm a ++ ", " ++ prettyTerm b ++ ")" | |
| prettyTerm (TFst t) = "fst(" ++ prettyTerm t ++ ")" | |
| prettyTerm (TSnd t) = "snd(" ++ prettyTerm t ++ ")" | |
| -- | Pretty-print a variable | |
| prettyVar :: Var -> String | |
| prettyVar (Var name) = name | |
| -- ============================================================================ | |
| -- Elaboration Interface | |
| -- ============================================================================ | |
| -- | Surface-level assertion (produced by elaborator, Agent 3A) | |
| -- This is the input type for elaboration | |
| data SurfaceAssertion = SurfaceAssertion | |
| { surfaceName :: String | |
| -- ^ The assertion name | |
| , surfacePropositionText :: String | |
| -- ^ The proposition as text (needs parsing by Agent 3A) | |
| , surfaceSourceLoc :: SourceLoc | |
| -- ^ Source location | |
| } | |
| deriving (Eq, Show, Generic, Typeable) | |
| -- | Elaboration interface | |
| -- This signature shows how surface assertions are converted to core assertions | |
| -- Implementation of this function is the responsibility of Agent 3A | |
| -- when they implement the parser/elaborator | |
| -- | |
| -- Type signature for Agent 3A to implement: | |
| -- elaborateSurfaceAssertion :: SurfaceAssertion -> Either String Assertion | |
| -- | |
| -- For now, we provide a stub that always fails with an appropriate message | |
| elaborateAssertion :: SurfaceAssertion -> Either String Assertion | |
| elaborateAssertion surface = | |
| Left $ "Elaboration not yet implemented. " | |
| ++ "Agent 3A must implement parsing of: " | |
| ++ surfacePropositionText surface | |
| -- ============================================================================ | |
| -- Integration Interface for Agent 2B (Proof-Term Checker) | |
| -- ============================================================================ | |
| {-| | |
| Proof-term checker integration (Agent 2B) | |
| The proof checker receives: | |
| 1. A Proposition (the obligation to discharge) | |
| 2. A ProofTerm (the evidence for the proposition) | |
| Expected type signature in Agent 2B's module: | |
| proofChecks :: ProofTerm -> Proposition -> Either String () | |
| Invariants the proof checker assumes about well-formed propositions: | |
| - All bound variables are properly scoped (no captures) | |
| - Free variables are explicitly mentioned in universal quantifications | |
| - Terms in equality propositions are well-typed (type checking by Agent 3B) | |
| - Predicate applications reference known predicates | |
| - No circular references in implication chains | |
| When a proof is invalid or the proposition is malformed, the checker | |
| MUST return a descriptive error message, never silently reject. | |
| -} | |