6.7. Input shape
The defaults from Arguments cover most arguments: a scalar is read from argv, a compound value is inline JSON or a file. Three directives override that when an argument needs a particular shape.
-
@sourcesays where the bytes come from:inline(the argv token is the value) orfile(the argv token is a path and the file’s contents are the value). -
@formsays how the bytes are read:list,bytes,bytes-only, orpacket. -
@check.<kind>states an invariant the argument must satisfy before the command runs. The only kind today ispath.
For a list, the same three exist per element as @list.source, @list.form,
and @list.check.<kind>, describing what each line of the outer file means.
Which combinations are legal depends on the argument’s wire type. The tables below are the complete set; anything outside them is a compile error, reported against the docstring line that caused it.
| Modifier | Effect |
|---|---|
(none — the only valid case) |
argv is the literal value ( |
| Modifier | Effect |
|---|---|
(default) |
argv is the string itself, verbatim. |
|
argv must be a path satisfying the requested mode. |
|
argv is a path; the file’s contents become the string. One trailing newline
is stripped, so it behaves like |
|
Makes the positional optional and reads standard input when it is omitted.
Implies |
| Modifier | Effect |
|---|---|
(default) |
argv is a JSON array ( |
|
argv is a path; the file is checked for a Morloc packet header and otherwise read as packed raw bytes. |
|
argv is a path; the file is packed raw bytes, with no packet check. |
|
argv is a path; the file must be a Morloc packet. |
|
Only on |
| Modifier | Effect |
|---|---|
|
argv is a file (or |
|
Each line of the outer file is a path to a per-element file, each classified on its own (JSON / MessagePack / packet). |
|
Each line is a path, and each per-element file must be a Morloc packet. |
|
Each line is a path, and each file is read as packed raw bytes. The element type must be an array of fixed-width scalars. |
|
Each line of the outer file must be a path satisfying the requested mode. The
element type must be |
| Modifier | Effect |
|---|---|
(default, the only valid case) |
argv is JSON, or a path to a file holding JSON / MessagePack / a packet. No outer modifiers are allowed. |
6.7.1. A worked example
sift’s `scanAll uses two of these. The pattern list is a file with one
pattern per line, and the search root must be a directory that exists:
--' Search for any of several patterns, one per line of a file
scanAll ::
--' A file of patterns, one per line
--' @form list
[Str] ->
--' The directory to search
--' @check.path r
Str ->
Options ->
<IO> [Hit]
A shaped argument gets a format: line in the help saying what it will accept.
So does every Str argument, shaped or not: Str is the one type where argv
is genuinely ambiguous, and stating the default reading is cheaper than making
a reader infer it from the absence of a line.
$ ./sift scanAll -h
...
Positional arguments:
1: A file of patterns, one per line
type: [Str]
format: path to text file with one string per line
2: The directory to search
type: Str
format: path to a readable file
...
$ printf 'milk\nrest\n' > patterns.txt
$ ./sift scanAll patterns.txt notes -p
notes/todo.txt:1:buy milk
notes/2026/plan.txt:3:rest
@form list still accepts inline JSON, so the same command works without a
file:
$ ./sift scanAll '["milk","rest"]' notes -c
2
A failing @check is reported before the command runs, naming the check that
failed:
$ ./sift scan the nosuchdir
Error: argument #1: check.path: r requires path 'nosuchdir' to exist and be readable
6.7.2. Shape follows the wire form
Shape is classified against an argument’s wire form, not its source-level
type name. A type declared with Packable [(a, b)] T crosses the language
boundary as a list of pairs, so the CLI treats it as [(a, b)] and every list
modifier is available.
Map a b from the standard library is the case you are most likely to meet.
Its wire form is [(a, b)], so a Map Str Int argument reads a two-column TSV
or CSV exactly as [(Str, Int)] would:
module tally (tally)
import root-py
import map-py
--' Count the entries of a two-column table read as a Map
tally ::
--' A file with one `key<TAB>value` pair per line
--' @form list
Map Str Int -> U64
tally m = size m
$ printf 'apple\t3\nbanana\t7\ncherry\t1\n' > counts.tsv
$ ./tally counts.tsv
3
Commas work as well as tabs, and a JSON array per line is the fallback:
$ printf 'apple,3\nbanana,7\n' > counts.csv
$ ./tally counts.csv
2
$ printf '["apple",3]\n["banana",7]\n' > counts.jsonl
$ ./tally counts.jsonl
2
|
|
If you need column names, declare the argument as a |