std/cli
Pure command argument descriptions with typed results. Parser hides fixed metadata and a decoder; Names, DefinitionError and CliError expose their constructors. The pure parser has no reserved flag or implicit default.
pure stores a value; flag returns Bool; option/argument return a converted value; optional returns Option; many/rest return List. map and map2 preserve their result types. parse returns Result(A,CliError); help returns Result(String,DefinitionError). text and integer are pure Result converters. Conversion callbacks have type (String)->Result(A,String), without effects.
A1: validate all metadata first, in leaf order: ASCII long/positional names [a-z][a-z0-9-]*, one ASCII letter for short names, spelling before duplicates before positional order. Long, short and positional namespaces are separate. Rest is unique and last among positional leaves; later options are allowed. A2: tokens are unchanged strings with zero-based argument indexes. Recognize exact –long, –long=value and -s until bare –. A lone - is positional; all other leading-dash tokens are unknown. Options may follow positionals. A3: flags take no value; other options consume an attached suffix, including empty, or the next token unless absent or exactly –. Callback behavior does not change token ownership. All recognized-option errors use –long names. A4: singleton repetition wins over arity errors; many keeps occurrence order. A5: allocate required positionals in order, then rest. Optional absence is None; flags default False; many/rest default []; required leaves must occur. A6: errors precede decoding: definition, scanning, extra positionals, then missing/conversion errors in descriptor order. Conversion indexes identify the value token, or option token for attached values; scanning indexes always identify the option token. map/map2 run callbacks once after inner success. A7: integer accepts an optional sign and ASCII decimal digits, otherwise Error(“expected integer”); text preserves all token values, including NUL. A8: help validates metadata without callbacks. Usage plus program plus LF, then two spaces, display name, tab, verbatim help and LF for each leaf. Flags/optional/many are bracketed; value options add VALUE, many adds …, positionals use <name>, and rest uses [<name>…]. No automatic help action.
Laws: map(p,id) preserves parse and help observations. Regrouping map2 with tuple reassociation preserves leaf and callback order. Repeated parse is deterministic for deterministic, terminating callbacks. Closure equality is not observational equality. Linear field lookup costs O(tokens*fields + token codepoints), plus conversion; left-associated construction may cost O(fields^2). Decoder stack depth follows builder depth. Text scans are linear.
Parser
Parser(A) = ( Names
Names = (long = String, short = Option(String)) DefinitionError
DefinitionError = InvalidName | DuplicateName | MisplacedRest CliError
CliError = BadDefinition pure
pure = <A>(value : A) -> Parser(A) { flag
flag = (names : Names, help : String) -> Parser(Bool) { option
option = <A>( optional
optional = <A>( many
many = <A>( argument
argument = <A>( rest
rest = <A>( map
map = <A, B>(parser : Parser(A), with : (A) -> B) -> Parser(B) { map2
map2 = <A, B, C>( parse
parse = <A>(parser : Parser(A), args : List(String)) -> Result(A, CliError) { read
Parses the arguments supplied by the process service.
read = <A>(parser : Parser(A)) { parse(parser, process.args()) } run
Runs a command after parsing its arguments, reporting failures on stderr.
run = <A, N>(parser : Parser(A), program : String, body : (A) <- N -> Nil) { help
help = <A>(parser : Parser(A), program : String) -> Result( text
text = (text : String) -> Result(String, String) { Ok(text) } integer
integer = (text : String) -> Result(Int, String) {