|
| 1 | +-- SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +-- SPDX-FileCopyrightText: 2025 Jonathan D.A. Jewell |
| 3 | +-- Calculus.idr - λ_consent calculus: syntax, types, values, handler algebra |
| 4 | +-- |
| 5 | +-- Mechanises Definition 1 (Syntax) from §7 of arxiv-consent-aware-programming.tex. |
| 6 | +-- The calculus extends the simply-typed lambda calculus with four consent-specific |
| 7 | +-- forms: only_if_okay, consent_handler, attr, and feel. |
| 8 | + |
| 9 | +module WokeLang.Calculus |
| 10 | + |
| 11 | +%default total |
| 12 | + |
| 13 | +-- --------------------------------------------------------------------------- |
| 14 | +-- Consent decisions |
| 15 | +-- --------------------------------------------------------------------------- |
| 16 | + |
| 17 | +||| The binary decision a consent handler produces for any given prompt. |
| 18 | +||| Handlers are required to be total — they always return one of these. |
| 19 | +public export |
| 20 | +data ConsentDecision : Type where |
| 21 | + Granted : ConsentDecision |
| 22 | + Denied : ConsentDecision |
| 23 | + |
| 24 | +-- --------------------------------------------------------------------------- |
| 25 | +-- Consent handlers (Definition 1, handler forms) |
| 26 | +-- --------------------------------------------------------------------------- |
| 27 | + |
| 28 | +||| A consent handler maps a prompt string to a consent decision. |
| 29 | +||| Four canonical forms: |
| 30 | +||| HInteractive — defers to the user (modelled as Granted here) |
| 31 | +||| HPolicy p — applies a policy predicate |
| 32 | +||| HAutoGranted — always grants (auto(granted)) |
| 33 | +||| HAutoDenied — always denies (auto(denied)) |
| 34 | +public export |
| 35 | +data Handler : Type where |
| 36 | + HInteractive : Handler |
| 37 | + HPolicy : (String -> ConsentDecision) -> Handler |
| 38 | + HAutoGranted : Handler |
| 39 | + HAutoDenied : Handler |
| 40 | + |
| 41 | +||| Apply a handler to a consent prompt string. |
| 42 | +||| This function is total by construction: every case returns a decision. |
| 43 | +public export |
| 44 | +applyHandler : Handler -> String -> ConsentDecision |
| 45 | +applyHandler HInteractive _ = Granted |
| 46 | +applyHandler (HPolicy p) s = p s |
| 47 | +applyHandler HAutoGranted _ = Granted |
| 48 | +applyHandler HAutoDenied _ = Denied |
| 49 | + |
| 50 | +-- --------------------------------------------------------------------------- |
| 51 | +-- Types (Definition 1, type syntax) |
| 52 | +-- --------------------------------------------------------------------------- |
| 53 | + |
| 54 | +||| λ_consent types. |
| 55 | +||| TyConsent τ — result gated behind a consent decision |
| 56 | +||| TyDenied — type of the `denied` terminal value |
| 57 | +public export |
| 58 | +data Ty : Type where |
| 59 | + TyInt : Ty |
| 60 | + TyStr : Ty |
| 61 | + TyBool : Ty |
| 62 | + TyFun : Ty -> Ty -> Ty |
| 63 | + TyConsent : Ty -> Ty |
| 64 | + TyDenied : Ty |
| 65 | + |
| 66 | +-- --------------------------------------------------------------------------- |
| 67 | +-- Variable names |
| 68 | +-- --------------------------------------------------------------------------- |
| 69 | + |
| 70 | +||| Variables are represented as natural numbers. |
| 71 | +public export |
| 72 | +Var : Type |
| 73 | +Var = Nat |
| 74 | + |
| 75 | +-- --------------------------------------------------------------------------- |
| 76 | +-- Expressions (Definition 1, expression syntax) |
| 77 | +-- --------------------------------------------------------------------------- |
| 78 | + |
| 79 | +||| λ_consent expression syntax. |
| 80 | +||| |
| 81 | +||| Core forms: |
| 82 | +||| EVar x — variable (de-Bruijn-named) |
| 83 | +||| ELam x τ e — lambda abstraction |
| 84 | +||| EApp f a — application |
| 85 | +||| ELit n — integer literal |
| 86 | +||| EAdd e1 e2 — integer addition (representative binary op) |
| 87 | +||| |
| 88 | +||| Consent-specific forms: |
| 89 | +||| EOnlyIfOkay s e — consent gate: only_if_okay s e |
| 90 | +||| EConsentHandler h e — handler install: consent_handler h e |
| 91 | +||| EAttr p d e — attribution annotation: attr p d e |
| 92 | +||| EFeel f e — empathy annotation: feel f e (phantom at runtime) |
| 93 | +||| |
| 94 | +||| Terminal values: |
| 95 | +||| EDenied — the `denied` terminal produced by a denied consent gate |
| 96 | +public export |
| 97 | +data Expr : Type where |
| 98 | + EVar : Var -> Expr |
| 99 | + ELam : Var -> Ty -> Expr -> Expr |
| 100 | + EApp : Expr -> Expr -> Expr |
| 101 | + ELit : Int -> Expr |
| 102 | + EAdd : Expr -> Expr -> Expr |
| 103 | + EOnlyIfOkay : String -> Expr -> Expr |
| 104 | + EConsentHandler : Handler -> Expr -> Expr |
| 105 | + EAttr : String -> String -> Expr -> Expr |
| 106 | + EFeel : String -> Expr -> Expr |
| 107 | + EDenied : Expr |
| 108 | + |
| 109 | +-- --------------------------------------------------------------------------- |
| 110 | +-- Values (Definition 1, value syntax) |
| 111 | +-- --------------------------------------------------------------------------- |
| 112 | + |
| 113 | +||| Values are fully-reduced expressions: lambdas, literals, and denied. |
| 114 | +||| `denied` is a value because E-ConsentDenied produces it as a terminal. |
| 115 | +public export |
| 116 | +data IsValue : Expr -> Type where |
| 117 | + VLam : IsValue (ELam x τ e) |
| 118 | + VLit : IsValue (ELit n) |
| 119 | + VDenied : IsValue EDenied |
| 120 | + |
| 121 | +-- --------------------------------------------------------------------------- |
| 122 | +-- Capture-avoiding substitution |
| 123 | +-- --------------------------------------------------------------------------- |
| 124 | + |
| 125 | +||| e[v/x] — substitute v for x in e. |
| 126 | +||| Precondition (caller's responsibility): v is closed. |
| 127 | +||| Lambda bodies where the binder shadows x are left unchanged. |
| 128 | +public export |
| 129 | +subst : (x : Var) -> (v : Expr) -> (e : Expr) -> Expr |
| 130 | +subst x v (EVar y) = |
| 131 | + case decEq x y of |
| 132 | + Yes _ => v |
| 133 | + No _ => EVar y |
| 134 | +subst x v (ELam y τ body) = |
| 135 | + case decEq x y of |
| 136 | + Yes _ => ELam y τ body -- x is shadowed; do not descend |
| 137 | + No _ => ELam y τ (subst x v body) |
| 138 | +subst x v (EApp f a) = EApp (subst x v f) (subst x v a) |
| 139 | +subst _ _ (ELit n) = ELit n |
| 140 | +subst x v (EAdd e1 e2) = EAdd (subst x v e1) (subst x v e2) |
| 141 | +subst x v (EOnlyIfOkay s e) = EOnlyIfOkay s (subst x v e) |
| 142 | +subst x v (EConsentHandler h e) = EConsentHandler h (subst x v e) |
| 143 | +subst x v (EAttr p d e) = EAttr p d (subst x v e) |
| 144 | +subst x v (EFeel f e) = EFeel f (subst x v e) |
| 145 | +subst _ _ EDenied = EDenied |
| 146 | + |
| 147 | +-- --------------------------------------------------------------------------- |
| 148 | +-- Audit log |
| 149 | +-- --------------------------------------------------------------------------- |
| 150 | + |
| 151 | +||| A single entry in the audit accumulator A. |
| 152 | +||| ConsentEntry records a handler's decision for a given prompt. |
| 153 | +||| AttrEntry records attribution (person, description) when attr is evaluated. |
| 154 | +public export |
| 155 | +data LogEntry : Type where |
| 156 | + ConsentEntry : ConsentDecision -> String -> LogEntry |
| 157 | + AttrEntry : String -> String -> LogEntry |
| 158 | + |
| 159 | +||| The audit accumulator A — an ordered sequence of log entries. |
| 160 | +public export |
| 161 | +AuditLog : Type |
| 162 | +AuditLog = List LogEntry |
0 commit comments