module Spec:sig
..end
type 'a
param
val const : 'a -> 'a param
val map : 'a param -> f:('a -> 'b) -> 'b param
val help : string Lazy.t param
val path : string list param
val args : string list param
type ('main_in, 'main_out)
t
Ultimately one forms a base command by combining a spec of type ('main, unit) t
with a main function of type 'main
; see the basic
function below. Combinators
in this library incrementally build up the type of main according to what
command-line parameters it expects, so the resulting type of main
is something
like:
arg1 -> ... -> argN -> unit
It may help to think of ('a, 'b) t
as a function space 'a -> 'b
embellished with
information about:
One can view a value of type ('main_in, 'main_out) t
as function that transforms a
main function from type 'main_in
to 'main_out
, typically by supplying some
arguments. E.g. a value of type Spec.t
might have type:
| (arg1 -> ... -> argN -> 'r, 'r) Spec.t
Such a value can transform a main function of type arg1 -> ... -> argN -> 'r
by
supplying it argument values of type arg1
, ..., argn
, leaving a main function
whose type is 'r
. In the end, Command.basic
takes a completed spec where 'r =
unit
, and hence whose type looks like:
| (arg1 -> ... -> argN -> unit, unit) Spec.t
A value of this type can fully apply a main function of type arg1 -> ... -> argN
-> unit
to all its arguments.
The view of ('main_in, main_out) Spec.t
as a function from 'main_in
to
'main_out
is directly reflected by the step
function, whose type is:
| val step : ('m1 -> 'm2) -> ('m1, 'm2) t
spec1 ++ spec2 ++ ... ++ specN
composes spec1 through specN.
For example, if spec_a
and spec_b
have types:
| spec_a: (a1 -> ... -> aN -> 'ra, 'ra) Spec.t | spec_b: (b1 -> ... -> bM -> 'rb, 'rb) Spec.t
then spec_a ++ spec_b
has the following type:
| (a1 -> ... -> aN -> b1 -> ... -> bM -> 'rb, 'rb) Spec.t
So, spec_a ++ spec_b
transforms a main function it by first supplying spec_a
's
arguments of type a1
, ..., aN
, and then supplying spec_b
's arguments of type
b1
, ..., bm
.
One can understand ++
as function composition by thinking of the type of specs
as concrete function types, representing the transformation of a main function:
| spec_a: \/ra. (a1 -> ... -> aN -> 'ra) -> 'ra | spec_b: \/rb. (b1 -> ... -> bM -> 'rb) -> 'rb
Under this interpretation, the composition of spec_a
and spec_b
has type:
| spec_a ++ spec_b
: \/rc. (a1 -> ... -> aN -> b1 -> ... -> bM -> 'rc) -> 'rc
And the implementation is just function composition:
| sa ++ sb = fun main -> sb (sa main)
val empty : ('m, 'm) t
val (++) : ('m1, 'm2) t ->
('m2, 'm3) t -> ('m1, 'm3) t
val (+>) : ('m1, 'a -> 'm2) t ->
'a param -> ('m1, 'm2) t
val (+<) : ('m1, 'm2) t ->
'a param -> ('a -> 'm1, 'm2) t
this function should only be used as a workaround in situations where the
order of composition is at odds with the order of anonymous arguments due
to factoring out some common spec
val step : ('m1 -> 'm2) -> ('m1, 'm2) t
Here are a couple examples of some of its many uses
step (fun m v -> m ~foo:v) +> flag "-foo" no_arg : (foo:bool -> 'm, 'm) t
step (fun m user -> match user with | Some user -> m user | None -> print_string "enter username: "; m (read_line ())) +> flag "-user" (optional string) ~doc:"USER to frobnicate" : (string -> 'm, 'm) t
A use of step
might look something like:
| step (fun main -> let ... in main x1 ... xN) : (arg1 -> ... -> argN -> 'r, 'r) t
Thus, step
allows one to write arbitrary code to decide how to transform a main
function. As a simple example:
| step (fun main -> main 13.) : (float -> 'r, 'r) t
This spec is identical to const 13.
; it transforms a main function by supplying
it with a single float argument, 13.
. As another example:
| step (fun m v -> m ~foo:v) : (foo:'foo -> 'r, 'foo -> 'r) t
This spec transforms a main function that requires a labeled argument into
a main function that requires the argument unlabeled, making it easily composable
with other spec combinators.
val wrap : (run:('m1 -> 'r1) -> main:'m2 -> 'r2) ->
('m1, 'r1) t -> ('m2, 'r2) t
Here are two examples of command classes defined using wrap
wrap (fun ~run ~main -> Exn.handle_uncaught ~exit:true (fun () -> run main) ) : ('m, unit) t -> ('m, unit) t
wrap (fun ~run ~main -> In_channel.iter_lines stdin ~f:(fun line -> run (main line)) ) : ('m, unit) t -> (string -> 'm, unit) t
module Arg_type:sig
..end
val string : string Arg_type.t
val int : int Arg_type.t
val float : float Arg_type.t
val bool : bool Arg_type.t
val date : Date.t Arg_type.t
val time_span : Span.t Arg_type.t
val file : string Arg_type.t
type 'a
flag
val flag : ?aliases:string list ->
string -> 'a flag -> doc:string -> 'a param
flag name spec ~doc
specifies a command that, among other things, takes a flag
named name
on its command line. doc
indicates the meaning of the flag.
NOTE: the doc
for a flag which takes an argument should be of the form arg_name ^
" " ^ description
where arg_name
describes the argument and description
describes the meaning of the flag.
NOTE: flag names (including aliases) containing underscores will
be rejected. Use dashes instead.
val required : 'a Arg_type.t -> 'a flag
val optional : 'a Arg_type.t -> 'a option flag
val optional_with_default : 'a -> 'a Arg_type.t -> 'a flag
optional_with_default
flags may be passed at most once, and
default to a given valueval listed : 'a Arg_type.t -> 'a list flag
listed
flags may be passed zero or more timesval no_arg : bool flag
no_arg
flags may be passed at most once. The boolean returned
is true iff the flag is passed on the command lineval no_arg_register : key:'a Univ_map.With_default.Key.t -> value:'a -> bool flag
no_arg_register ~key ~value
is like no_arg
, but associates value
with key
in the in the auto-completion environmentval no_arg_abort : exit:(unit -> Std_internal.never_returns) -> unit flag
no_arg_abort ~exit
is like no_arg
, but aborts command-line parsing
by calling exit
. This flag type is useful for "help"-style flags that
just print something and exit.val escape : string list option flag
escape
flags may be passed at most once. They cause the command
line parser to abort and pass through all remaining command line
arguments as the value of the flag.type 'a
anons
val anon : 'a anons -> 'a param
anon spec
specifies a command that, among other things, takes
the anonymous arguments specified by spec
.val (%:) : string -> 'a Arg_type.t -> 'a anons
(name %: typ)
specifies a required anonymous argument of type typ
.
The name
is mentioned in the generated help for the command.val sequence : 'a anons -> 'a list anons
sequence anons
specifies a sequence of anonymous arguments. An exception
will be raised if anons
matches anything other than a fixed number of
anonymous argumentsval maybe : 'a anons -> 'a option anons
(maybe anons)
indicates that some anonymous arguments are optionalval maybe_with_default : 'a -> 'a anons -> 'a anons
(maybe_with_default default anons)
indicates an optional anonymous
argument with a default valuet2
, t3
, and t4
each concatenate multiple anonymous argument
specs into a single one. The purpose of these combinators is to allow
for optional sequences of anonymous arguments. Consider a command with
usage
main.exe FOO BAR BAZ
where the second and third anonymous arguments must either both be there or both not be there. This can be expressed as
t2 ("FOO" %: foo) (maybe (t2 ("BAR" %: bar) ("BAZ" %: baz)))
Sequences of 5 or more anonymous arguments can be built up using nested tuples
maybe (t3 a b (t3 c d e))
val t2 : 'a anons ->
'b anons -> ('a * 'b) anons
val t3 : 'a anons ->
'b anons ->
'c anons -> ('a * 'b * 'c) anons
val t4 : 'a anons ->
'b anons ->
'c anons ->
'd anons -> ('a * 'b * 'c * 'd) anons