Skip to content

docs: add doc comments and immutability design notes#1

Merged
tiye merged 1 commit into
mainfrom
docs/doc-comments-immutability
May 1, 2026
Merged

docs: add doc comments and immutability design notes#1
tiye merged 1 commit into
mainfrom
docs/doc-comments-immutability

Conversation

@tiye
Copy link
Copy Markdown
Member

@tiye tiye commented May 1, 2026

Changes

  • Doc comments added to all public items:
    • PathSegment — explains traversal semantics
    • PatchOp — explains each variant, keyed-array condition, and immutability contract
    • diff_json / diff_value — usage examples in doc strings
    • apply_patch / apply_patches / apply_to_value — immutability guarantees and Cumulo usage pattern
  • README rewritten:
    • New "Immutable data design" section explains why inputs are never mutated, how structural sharing works, and the pitfall of mut fields
    • Import path updated to tiye/recollect (standalone module)
    • Code blocks use nocheck where needed
  • Version bumped to 0.2.0

Copilot AI review requested due to automatic review settings May 1, 2026 04:01
@tiye tiye merged commit ab44011 into main May 1, 2026
3 checks passed
@tiye tiye deleted the docs/doc-comments-immutability branch May 1, 2026 04:02
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds/expands documentation for the MoonBit recollect structural JSON diff/patch library, clarifying immutability guarantees and usage, and bumps the module version for release.

Changes:

  • Add doc comments to public enums and key API functions in recollect.mbt (diff/apply APIs, immutability notes, examples).
  • Rewrite/expand README with an “Immutable data design” section, updated import path, and new examples.
  • Bump package version to 0.2.0.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
recollect.mbt Adds API doc comments and examples for patch format, diff functions, and apply functions.
moon.mod.json Updates the module version to 0.2.0.
README.md Updates library overview, immutability design notes, import path, and expands examples.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread README.md
`tiye/recollect` is a structural JSON diff/patch library for MoonBit.

It is intentionally kept as a leaf package inside this repository so it can be split into its own module later with minimal work.
It computes a minimal sequence of `PatchOp` operations between two `Json` trees
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

README claims the library computes a "minimal" sequence of PatchOps. The implementation falls back to positional array diffing for non-keyed arrays, which can produce non-minimal sequences for insertions/deletions (it may emit multiple Sets instead of a single Insert/Delete). Consider adjusting the wording to avoid promising minimality unless it’s guaranteed.

Suggested change
It computes a minimal sequence of `PatchOp` operations between two `Json` trees
It computes a sequence of `PatchOp` operations between two `Json` trees

Copilot uses AI. Check for mistakes.
Comment thread README.md
Comment on lines +101 to 111
```}

let after : Json = {
"todos": [
{ "id": "t-1", "title": "A", "done": true },
{ "id": "t-2", "title": "B", "done": false },
],
}

let patches = @recollect.diff_json(before, after)
let rebuilt = @recollect.apply_patches(before, patches)
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

The Typed value example code block is closed with }, which breaks Markdown rendering, and the following let after : Json = { ... } snippet appears to be stray/duplicated content outside any fenced block. Fix the fence terminator to ``` and remove the unrelated duplicated JSON snippet so the README renders and copy/paste examples work.

Suggested change
```}
let after : Json = {
"todos": [
{ "id": "t-1", "title": "A", "done": true },
{ "id": "t-2", "title": "B", "done": false },
],
}
let patches = @recollect.diff_json(before, after)
let rebuilt = @recollect.apply_patches(before, patches)

Copilot uses AI. Check for mistakes.
Comment thread README.md
// rebuilt == after
```

## Typed value example
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

There are now two "## Typed value example" sections in the README (one starts here, and another exists later in the file). Consider removing one to avoid duplicated/conflicting documentation.

Suggested change
## Typed value example
## Typed value example (structs)

Copilot uses AI. Check for mistakes.
Comment thread recollect.mbt
///
/// | Variant | Meaning |
/// |---------|---------|
/// | `Set` | Overwrite a value at `path` (creates intermediate nodes if absent) |
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

The doc table says Set "creates intermediate nodes if absent", but apply_set aborts when it needs to traverse through a missing intermediate node (it substitutes null for a missing child, which then fails the next Object/Array guard). Either adjust the docs to describe the actual behavior (only creates the leaf when the parent exists) or update apply_set to materialize intermediate objects/arrays based on the remaining path segments.

Suggested change
/// | `Set` | Overwrite a value at `path` (creates intermediate nodes if absent) |
/// | `Set` | Overwrite a value at `path` (creates the leaf only when the parent path exists) |

Copilot uses AI. Check for mistakes.
Comment thread recollect.mbt
Comment on lines +288 to +293
/// Compute a minimal sequence of `PatchOp`s that transforms `old_json` into
/// `new_json`.
///
/// The diff is **structural**: objects are compared field-by-field, arrays are
/// compared element-by-element (or by `"id"` key when all elements carry
/// unique string ids). Unchanged subtrees produce no patches.
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

diff_json is documented as producing a "minimal" patch sequence, but the current positional array diff emits per-index Set operations for shifts (e.g. inserting at the front of a plain array) and is not minimal in terms of number of ops. Consider relaxing the wording (e.g. "structural" / "deterministic") or defining what "minimal" means here and ensuring the implementation meets it.

Suggested change
/// Compute a minimal sequence of `PatchOp`s that transforms `old_json` into
/// `new_json`.
///
/// The diff is **structural**: objects are compared field-by-field, arrays are
/// compared element-by-element (or by `"id"` key when all elements carry
/// unique string ids). Unchanged subtrees produce no patches.
/// Compute a deterministic structural sequence of `PatchOp`s that transforms
/// `old_json` into `new_json`.
///
/// The diff is **structural**: objects are compared field-by-field, arrays are
/// compared element-by-element (or by `"id"` key when all elements carry
/// unique string ids). Unchanged subtrees produce no patches. For plain
/// arrays, the patch sequence is positional and not guaranteed to be minimal
/// in number of operations.

Copilot uses AI. Check for mistakes.
Comment thread recollect.mbt
Comment on lines +527 to +528
/// let new_json = apply_patches(old_json, diff_json(old_json, new_json))
/// assert_eq(new_json, new_json)
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

The apply_patches doc example is self-referential and will always pass (assert_eq(new_json, new_json)), and it also reuses new_json as both the expected value and the result name. Update the example to compare against an expected/target value so it actually demonstrates correctness.

Suggested change
/// let new_json = apply_patches(old_json, diff_json(old_json, new_json))
/// assert_eq(new_json, new_json)
/// let patched_json = apply_patches(old_json, diff_json(old_json, target_json))
/// assert_eq(patched_json, target_json)

Copilot uses AI. Check for mistakes.
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.

2 participants