module Spec:sig
..end
type'a
param ='a Command.Param.t
include Command.Param.S
val const : 'a -> 'a param
return
, preserved for backwards compatibilityval pair : 'a param ->
'b param -> ('a * 'b) param
both
, preserved for backwards compatibilitytype ('main_in, 'main_out)
t
Ultimately one forms a basic command by combining a spec of type
('main, unit -> 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 -> 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 -> unit
, and hence whose type looks like:
(arg1 -> ... -> argN -> unit -> unit, unit -> unit) Spec.t
A value of this type can fully apply a main function of type
arg1 -> ... -> argN -> unit -> unit
to all its arguments.
The final unit argument allows the implementation to distinguish between the phases of (1) parsing the command line and (2) running the body of the command. Exceptions raised in phase (1) lead to a help message being displayed alongside the exception. Exceptions raised in phase (2) are displayed without any command line help.
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
val of_params : ('a, 'b) Command.Param.Args.t -> ('a, 'b) t
module Arg_type:module type of Arg_type
with type 'a t = 'a Arg_type.t
include Command.Spec.Arg_type.Export
type'a
flag ='a Command.Flag.t
include Command.Flag
val map_flag : 'a flag -> f:('a -> 'b) -> 'b flag
map_flag flag ~f
transforms the parsed result of flag
by applying f
val flags_of_args_exn : Core_kernel.Std.Arg.t list -> ('a, 'a) t
flags_of_args_exn args
creates a spec from Caml.Arg.t
s, for compatibility with
ocaml's base libraries. Fails if it encounters an arg that cannot be converted.
NOTE: There is a difference in side effect ordering between Caml.Arg
and
Command
. In the Arg
module, flag handling functions embedded in Caml.Arg.t
values will be run in the order that flags are passed on the command line. In the
Command
module, using flags_of_args_exn flags
, they are evaluated in the order
that the Caml.Arg.t
values appear in flags
.
type'a
anons ='a Command.Anons.t
include Command.Anons
val map_anons : 'a anons -> f:('a -> 'b) -> 'b anons
map_anons anons ~f
transforms the parsed result of anons
by applying f