voxgig-struct: Haskell port of voxgig/struct: JSON-like data structure utilities.

[ data, library, mit ] [ Propose Tags ] [ Report a vulnerability ]

A faithful Haskell port of the canonical voxgig/struct library: utilities for walking, merging, transforming, injecting into, and validating JSON-like data structures (getpath, merge, inject, transform, validate, walk). No third-party dependencies — only the GHC boot libraries.


[Skip to Readme]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0
Change log CHANGELOG.md
Dependencies array (>=0.5 && <0.6), base (>=4.14 && <5) [details]
License MIT
Copyright (c) 2025-2026 Voxgig Ltd.
Author Voxgig
Maintainer richard.rodger@voxgig.com
Uploaded by rjrodger at 2026-07-06T12:42:14Z
Category Data
Home page https://github.com/voxgig/struct
Bug tracker https://github.com/voxgig/struct/issues
Source repo head: git clone https://github.com/voxgig/struct.git
Distributions
Downloads 0 total (0 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2026-07-06 [all 1 reports]

Readme for voxgig-struct-0.1.0

[back to package description]

voxgig_struct — Haskell

A Haskell port of voxgig/struct: one small, fixed API for manipulating JSON-shaped data — lookups, deep merge, by-example transform, by-example validate, tree walk, path get/set, selection — that returns the same answer as the canonical TypeScript implementation and every other port. The behavioural contract is the shared JSON corpus in build/test/; this port passes it in full.

Status

Complete. Every canonical public function is implemented and the entire shared corpus passes (make test). Zero third-party dependencies — only GHC and its boot libraries (base, array) are required.

Requirements

  • GHC 9.x (tested with 9.4). No Cabal/Stack packages needed.

Use

import VoxgigStruct

main :: IO ()
main = do
  store <- jm ["a", undefined]      -- build nodes with jm / jt (see below)
  v <- getpath INone store (VStr "a.b")
  print =<< stringify v

jm / jt are the JSON-object / JSON-array builders (jm takes a flat [k1, v1, k2, v2, ...] list of Values; jt takes a list of items):

do inner <- jt [VNum 2, VNum 3]
   m <- jm [VStr "a", VNum 1, VStr "b", inner]
   putStrLn =<< jsonify m VNoval

Data model

The canonical algorithm mutates nodes in place and relies on reference-stable nodes (a node updated through one reference is seen through every other). Haskell has no mutable native collection, so a node carries an IORef to its contents — VList (IORef [Value]) for arrays, VMap (IORef [(String, Value)]) for objects (insertion-ordered) — the analog of OCaml's ref or Rust's Rc<RefCell>. Because the heap is mutable, the whole API runs in IO.

Like the OCaml / Scala ports, undefined (VNoval) and JSON null (VNull) are distinct constructors, so the port mirrors the canonical TS logic directly without the Group-A/B null-collapsing the single-null ports need.

API

The public surface matches the canonical export list, in lower-smushed / snake_cased names:

clone delprop escre escurl filter flatten getdef getelem getpath getprop haskey inject isempty isfunc iskey islist ismap isnode items join jsonify keysof merge pad pathify select setpath setprop size slice strkey stringify transform typify typename validate walk re_compile re_find re_find_all re_replace re_test re_escape jm jt check_placement injector_args inject_child

See DOCS.md for the full guide and the language-neutral docs for concepts and examples.

Develop

make test     # compile + run the shared corpus
make lint     # ghc -fno-code (a clean type-check = pass)

License

MIT. See ../LICENSE.