Skip to content

Fix applyeach dispatch, split unions, and default absent fields - #73

Merged
quinnj merged 3 commits into
mainfrom
jq/geojson-followups
Sep 18, 2026
Merged

quinnj merged 3 commits into
mainfrom
jq/geojson-followups

Conversation

@quinnj

@quinnj quinnj commented Sep 17, 2026 •

Copy link
Copy Markdown
Member

Follow-ups from JuliaGeo/GeoJSON.jl#128. Missing fields use the null their types admit, union selection remains statically resolvable under safe trimming, and callback-first applyeach accepts callable structs without conflicting with style-first overloads.

Every built-in key and index continues to pass through lowerkey, including arrays, tuples, sets, generators, simple vectors, union elements, and undefined slots. JuliaIO/JSON.jl#486 removes discarded numeric-index string allocations within JSON while preserving the hook calls.

Only union element types use the generated union helper. Wide struct unions share a non-inlined helper to avoid copying the dispatch ladder into every field.

Validation at 4f4e220e91b474bcd3d2cdc6bb74f9cd13aa40c1:

  • All 8 CI checks pass, including minimum/current/prerelease Julia and documentation.
  • All 605 local checks pass on Julia 1.10.11, 1.12.6, and 1.13.0.
  • The Julia 1.13 safe-trim workload compiles and executes successfully.
  • Paired JSON testing retains the large-array speedup after restoring hooks: one million Float64 values take 36.9 ms and 31 allocations on Julia 1.12, versus 36.8 ms and 31 allocations before the restoration. Package-base performance was about 102 ms and 2,000,031 allocations.
  • Reproducible traversal benchmarks are in benchmarks/applyeach.jl; the companion JSON PR contains runtime, first-call, load, and precompile benchmarks.

The earlier review found remaining tradeoffs versus the package bases: smaller wide-union structs can take longer on first use, and focused Julia 1.13 warm wide-struct writes were about 4–6% slower with unchanged allocations. The hook restoration does not claim to remove those tradeoffs.

Co-authored by Codex

…t absent fields

- `applyeach(f, style, x)` (the do-block order) now requires `f::Function`, so it no
  longer collides with style-first overloads a package defines for its own types
  (`applyeach(::MyStyle, f, ::MyType)`); those were ambiguous before.
- Array, set and tuple indices pass through `applyeach` unlowered; only object keys go
  through `lowerkey`. With JSON's `lowerkey(::Real) = string(x)` every element index of
  an array was a String allocation (2M allocations for a 1e6-element vector), and
  multi-dimensional `make` under such a style received String indices.
- Union-typed struct fields and array elements reach `f` one member at a time through
  an `isa` ladder generated from the field/element type, so unions wider than four
  members, or ones met inside a recursive write, are static calls.
- Absent fields: a `Union{Missing,T}` field with no default reads as `missing`; a
  required field that is absent throws an `ArgumentError` naming the field instead of a
  bare `TypeError`. The message holds no type, so it is safe under `juliac --trim`.
- Union selection in `make` is unrolled over the members, so `arraylike(::DataType)` is
  no longer a dynamic call under `--trim` for unions of three or more members.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@quinnj quinnj changed the title Fix applyeach index keys and do-block ambiguity, split unions, default absent fields Fix applyeach dispatch, split unions, and default absent fields Sep 18, 2026
@quinnj
quinnj merged commit 1aa0b27 into main Sep 18, 2026
8 checks passed
@quinnj
quinnj deleted the jq/geojson-followups branch September 18, 2026 20:41
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