Skip to content

Commit ecda96e

Browse files
committed
proof(SafeJson): drop unnecessary total change; keep minimal appendLengthInc fix
Aligns this branch with the verified-minimal change set (matching the fix on #138's branch). Supersedes the previous commit, which marked getPath/getSegment `total` and claimed that annotation was required for singleKeyPath to discharge. That claim was wrong. Verified against a clean Idris2 0.8.0 + contrib toolchain: with appendLengthInc fixed, SafeJson.Proofs type-checks with getPath/getSegment left `covering` (the module default) — 0 errors across all five SafeJson modules. The covering annotation does not block the clause-level reduction singleKeyPath relies on, so no totality change is needed. Access.idr is therefore reverted to its main state (untouched). The only substantive fix is the one this branch actually needs: appendLengthInc used `lengthSnoc arr v`, but Data.List.Equalities.lengthSnoc is element-first (`lengthSnoc x xs`); `lengthSnoc arr v` fails to type-check under Idris2 0.8.0 contrib and blocks the whole module. Corrected to `lengthSnoc v arr`. Proof comments for singleKeyPath and anyMatchesTAny corrected to match (no false "they are total" claim), and stale /tmp/ scratch-path references removed. No believe_me / postulate / idris_crash / assert_total introduced. Refs #138, #127, proven#90 #107 #119. https://claude.ai/code/session_01MN5vzRR4MK2dkDNaHqqRDy
1 parent c63abf8 commit ecda96e

2 files changed

Lines changed: 21 additions & 41 deletions

File tree

src/Proven/SafeJson/Access.idr

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,8 @@ field _ _ = Left NotAnObject
9393
-- Safe Path Access
9494
--------------------------------------------------------------------------------
9595

96-
||| Access JSON value at a path segment.
97-
||| Marked `total` (overriding the module-default `covering`): the top-level
98-
||| dispatch is non-recursive and the `index'` helper recurses structurally
99-
||| on `Nat`/`List`. Totality — not merely coverage — is what lets the
100-
||| equational lemmas in `Proven.SafeJson.Proofs` reduce `getSegment` on
101-
||| abstract arguments, since Idris2 0.8.0 does not unfold `covering`
102-
||| definitions during conversion checking.
103-
public export
104-
total
96+
||| Access JSON value at a path segment
97+
public export
10598
getSegment : PathSegment -> JsonValue -> Maybe JsonValue
10699
getSegment (Key k) (JsonObject pairs) = lookup k pairs
107100
getSegment (Index i) (JsonArray arr) = index' i arr
@@ -112,13 +105,8 @@ getSegment (Index i) (JsonArray arr) = index' i arr
112105
index' (S n) (_ :: xs) = index' n xs
113106
getSegment _ _ = Nothing
114107

115-
||| Access JSON value at a path.
116-
||| Marked `total` (overriding the module-default `covering`): structurally
117-
||| decreasing on the path list and dispatching through the now-`total`
118-
||| `getSegment`. Required so `singleKeyPath` (and future path lemmas) in
119-
||| `Proven.SafeJson.Proofs` can reduce `getPath` on abstract arguments.
108+
||| Access JSON value at a path
120109
public export
121-
total
122110
getPath : JsonPath -> JsonValue -> Maybe JsonValue
123111
getPath [] val = Just val
124112
getPath (seg :: rest) val = case getSegment seg val of

src/Proven/SafeJson/Proofs.idr

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ prependLengthInc v arr = Refl
202202
||| close the `S n` vs `n + 1` gap with `plusCommutative 1 n`.
203203
||| (`Data.List.Equalities.lengthSnoc` takes the element first, then the
204204
||| list — `lengthSnoc x xs`; the previous `lengthSnoc arr v` argument
205-
||| order did not type-check under Idris2 0.8.0 contrib.)
205+
||| order did not type-check under Idris2 0.8.0 contrib and blocked the
206+
||| whole module from compiling.)
206207
public export
207208
appendLengthInc : (v : JsonValue) -> (arr : List JsonValue) ->
208209
arrayLength (append v (JsonArray arr)) =
@@ -221,24 +222,15 @@ emptyPathIdentity v = Refl
221222

222223
||| DISCHARGED: a single-segment `Key k` path equals direct `lookup`:
223224
||| `getPath [Key k] (JsonObject obj) = lookup k obj`.
224-
|||
225-
||| The original OWED comment correctly identified the blocker: while
226-
||| `getPath`/`getSegment` were `covering` (via the `%default covering`
227-
||| in `Proven.SafeJson.Access`), Idris2 0.8.0 does not reduce `covering`
228-
||| definitions during proof conversion, so neither side of the equation
229-
||| meets — verified: even the one-step
230-
||| `getSegment (Key k) (JsonObject obj) = lookup k obj` fails by `Refl`
231-
||| while the module default holds. The fix lives in
232-
||| `Proven.SafeJson.Access`: both functions are now explicitly `total`
233-
||| (they always were structurally — `getSegment` is non-recursive,
234-
||| `getPath` decreases on the path list). With totality `getPath`
235-
||| reduces:
236-
||| getPath [Key k] (JsonObject obj)
237-
||| = case getSegment (Key k) (JsonObject obj) of
238-
||| { Nothing => Nothing; Just v => getPath [] v }
239-
||| = case lookup k obj of { Nothing => Nothing; Just v => Just v }
240-
||| and the `with`-pattern on `lookup k obj` closes both arms by `Refl`
241-
||| (Maybe-identity).
225+
||| On the concrete spine `[Key k]` / `JsonObject obj`, `getPath` and
226+
||| `getSegment` reduce by clause-matching to
227+
||| case lookup k obj of { Nothing => Nothing; Just v => Just v }
228+
||| Abstracting `lookup k obj` with a `with`-pattern then closes both
229+
||| arms by `Refl` (Maybe-identity).
230+
||| (`getPath`/`getSegment` are `covering` via the `%default covering`
231+
||| in `Proven.SafeJson.Access`; that annotation does not block this
232+
||| clause-level reduction, so they are left covering and the proof
233+
||| still goes through.)
242234
public export
243235
singleKeyPath : (k : String) -> (obj : List (String, JsonValue)) ->
244236
getPath [Key k] (JsonObject obj) = lookup k obj
@@ -338,13 +330,13 @@ stringMatchesTString s = Refl
338330
||| DISCHARGED: every `JsonValue` matches `TAny`:
339331
||| `matchesType v TAny = True` for all `v`.
340332
||| Definitionally `matchesType _ TAny = True` (`SafeJson.idr` L353).
341-
||| Unlike `singleKeyPath`, this needs no totality change: although
342-
||| `matchesType` is `covering`, supplying a concrete head constructor
343-
||| in each arm of a manual six-arm case-split
344-
||| (`JsonNull` / `JsonBool b` / `JsonNumber n` / `JsonString s` /
345-
||| `JsonArray arr` / `JsonObject pairs`) is enough — every earlier
346-
||| clause demands a non-`TAny` second argument, so each arm reduces
347-
||| through the catch-all `matchesType _ TAny = True` clause by `Refl`.
333+
||| Discharged by a six-arm case-split on `v` (`JsonNull` / `JsonBool b`
334+
||| / `JsonNumber n` / `JsonString s` / `JsonArray arr` /
335+
||| `JsonObject pairs`): `matchesType _ TAny = True` is a catch-all, and
336+
||| for each concrete head every earlier clause demands a non-`TAny`
337+
||| second argument, so each arm reduces to the catch-all and closes by
338+
||| `Refl`. No totality change is needed — a concrete head per arm is
339+
||| sufficient even though `matchesType` is `covering`.
348340
public export
349341
anyMatchesTAny : (v : JsonValue) -> matchesType v TAny = True
350342
anyMatchesTAny JsonNull = Refl

0 commit comments

Comments
 (0)