Module Command.Spec

The old interface for command-line specifications -- Do Not Use.

This interface should not be used. See the Param module for the new way to do things.

Command parameters

type 'a param = 'a Param.t

Specification of an individual parameter to the command's main function.

  • deprecated [since 2019-03] use [Command.Param.t] instead
include Param.S with type 'a t := 'a Param.t

Command.Param is intended to be used with the [%map_open] syntax defined in ppx_let, like so:

let command =
  Command.basic ~summary:"..."
    [%map_open
      let count  = anon ("COUNT" %: int)
      and port   = flag "port" (optional int) ~doc:"N listen on this port"
      and person = person_param
      in
      (* ... Command-line validation code, if any, goes here ... *)
      fun () ->
        (* The body of the command *)
        do_stuff count port person
    ]

One can also use [%map_open] to define composite command line parameters, like person_param in the previous snippet:

type person = { name : string; age : int }

let person_param : person Command.Param.t =
  [%map_open
    let name = flag "name" (required string) ~doc:"X name of the person"
    and age  = flag "age"  (required int)    ~doc:"N how many years old"
    in
    {name; age}
  ]

The right-hand sides of [%map_open] definitions have Command.Param in scope.

Alternatively, you can say:

let open Foo.Let_syntax in
[%map_open
  let x ...
]

if Foo follows the same conventions as Command.Param.

See example/command/main.ml for more examples.

include Base.Applicative.S with type 'a t := 'a Param.t
val return : 'a -> 'a Param.t
val map : 'a Param.t -> f:('a -> 'b) -> 'b Param.t
val both : 'a Param.t -> 'b Param.t -> ('a * 'b) Param.t
val (<*>) : ('a -> 'b) Param.t -> 'a Param.t -> 'b Param.t

same as apply

val (<*) : 'a Param.t -> unit Param.t -> 'a Param.t
val (*>) : unit Param.t -> 'a Param.t -> 'a Param.t
val (>>|) : 'a Param.t -> ('a -> 'b) -> 'b Param.t
val apply : ('a -> 'b) Param.t -> 'a Param.t -> 'b Param.t
val map2 : 'a Param.t -> 'b Param.t -> f:('a -> 'b -> 'c) -> 'c Param.t
val map3 : 'a Param.t -> 'b Param.t -> 'c Param.t -> f:('a -> 'b -> 'c -> 'd) -> 'd Param.t
val all : 'a Param.t list -> 'a list Param.t
val all_unit : unit Param.t list -> unit Param.t
module Applicative_infix : sig ... end

Various internal values

The help text for the command.

The subcommand path of the command.

The arguments passed to the command.

val flag : ?aliases:Base.string Base.list -> ?full_flag_required:Base.unit -> Base.string -> 'a Flag.t -> doc:Base.string -> 'a Param.t

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.

All flags must have a dash at the beginning of the name. If name is not prefixed by "-", it will be normalized to "-" ^ name.

Unless full_flag_required is used, one doesn't have to pass name exactly on the command line, but only an unambiguous prefix of name (i.e., a prefix which is not a prefix of any other flag's name).

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.

NOTE: "-" by itself is an invalid flag name and will be rejected.

val flag_optional_with_default_doc : ?aliases:Base.string Base.list -> ?full_flag_required:Base.unit -> Base.string -> 'a Arg_type.t -> ('a -> Base.Sexp.t) -> default:'a -> doc:Base.string -> 'a Param.t

flag_optional_with_default_doc name arg_type sexp_of_default ~default ~doc is a shortcut for flag, where:

  1. The Flag.t is optional_with_default default arg_type
  2. The doc is passed through with an explanation of what the default value appended.
val anon : 'a Anons.t -> 'a Param.t

anon spec specifies a command that, among other things, takes the anonymous arguments specified by spec.

val escape_anon : final_anon:'a Anons.t -> ('a * Base.string Base.list) Param.t

escape_anon ~final_anon parses anon and then stops parsing. Remaining command line arguments are collected in the string list, even if they start with dashes.

See escape for the flag version of this behavior.

The final anon is required to indicate when to stop parsing.

module If_nothing_chosen : sig ... end
val choose_one : 'a Base.option Param.t Base.list -> if_nothing_chosen:('a, 'b) If_nothing_chosen.t -> 'b Param.t

choose_one clauses ~if_nothing_chosen expresses a sum type. It raises if more than one of clauses is Some _. When if_nothing_chosen = Raise, it also raises if none of clauses is Some _.

val choose_one_non_optional : 'a Param.t Base.list -> if_nothing_chosen:('a, 'b) If_nothing_chosen.t -> 'b Param.t

choose_one_non_optional clauses ~if_nothing_chosen expresses a sum type. It raises if more than one of the clauses has any flags given on the command-line, and returns the value parsed from the clause that's given.

When if_nothing_chosen = Raise, it also raises if none of the clauses are given.

val and_arg_names : 'a Param.t -> ('a * Base.string Base.list) Param.t

and_arg_names t returns both the value of t and the names of the arguments that went into t. Useful for errors that reference multiple params.

val and_arg_name : 'a Param.t -> ('a * Base.string) Param.t

Like and_arg_names, but asserts that there is exactly one name.

val arg_names : 'a Param.t -> Base.string Base.list
val const : 'a -> 'a Param.t

Superceded by return, preserved for backwards compatibility.

  • deprecated [since 2018-10] use [Command.Param.return] instead of [Command.Spec.const]
val pair : 'a Param.t -> 'b Param.t -> ('a * 'b) Param.t

Superceded by both, preserved for backwards compatibility.

  • deprecated [since 2018-10] use [Command.Param.both] instead of [Command.Spec.pair]

Command specifications

type (-'main_in, +'main_out) t

Composable command-line specifications.

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:

  • how to parse the command line
  • what the command does and how to call it
  • how to autocomplete a partial command line

One can view a value of type ('main_in, 'main_out) t as a 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 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

The empty command-line spec.

val (++) : ('m1, 'm2) t -> ('m2, 'm3) t -> ('m1, 'm3) t

Command-line spec composition.

val (+>) : ('m1, 'a -> 'm2) t -> 'a Param.t -> ('m1, 'm2) t

Adds a rightmost parameter onto the type of main.

val (+<) : ('m1, 'm2) t -> 'a Param.t -> ('a -> 'm1, 'm2) t

Adds a leftmost parameter onto the type of main.

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 because you're factoring out some common spec.

val step : ('m1 -> 'm2) -> ('m1, 'm2) t

Combinator for patching up how parameters are obtained or presented.

Here are a couple examples of some of its many uses:

  • introducing labeled arguments

    step (fun m v -> m ~foo:v)
                   +> flag "-foo" no_arg : (foo:bool -> 'm, 'm) t
  • prompting for missing values

    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

Combinator for defining a class of commands with common behavior.

Here are two examples of command classes defined using wrap:

  • print top-level exceptions to stderr

    wrap (fun ~run ~main ->
                     Exn.handle_uncaught ~exit:true (fun () -> run main)
                   ) : ('m, unit) t -> ('m, unit) t
  • iterate over lines from stdin

    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 : module type of Arg_type with type 'a t = 'a Arg_type.t
include module type of Arg_type.Export

Values to include in other namespaces.

val string : Base.string Arg_type.t

Beware that an anonymous argument of type int cannot be specified as negative, as it is ambiguous whether -1 is a negative number or a flag. (The same applies to float, time_span, etc.) You can use the special built-in "-anon" flag to force a string starting with a hyphen to be interpreted as an anonymous argument rather than as a flag, or you can just make it a parameter to a flag to avoid the issue.

val sexp_conv : ?complete:Auto_complete.t -> (Base.Sexp.t -> 'a) -> 'a Arg_type.t
type 'a flag = 'a Flag.t

A flag specification.

include module type of Flag with type 'a t := 'a flag

Command-line flag specifications.

val required : 'a Arg_type.t -> 'a flag

Required flags must be passed exactly once.

val optional : 'a Arg_type.t -> 'a Base.option flag

Optional flags may be passed at most once.

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 value.

val listed : 'a Arg_type.t -> 'a Base.list flag

listed flags may be passed zero or more times.

val one_or_more_as_pair : 'a Arg_type.t -> ('a * 'a Base.list) flag

one_or_more_as_pair flags must be passed one or more times.

val one_or_more_as_list : 'a Arg_type.t -> 'a Base.list flag

Like one_or_more_as_pair, but returns the flag values as a list.

val no_arg : Base.bool flag

no_arg flags may be passed at most once. The boolean returned is true iff the flag is passed on the command line.

val no_arg_register : key:'a Univ_map.With_default.Key.t -> value:'a -> Base.bool flag

no_arg_register ~key ~value is like no_arg, but associates value with key in the autocomplete environment.

val no_arg_some : 'a -> 'a Base.option flag

no_arg_some value is like no_arg, but will return Some value if the flag is passed on the command line, and return None otherwise.

val no_arg_required : 'a -> 'a flag

no_arg_some value is like no_arg, but the argument is required. This is useful in combination with choose_one_non_optional.

val no_arg_abort : exit:(Base.unit -> Base.Nothing.t) -> Base.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.

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.

A standard choice of flag name to use with escape is "--".

val escape_with_autocomplete : complete:Auto_complete.For_escape.t -> Base.string Base.list Base.option 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 : (Arg.key * Arg.spec * Arg.doc) Base.list -> ('a, 'a) t

flags_of_args_exn args creates a spec from Caml.Arg.ts, 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 args.

  • deprecated [since 2018-10] switch to Command.Param
type 'a anons = 'a Anons.t

A specification of some number of anonymous arguments.

include module type of Anons with type 'a t := 'a anons

Anonymous command-line argument specification.

val (%:) : Base.string -> 'a Arg_type.t -> 'a anons

(name %: typ) specifies a required anonymous argument of type typ.

The name must not be surrounded by whitespace; if it is, an exn will be raised.

If the name is surrounded by a special character pair (<>, {}, [] or (),) name will remain as-is, otherwise, name will be uppercased.

In the situation where name is only prefixed or only suffixed by one of the special character pairs, or different pairs are used (e.g., "<ARG]"), an exn will be raised.

The (possibly transformed) name is mentioned in the generated help for the command.

val sequence : 'a anons -> 'a Base.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 arguments.

val non_empty_sequence_as_pair : 'a anons -> ('a * 'a Base.list) anons

non_empty_sequence_as_pair anons and non_empty_sequence_as_list anons are like sequence anons except that an exception will be raised if there is not at least one anonymous argument given.

val non_empty_sequence_as_list : 'a anons -> 'a Base.list anons
val maybe : 'a anons -> 'a Base.option anons

(maybe anons) indicates that some anonymous arguments are optional.

val maybe_with_default : 'a -> 'a anons -> 'a anons

(maybe_with_default default anons) indicates an optional anonymous argument with a default value.

t2, 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
val map_anons : 'a anons -> f:('a -> 'b) -> 'b anons

map_anons anons ~f transforms the parsed result of anons by applying f.

Conversions to and from new-style Param command line specifications.

val to_param : ('a, 'r) t -> 'a -> 'r Param.t
val of_param : 'r Param.t -> ('r -> 'm, 'm) t