# Command-line parsing (Real world OCaml)

**URL:** https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210
**Category:** Learning
**Tags:** real-world-ocaml
**Created:** [January 24, 2022, 10:58am UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210 "2022-01-24T10:58:50Z")
**Posts on this page:** 13
**Page:** 1

<div class="post-metadata">

### Author: ![user1](https://avatars.discourse-cdn.com/v4/letter/u/5daacb/32.png) [@user1](https://discuss.ocaml.org/u/user1)
#### Post date: [January 24, 2022, 10:58am UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/1 "2022-01-24T10:58:51Z")

</div>

I am reading the chapter [Command-Line Parsing - Real World OCaml](https://dev.realworldocaml.org/command-line-parsing.html), which uses the `Command` library in `Core`. After giving the signatures

```auto
#show Command.basic ;;
val basic : unit Command.basic_command
#show Command.basic_command ;;
type nonrec 'result basic_command =
    summary:string ->
    ?readme:(unit -> string) ->
    (unit -> 'result) Command.Spec.param -> Command.t

```

it states “It makes sense that Command.basic wants a parser that returns a function”. But it seems that no example is given that makes use of this. More precisely, we can define a function

```auto
let basic2 ~summary ?readme a = 
  Command.basic ~summary ?readme
  (Command.Param.map a ~f:(fun x -> (fun () -> x)))

```

which has the simpler signature

```auto
val basic2 :
  summary:string ->
  ?readme:(unit -> string) -> unit Command.Spec.param -> Command.t

```

and in all the examples of the chapter, replace `Command.basic` with `basic2`. There is probably something I’m not seeing ?

---

<div class="post-metadata">

### Author: ![Levi\_Roth](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/levi_roth/32/2268_2.png) [@Levi\_Roth](https://discuss.ocaml.org/u/Levi_Roth)
#### Post date: [January 24, 2022, 2:55pm UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/2 "2022-01-24T14:55:12Z")

</div>

`Command.basic` distinguishes between exceptions raised while parsing the command line parameters and exceptions raised in the body of the `unit -> unit` thunk. That distinction goes away in your `basic2`.

---

<div class="post-metadata">

### Author: ![user1](https://avatars.discourse-cdn.com/v4/letter/u/5daacb/32.png) [@user1](https://discuss.ocaml.org/u/user1)
#### Post date: [January 24, 2022, 6:36pm UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/3 "2022-01-24T18:36:46Z")

</div>

Thanks for your answer, but I do not understand: are you talking about the exceptions raised by Command.basic or by Command.Param.map ? Can you give a short example, or even a sketch, where behaviors of basic and basic2 would differ ?

---

<div class="post-metadata">

### Author: ![Levi\_Roth](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/levi_roth/32/2268_2.png) [@Levi\_Roth](https://discuss.ocaml.org/u/Levi_Roth)
#### Post date: [January 24, 2022, 11:29pm UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/4 "2022-01-24T23:29:37Z")

</div>

> [@user1](#):
>
> Can you give a short example, or even a sketch, where behaviors of basic and basic2 would differ ?

Sure, compare the output of the following. Here is a command defined with `Command.basic`:

```ocaml
let command_basic =
  Command.basic ~summary:"This command is defined using [Command.basic]."
    (let%map_open.Command () = return () in
     fun () -> failwith "Raising an exception.")

```

```auto
Uncaught exception:

  (Failure "Raising an exception.")

Raised at Stdlib.failwith in file "stdlib.ml", line 29, characters 17-33
Called from Core_kernel__Command.For_unix.run.(fun) in file "src/command.ml", line 2453, characters 8-238
Called from Base__Exn.handle_uncaught_aux in file "src/exn.ml", line 111, characters 6-10

```

And here is a command defined with `basic2`:

```ocaml
let command_basic2 =
  basic2 ~summary:"This command is defined using [basic2]."
    (let%map_open.Command () = return () in
     failwith "Raising an exception.")

```

```auto
Error parsing command line:

  (Failure "Raising an exception.")

For usage information, run

  main.exe basic2 -help

```

Notice how in the latter case, even though we raised an exception in what was meant to be the _body_ of our command, the exception was presented to the user as a parsing error.

---

<div class="post-metadata">

### Author: ![user1](https://avatars.discourse-cdn.com/v4/letter/u/5daacb/32.png) [@user1](https://discuss.ocaml.org/u/user1)
#### Post date: [January 25, 2022, 9:45am UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/5 "2022-01-25T09:45:22Z")

</div>

Wow, something subtle might be going on, because the two commands look synonymous to me (once you expand the definition of basic2 inside command\_basic2), but they behave differently… I should probably read again the chapter [Error Handling - Real World OCaml](https://dev.realworldocaml.org/error-handling.html) to understand this (or any other tutorial that you may recommend on that). Thanks !

For the record: I have to understand the difference between the following two functions:

```auto
let command_basic =
  Command.basic ~summary:""
    (let%map_open.Command () = return () in
     fun () -> failwith "")

let command_basic2 =
  Command.basic ~summary:""
    (Command.Param.map
      (let%map_open.Command () = return () in
       failwith "")
      ~f:(fun x -> (fun () -> x)))

```

---

<div class="post-metadata">

### Author: ![Levi\_Roth](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/levi_roth/32/2268_2.png) [@Levi\_Roth](https://discuss.ocaml.org/u/Levi_Roth)
#### Post date: [January 26, 2022, 3:15am UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/6 "2022-01-26T03:15:59Z")

</div>

I’m not sure there’s anything nuanced about error handling at issue here. It’s just about how `Command.Param.t` works:

- A `'a Param.t` is essentially a recipe for consuming 0 or more command-line arguments and producing a `'a`.
- `Command.basic` takes in a `(unit -> unit) Param.t`, (a) uses it to produce a `unit -> unit` function, and (b) calls that function.
- Because of the separation between steps (a) and (b), they can be executed in different contexts; for example, different exception handlers can be in place.
- Your `command_basic2` forces all the computation, including the call to `failwith`, to be done in step (a); conversely, in `command_basic` the call to `failwith` happens in step (b).

It might help to think about the following example, which is similar in spirit:

```ocaml
type 'a t = [`outer_param] -> 'a

let map (t : 'a t) ~(f : 'a -> 'b) : 'b t = fun `outer_param -> f (t `outer_param)

let exec : (unit -> unit) t -> unit =
 fun outer_function ->
  printf "Before calling outer function.\n";
  let inner_function = outer_function `outer_param in
  printf "Before calling inner function.\n";
  inner_function ()
;;

let a = exec (fun `outer_param () -> failwith "")
let b = exec (map (fun `outer_param -> failwith "") ~f:(fun x () -> x))

```

Calling `a` will cause both “Before calling outer function” and “Before calling inner function” to be printed before the exception is raised, but if we call `b` then only the former is printed before the exception. Does it make sense why?

---

<div class="post-metadata">

### Author: ![user1](https://avatars.discourse-cdn.com/v4/letter/u/5daacb/32.png) [@user1](https://discuss.ocaml.org/u/user1)
#### Post date: [January 27, 2022, 10:32pm UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/7 "2022-01-27T22:32:18Z")

</div>

Thanks, that’s clear. The point was to be reminded when evaluation happens or is delayed. (I think your previous version before the edit, with “option”, was simpler to understand.)

---

<div class="post-metadata">

### Author: ![UnixJunkie](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unixjunkie/32/638_2.png) [@UnixJunkie](https://discuss.ocaml.org/u/UnixJunkie)
#### Post date: [January 28, 2022, 12:29am UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/8 "2022-01-28T00:29:40Z")

</div>

There are several command line parsing libraries in opam, if you are dissatisfied with your current status.  
My biased choice is towards minicli.  
Here is an example:

> <https://github.com/UnixJunkie/minicli/blob/master/test.ml>

---

<div class="post-metadata">

### Author: ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)
#### Post date: [January 28, 2022, 12:56am UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/9 "2022-01-28T00:56:25Z")

</div>

If we are bringing up alternatives, there is also the `Arg` module which comes built-in with the standard OCaml distribution. It’s somewhat primitive in style, but does get the job done (unless your needs are extremely complex). I wrote up a post on it: [Quick-and-dirty pure command-line arguments in OCaml - DEV Community](https://dev.to/yawaramin/quick-and-dirty-pure-command-line-arguments-in-ocaml-3hcg)

---

<div class="post-metadata">

### Author: ![user1](https://avatars.discourse-cdn.com/v4/letter/u/5daacb/32.png) [@user1](https://discuss.ocaml.org/u/user1)
#### Post date: [January 28, 2022, 4:22pm UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/10 "2022-01-28T16:22:12Z")

</div>

Thanks @UnixJunkie and @yawaramin. It is indeed a bit hard to choose:

- Arg is in Stdlib, a big pro, but does it always use linux syntax for command arguments? Your post on making it functional is interesting.
- Core.Command is nicely explained in a good textbook, but uses Base/Core (as the rest of the book), which are incompatible with Stdlib.
- Cmdliner uses linux syntax, but seems to have a steeper learning curve (though I guess once one has seen such a “command-line parser”, it is not too hard to adapt to others).

---

<div class="post-metadata">

### Author: ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)
#### Post date: [January 28, 2022, 7:12pm UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/11 "2022-01-28T19:12:29Z")

</div>

@user1 note that there is no such thing as “linux syntax”. There is the [POSIX syntax](https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html) and the [GNU one](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html) and `cmdliner` supports both.

> [@user1](#):
>
> but seems to have a steeper learning curve

Did you try [the tutorial](https://docs.ocaml.pro/docs/LIBRARY.cmdliner@cmdliner.1.0.4/Cmdliner/index.html#basics) (also available to you via `odig doc cmdliner`) ?

---

<div class="post-metadata">

### Author: ![lindig](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/lindig/32/532_2.png) [@lindig](https://discuss.ocaml.org/u/lindig)
#### Post date: [January 28, 2022, 7:28pm UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/12 "2022-01-28T19:28:36Z")

</div>

Unless you are sure that you will never need git-like sub commands or online help pages, Cmdliner is worth the effort. It’s a good idea to start from a template project that has the basics covered and extend this as needed.

---

<div class="post-metadata">

### Author: ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)
#### Post date: [January 28, 2022, 7:50pm UTC](https://discuss.ocaml.org/t/command-line-parsing-real-world-ocaml/9210/13 "2022-01-28T19:50:15Z")

</div>

Well, `Arg` can also do subcommands thanks to `parse_dynamic` 🙂

I agree it’s not elegant, but it can certainly get the job done.

As for online help pages, that’s a good point. If that is required, cmdliner does nicely. However, traditionally a quick reference is printed out for `--help`, and online manual pages are maintained separately, so I wouldn’t assume one size fits all.
