Cmdliner is an awesome tool. However, the entire documentation is explained around the word “term”. This makes it hard for me to understand the docs properly because I’m constantly thinking about what term refers to. Because how generic and broad the term “term” is it’s very hard to google it, so any explanation in context will be awesome. Even in the specific page of the term I could not find any specific explanation of what a term is
In case anyone wants to answer there, I also have open a stackoverflow quesfion: command line - What is a term in the context of cmdliner? - Stack Overflow
Also, nos that I mention the term document page, there are also a few confusing things there.
For example, given this two definitions
val const : 'a -> 'a t const v is a term that evaluates to v. val ($) : ('a -> 'b) t -> 'a t -> 'b t f $ v is a term that evaluates to the result of applying the evaluation of v to the one of f.
v refer to? I don’t see it as part of the signature, so either it is an ocaml idiom that I don’t know about or some specific thing to cmdliner.
It is defined in the docstrings you mention:
const v is a term that evaluates to v.
f $ vis a term that evaluates to the result of applying the evaluation of v to the one of f.
It is a metasyntactic variable: it can be substituted by any value. for example it means that
const 1 is a term that evaluates to
Thanks for your answer. After reading it, and rereading the docs several times I finally figured it out. The fact that
const v is not formatted as code made me not understand it is like some kind of “generic example”. I just read it as prose and I was looking for something to relate with the V within the function signatures
Probably @dbuenzli will answer this much better, but I will take a stab at it and maybe in the process will have learned something in case I’m way off the mark.
So the way I look at it,
Cmdliner defines a small EDSL (Embedded Domain-Specific Language) for the domain of turning a command-line (string) into OCaml values. Much of the choice of naming/concepts come from this “designing a language” stuff.
From the tutorial:
With Cmdliner your tool’s main function evaluates a command. A command is a value of type Cmdliner.Cmd.t which gathers a command name with a term of type Cmdliner.Term.t.
term is sometimes used interchangeably with
expression. This is confusing to many people, myself included. I think of
Term.t as just an expression in this tiny language. Typically expressions in a language have several constructors, e.g. a simple arithmetic expression can be constructed using numbers (1,2,3…), operators (+,-,*,/) that combine other arithmetic expressions.
In the case of the
Cmdliner.Term.t, it can be constructed from:
Cmdliner.Arg.t- command-line arguments, via these constructors
- Regular OCaml values (data and functions), via this constructor
- Combining other
Cmdliner.Term.tterms/expressions, via this constructor
To take the analogy maybe a little too far, as with with programs in any language, there’s two (mostly disjoint) steps:
- Lexing+Parsing: Converting a strings to appropriate terms/expressions in the language (OCaml values) while identifying them (e.g. as numbers or operators in arithmetic, or in this case, identifying them as flags or positional arguments or optional arguments etc.). For
Cmdliner, this stuff is done in two stages by first converting strings to arguments and then transforming them into terms which assigns some additional semantics for evaluation (next step).
- Evaluation: This is where all the “term evaluates to” type of documentation comes from. (1+2) evaluates to 3, in some arithmetic. In
Cmdlinerevaluation produces an
Exitcode, like most command-line utilities do - i.e. whether it succeeded or not. Note that your whole program is also a term in this language, and its evaluation is wrapped into the evaluation of the command-line - the program can fail because the command-line arguments (a term) were wrong (failed during evaluation), or because your program logic (a term) failed during evaluation. Actually, the evaluation is on a
Cmdliner.Cmd.twhich is just wrapping some helpful information with a
Also, for most simple use-cases, I would recommend the ppx_cmdliner tool instead of writing your own boilerplate for constructing terms - it’s pretty neat!
To give a pointer to learn about such APIs there’s an important keyword that’s missing (from the explanation above and from the cmdliner docs too): cmdliner terms are an applicative DSL where
($) correspond to
<*> in Haskell.
I would indeed say that Cmdliner provides a small language (an “eDSL”, embedded domain-specific language) to programmatically define mini-programs that read the command-line, do stuff (by calling the rest of your OCaml code), and return success/failure information. The library knows how to “execute/interpret” those mini-programs (for real in a terminal) but also how to inspect them to generate
--help information and/or a manpage. “Terms” in the cmdliner documentation are fragments of such mini-programs. (For people familiar with those abstractions, those terms indeed form an applicative functor.)
It’s definitely formatted as code, it’s just that the formatting is pretty subtle (the monospace code font looks similar to the serif prose font if you don’t look closely). Try zooming in, you will see the difference:
Wow, even with all the zoom in and looking for it I barely see a difference. I think I’m used to see some greyed background for inline code blocks
The OCaml website package documentation formatting has a little more contrast: Cmdliner.Term · cmdliner 1.1.1 · OCaml Packages
It depends on who you talk to. Whatever you do you will anger someone :–)
People who know the concept will get at you because you don’t mention it. Those that don’t either think that they need to understand what it is to use the library and/or that you are an elitist pedantic person worshiping GAN.
Cmdliner’s documentation did initially mention it, it was removed in this commit. Because I think the second persons are right and that it’s a distraction if you don’t know what it is and your goal is to parse command lines.
I was even glad to remove it because it matches the development history. I discovered after the fact that this was an applicative functor. In fact this is always the way I end up using these structures, by rediscovering them by gut rather than starting from them.
Generalized abstract nonsense. ↩︎
@Danielo_Rodriguez, without thinking about all the details that have now been given to you :–)
Do you think it would have been easier to understand for you if “expression” had been used ?
I can certainly try to mention that alternate vocabulary in passing in the text.
I was definitely confused about what a “term” was for a while until I realized that it was an expression. So, yes, I think a parenthetical note might be helpful for some. (I don’t think that I ever realized until now that you were using “term” in a technical sense so @ahem’s link to “term” vs. “expression” was also helpful and I wish I had seen it before).
I don’t think the “expression” expresses a less generic and broad meaning as the term “term”.
I’d welcome a short comment on the fact that the library combinators form an edsl where terms/expressions describe command-line argument parsing in a way that the library can execute or extract documentation from. (Maybe a less technical term for edsl can be added in the comment too.)
What an impressive answer! Thank you very much! People in this forum are always super nice and helpful, and this is an great demonstration of it.
This is exactly the information I was looking for, in a very well and detailed explanation. Really helped understand the library better
Yes, it would have been way easier. I ended just accepting that term was “something” (a basic block, unit, or so) and tried to continue reading the docs. But having something that I didn’t know what it was,and not being able to even google it was very frustrating and distracting. As other have mention, some little notes clarifying, what for some may be obvious will reduce the barrier.
I think this is relatively well captured by the getting started section.
The second part (about what a term can be used as) yes. The first part (about the
Term module forming a DSL), not so much. Basically there’s no description of the
In fact, re-reading the tutorial I notice the following structure:
With Cmdliner something something command.
A command is something something term.
And then there’s no “A term is …” sentence after that. There are some explanations of what you can do with a term. But no attempt at defining it.
And I guess that might be the root source of this discussion.
You summarized very well what I wanted to write / how I feel about that section. I reread it with my new and shiny knowledge and I noticed that, even substituting term by expression I was still missing what the concept is about in concrete terms. Knowing that it is part of a grammar (like a token, a literal, an expression) definitively makes it easier to understand.
As @raphael-proust said, it jumps too fast from “understandable sentence” to “everything term, term, term, apply functions in terms into values from terms to get what te term evaluates to”. Even applicative functor feels easier