My agentic slop goes here. Not intended for anyone else!

OCaml JMAP Development Patterns#

Quick Reference for Production Implementation#

These patterns were proven during the December 2024 systematic compilation fixes and should be used consistently for all future development.

1. JSON Parsing Pattern (JSONABLE Interface)#

Always use this pattern for of_json implementations:

let of_json json =
  try
    match json with
    | `Assoc fields ->
      let field1 = match List.assoc_opt "field1" fields with
        | Some (`String s) -> s
        | _ -> failwith "Missing required 'field1' field"
      in
      let optional_field = match List.assoc_opt "optionalField" fields with
        | Some (`Bool b) -> Some b
        | Some `Null | None -> None
        | _ -> failwith "Invalid 'optionalField' field"
      in
      Ok { field1; optional_field }
    | _ -> Error "Expected JSON object"
  with
  | Failure msg -> Error ("JSON parsing error: " ^ msg)
  | exn -> Error ("JSON parsing exception: " ^ Printexc.to_string exn)

Key Points:

  • Always return (t, string) result for JSONABLE compliance
  • Use try/with to catch failwith and other exceptions
  • Provide descriptive error messages with context
  • Handle optional fields with explicit None cases

2. Type Inference Disambiguation Pattern#

Use explicit type annotations for complex constructions:

(* For record field assignments that might be ambiguous *)
let filter : Filter.t option = match json_opt with
  | None -> None
  | Some json -> Some (Jmap.Methods.Filter.condition json)

let position : uint option = match pos_json with
  | None -> None  
  | Some (`Int i) when i >= 0 -> Some i

let sort : Comparator.t list option = match sort_json with
  | None -> None
  | Some (`List items) -> Some (List.map parse_comparator items)

When to use:

  • Complex record constructions with multiple option types
  • When OCaml reports type inference conflicts
  • Functions with multiple similar-looking fields

3. Module Qualification Pattern#

Use full module paths for potentially ambiguous types:

(* Instead of *)
Filter.condition json

(* Use *)  
Jmap.Methods.Filter.condition json

(* Instead of *)
Comparator.of_json json

(* Use *)
Jmap.Methods.Comparator.of_json json

When to use:

  • When you get "unbound value" errors
  • Multiple modules define similar functions
  • Type conflicts between local and imported modules

4. Stub Function Parameter Pattern#

Acknowledge unused parameters appropriately:

(* For completely unused parameters *)
let stub_function _unused_arg _unused_ctx ~used_param ~unused_param:_ () =
  (* Use used_param normally *)
  Error "Not implemented yet"

(* For parameters used in commented code or TODOs *)  
let future_function env ctx ~account_id ~email_ids:_ () =
  (* TODO: Implement with email_ids when ready *)
  process_account env ctx account_id

(* For debugging/development *)
let debug_function env _ctx ~account_id () =
  Printf.printf "Processing account: %s\n" account_id;
  process_with_env env account_id

Pattern Rules:

  • _param: Completely unused, no intention to use
  • ~param:_: Named parameter that's explicitly unused
  • param: Actually used in function body
  • Preserve parameters that will be used in future implementations

5. Result Error Handling Pattern#

Consistent error propagation and handling:

(* Chain Results properly *)
let complex_parsing json =
  match Base.of_json json with
  | Error e -> Error ("Base parsing failed: " ^ e)
  | Ok base ->
    match Enhanced.of_json base.extra_data with  
    | Error e -> Error ("Enhanced parsing failed: " ^ e)
    | Ok enhanced -> Ok { base; enhanced }

(* Use Result.bind for cleaner chaining *)
let complex_parsing_v2 json =
  Base.of_json json
  |> Result.bind (fun base ->
    Enhanced.of_json base.extra_data
    |> Result.map (fun enhanced -> { base; enhanced }))
  |> Result.map_error (fun e -> "Complex parsing failed: " ^ e)

6. Interface Implementation Checklist#

Before implementing any JSONABLE interface:

  • Does of_json return (t, string) result?
  • Does to_json return Yojson.Safe.t?
  • Are all required fields validated?
  • Are optional fields handled with explicit None cases?
  • Is error handling comprehensive (try/with)?
  • Are error messages descriptive and contextual?

7. Build Verification Commands#

Always run these after making changes:

# Full compilation check
opam exec -- dune build @check

# Clean rebuild (if you suspect caching issues)
opam exec -- dune clean && opam exec -- dune build @check

# Documentation generation (checks for doc warnings)
opam exec -- dune build @doc

# Run with release profile (ignores warnings, for final verification)
opam exec -- dune build @check --profile=release

8. Common Issues and Solutions#

Issue: "Values do not match" interface error#

Cause: Function signature doesn't match interface declaration Solution: Check return type, add Result wrapper, verify parameter types

Issue: "This variant expression is expected to have type X"#

Cause: Type inference conflict in pattern matching Solution: Add explicit type annotation to the bound variable

Issue: "Unbound value" errors#

Cause: Module not properly opened or qualified
Solution: Use full module path or add appropriate open statement

Issue: "Unused variable" warnings in stub functions#

Cause: Parameters not acknowledged as intentionally unused Solution: Apply parameter acknowledgment patterns consistently

9. Development Workflow#

Recommended sequence for implementing new functionality:

  1. Design Types: Define the core types following RFC specifications
  2. Add Interfaces: Create .mli files with proper JSONABLE signatures
  3. Stub Implementation: Create minimal .ml files with proper parameter acknowledgment
  4. Verify Compilation: Ensure dune build @check passes
  5. Implement Incrementally: Replace stubs with real implementations one at a time
  6. Test Each Step: Verify compilation after each function implementation
  7. Add Documentation: Include RFC references and usage examples

This systematic approach ensures that compilation remains clean throughout development and reduces debugging time significantly.


Note: These patterns were validated during the December 2024 systematic fixes where 59+ functions across 10 files were successfully corrected. They represent battle-tested approaches for OCaml JMAP development.