Melange
GuidesSchema.Melange.components prints the components of a walk as one OCaml
module for a browser, over Melange’s Js.Json, Js.Date and Js.Dict
alone. It has a type per component and the functions that read and write
it, each listing every problem at its place with the server’s codes and
sentences, so a form finds what is wrong before it sends. The module needs
no library, and a native user of wiretype installs nothing new.
Printing
Section titled “Printing”module J = Wiretype
type line = { sku : string; quantity : int; [@min 1] [@max 99] (** how many *) note : string option; [@max_length 200]}[@@deriving wiretype](** A line of an order. *)
let () = let ctx = J.Schema.create () in ignore (J.Schema.walk ctx Encode ~at:"line" line_json : J.Schema.t); match J.Schema.errors ctx with | e :: _ -> prerr_endline (J.Schema.error_to_string e); exit 1 | [] -> print_string (J.Schema.Melange.components ~header:"Written by examples/melange.ml; run it again to rewrite." (J.Schema.components ctx))~header is the module’s first comment: what wrote it, and how to write it
again. The module it prints holds, for the line:
type line = { sku : string; quantity : int; note : string option; }
val decode_line : string -> (line, Problem.t list) resultval line_of_json : Js.Json.t -> (line, Problem.t list) resultval encode_line : line -> (string, Problem.t list) resultval line_to_json : line -> (Js.Json.t, Problem.t list) resultSave it beside your Melange code, as a module of its own, and call
decode_line on what a form holds.
What the module holds
Section titled “What the module holds”It opens with a Problem – { at; code; message }, at the path
Problem.path prints, code the server’s codes and message its
sentences – and the kinds’ types: Instant.t over a Js.Date.t,
Duration.t over milliseconds, Uuid.t over the lower-case text.
Then each component given, named flat by Melange.name: for OrderLine
the type order_line, decode_order_line from text and
order_line_of_json from a Js.Json.t, encode_order_line to text and
order_line_to_json. Each lists every problem, reading and writing alike,
since the browser’s writer is where a form finds what is wrong before it
sends.
What each schema becomes
Section titled “What each schema becomes”| Schema | OCaml |
|---|---|
| any JSON | Js.Json.t |
| null | unit |
| a number | float |
| an integer | int where both its bounds lie within 32 bits, which Melange’s int holds; otherwise a whole float, refused past 2^53 as z.int() refuses it |
| a string | string, its length counted in code points |
| an enum | a variant, a constructor per word |
| a list | an array |
| a tuple | a tuple |
| a map | an array of pairs, in the document’s order |
| an object | a record; one with no kind is named by its place, order_line_note, and one with no members is unit |
| a member that may be left out | an option; absent and null are both None where it is nullable too |
| a value of several sorts | a variant by sort: Boolean of _, Number of _, String of _, Array of _, Object of _ |
| a union by tag | a variant, a constructor per case with the case’s members as an inline record |
| an instant, a duration, a UUID | the module’s own abstract types |
A name is made as OCaml wants one: a field in lower case with each run of
other characters as _ and a keyword given _ after it; a constructor
with its first letter raised, W before a digit; a twin gets _2. A
member’s ~field names its record’s field instead – kind for a member
"type", not type_ – and the deriver gives it wherever a field is
renamed. A schema that is no object or enum is an alias of what it prints
as.
Where the browser is looser
Section titled “Where the browser is looser”The browser is the looser in four places, by design:
- a whole number past 2^53;
- a member given twice, which
JSON.parsereads as the last of them; - nesting, which
JSON.parsedoes not limit; - a URI, held to the browser’s
URLand not to the server’s grammar.
Text that is not JSON is a syntax problem at the document, where the
server names the member it was reading. Everywhere else, the module reads
and writes as the server does: one file of rows is read through decode
and through the printed module on every test run, and held to the same
problems.
