Skip to content

Add from-json interface and to-json for Map/Maybe#3

Merged
hellerve merged 1 commit into
mainfrom
claude/from-json-interface
May 23, 2026
Merged

Add from-json interface and to-json for Map/Maybe#3
hellerve merged 1 commit into
mainfrom
claude/from-json-interface

Conversation

@carpentry-agent

Copy link
Copy Markdown

What

Adds the from-json interface for converting JSON values back to Carp types, and completes the to-json story by adding implementations for Map and Maybe.

to-json additions

  • Map.to-json — converts (Map String a) to JSON.Obj, where a implements to-json
  • Maybe.to-json — converts (Maybe.Nothing) to JSON.Null and (Maybe.Just x) to (to-json x)

from-json interface and implementations

New interface: (definterface from-json (Fn [(Ref JSON)] (Result a String)))

Implementations for all core types:

  • Bool — extracts from JSON.Bool
  • Int — extracts from JSON.Num via Double.to-int (truncates toward zero)
  • Long — extracts from JSON.Num via Double.to-long
  • Float — extracts from JSON.Num via Double.to-float
  • Double — extracts from JSON.Num
  • String — extracts from JSON.Str
  • Array — extracts from JSON.Arr, converting each element; propagates first error
  • Map — extracts from JSON.Obj, converting each value; propagates first error
  • MaybeJSON.NullNothing, otherwise tries inner from-json and wraps in Just

All implementations return (Result.Error "expected <type>") on type mismatch.

Why

The library had to-json for 7 types but no from-json at all. Round-trip JSON serialization is fundamental for practical use — this completes the type conversion story.

Tests

38 new tests covering:

  • to-json for Map and Maybe
  • from-json for every type (success + type mismatch)
  • Edge cases (Int truncation, Array element errors, Maybe with null/value/mismatch)
  • Round-trip tests (to-jsonfrom-json recovers original value)

All 201 tests pass. carp-fmt and angler clean.


Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.

@carpentry-reviewer carpentry-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Build & Tests

CI passes on both macos-latest and ubuntu-latest. All 201 tests pass (163 existing + 38 new). No local Carp compiler available for independent build verification.

Findings

Reviewed the full implementation (json.carp:780-891) and all new tests (test/json.carp:752-935).

1. to-json additions (json.carp:781-793) — Correct.

  • Map.to-json: uses kv-reduce to build (Map String (Box JSON)), copies values with @v and boxes them. Keys passed by ref to Map.put. Matches Array.to-json pattern. ✓
  • Maybe.to-json: NothingJSON.Null, Just x(to-json x). Clean. ✓

2. from-json interface (json.carp:803)(Fn [(Ref JSON)] (Result a String)) is the right signature. Takes (Ref JSON) to avoid consuming input. Type variable a resolved per implementation. ✓

3. Primitive from-json implementations (json.carp:806-840) — All use match-ref correctly. Bool, Double, String are straightforward. ✓

4. Int/Long from-json (json.carp:813-828)Double.to-int and Double.to-long silently truncate. A JSON number like 1e20 will overflow Int without error; NaN/Infinity produce undefined results. The truncation test (line ~790 in tests) verifies 3.73, which is good. This is a reasonable design tradeoff for a first version, but worth documenting — callers should know conversion is lossy and unbounded.

5. let [_ 0] in Map.from-json (json.carp:876) — This appears to be a workaround for Carp's borrow checker: without the let binding, val may not have the right lifetime scope for &val in Map.put. The workaround works but would benefit from a short comment explaining why it's needed (e.g., ; extend val lifetime for &val borrow), since it looks like dead code to anyone unfamiliar with the quirk.

6. Array.from-json short-circuit (json.carp:847-862) — Correct. On error, Result.Error is propagated through remaining iterations. Box.peek only borrows, no leak. ✓

7. Maybe.from-json (json.carp:883-891) — Correct for single-level Maybe. Note: Maybe (Maybe Int) has an inherent JSON limitation — null maps to Nothing, so Just Nothing is unrepresentable. This is the standard tradeoff (Aeson, serde, etc.) and is fine, but worth a doc note eventually.

8. Missing test coverage (non-blocking):

  • No empty array from-json test (empty JSON.Arr [])
  • No empty map from-json test (empty JSON.Obj)
  • No roundtrip tests for Map or Maybe (roundtrips only cover Int, String, Bool, Array at lines 911-931)
  • No Map.from-json test with a bad element value (only tests top-level type mismatch)

9. No CHANGELOG — The repo has no CHANGELOG file, so this is not a gap introduced by this PR.

Verdict: merge

Solid implementation completing the JSON round-trip story. CI green on both platforms, 38 new tests with good coverage. The let [_ 0] workaround and missing edge-case tests are minor — suitable for a follow-up, not a blocker. The Int/Long overflow behavior is a reasonable first-cut design tradeoff.

@hellerve
hellerve merged commit 25d7b3b into main May 23, 2026
2 checks passed
@hellerve
hellerve deleted the claude/from-json-interface branch May 23, 2026 09:39
@carpentry-agent carpentry-agent Bot mentioned this pull request May 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant