├── .gitignore └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OCaml Documentation Guideline 2 | ============================= 3 | 4 | This document aims to be the community-driven one-stop reference of how to write 5 | good documentation for OCaml libraries. 6 | 7 | > Document status: DEPRECATED, but still valid -- This guideline has been 8 | > [integrated to OCamlverse][ocamlverse] so please direct your contributions 9 | > there instead. 10 | 11 | [ocamlverse]: https://ocamlverse.github.io/content/documentation_guidelines.html 12 | 13 | ## Table of Contents 14 | 15 | - [OCaml Documentation Guideline](#ocaml-documentation-guideline) 16 | * [Table of Contents](#table-of-contents) 17 | * [Background](#background) 18 | * [Purpose](#purpose) 19 | * [Structure](#structure) 20 | - [1. Writing documentation](#1-writing-documentation) 21 | * [Documentation <> Comments](#documentation--comments) 22 | * [Basic documentation syntax](#basic-documentation-syntax) 23 | * [Recommendations](#recommendations) 24 | + [Write documentation in .mli files](#write-documentation-in-mli-files) 25 | + [Write introductory documentation for the toplevel module](#write-introductory-documentation-for-the-toplevel-module) 26 | + [Organize signatures into logical sections](#organize-signatures-into-logical-sections) 27 | + [Document all your signatures](#document-all-your-signatures) 28 | + [Put documentation comments after signature elements](#put-documentation-comments-after-signature-elements) 29 | + [Write usage examples](#write-usage-examples) 30 | + [Properly document deprecations](#properly-document-deprecations) 31 | + [Have meaningful README](#have-meaningful-readme) 32 | - [2. Generating documentation](#2-generating-documentation) 33 | * [OCamldoc](#ocamldoc) 34 | * [odoc](#odoc) 35 | * [Dune (Jbuilder)](#dune-jbuilder) 36 | * [odig](#odig) 37 | * [Topkg](#topkg) 38 | - [3. Putting documentation online](#3-putting-documentation-online) 39 | - [Final words](#final-words) 40 | - [Changelog](#changelog) 41 | 42 | ## Background 43 | 44 | When we want to publish a library for the public to use, the presence of a 45 | documentation is a must. In fact, it can be the sole deciding factor of 46 | whether a potential user will end up choosing to use the library in their 47 | project. 48 | 49 | As a small illustration, in an undocumented system, every user loses time 50 | understanding the system (lost time is [O(n)][bigo]). On the other hand, 51 | in a well-documented system, one person "lost" time documenting the system 52 | but it saves time for all the other users (lost time is O(1)). That is a 53 | rather good investment. 54 | 55 | However, what makes a good documentation? How good is "good enough"? These 56 | questions can be a rather confusing obstacle, particularly for the 57 | less-experienced devs publishing their library for the first time and 58 | still unfamiliar with OCaml documentation tools. But it shouldn't be that 59 | way! 60 | 61 | Writing documentation should be fun, easy, and rewarding. It should not be 62 | an afterthought; instead, it should be included in the development phase, 63 | because as we write docs we can gain useful feedback on how good our 64 | library's design is from a user's perspective. 65 | 66 | These things considered, it would be nice to have a guideline on how to 67 | write good, effective documentation in the OCaml ecosystem. This document 68 | aims to tackle that problem. 69 | 70 | [bigo]: https://en.wikipedia.org/wiki/Big_O_notation 71 | 72 | ## Purpose 73 | 74 | The purpose of this guideline is to provide a standard that can be referred 75 | to by library authors to (ask contributors to) produce good docs. 76 | 77 | This guideline is expected to evolve with the latest discovered practices, 78 | language features, tooling features, and feedback from community. This document 79 | maintain a changelog to track these changes. 80 | 81 | ## Structure 82 | 83 | This guideline will be separated into three sections: 84 | 85 | 1. Writing documentation 86 | 2. Generating documentation 87 | 3. Putting documentation online 88 | 89 | # 1. Writing documentation 90 | 91 | ## Documentation <> Comments 92 | 93 | The first thing that needs to be established is the difference between 94 | documentation and comments. Elixir's [Writing Documentation][exdocs] guide 95 | has a nice section explaining about this: 96 | 97 | > Documentation is for users of your Application Programming Interface (API), 98 | > be it your co-worker or your future self. Modules and functions must always 99 | > be documented if they are part of your API. 100 | > 101 | > Code comments are for developers reading the code. They are useful to mark 102 | > improvements, leave notes for developers reading the code (for example, you 103 | > decided not to call a function due to a bug in a library) and so forth. 104 | > 105 | > In other words: documentation is required, code comments are optional. 106 | 107 | [exdocs]: https://hexdocs.pm/elixir/master/writing-documentation.html 108 | 109 | ## Basic documentation syntax 110 | 111 | OCaml documentation is written in the **OCamldoc** syntax. This is different 112 | from the usual markdown that many other languages use. OCamldoc is much more 113 | powerful than markdown, with distinguishing features such as cross-references 114 | and target-specific formatting (e.g. for LaTeX). 115 | 116 | Documentations are written as special comments wrapped in `(**` and `*)`. The 117 | content is free-form text much like markdown but with different set of 118 | formatting constructs. 119 | 120 | The [OCamldoc manual][ocamldocman] includes the formal explanation of the 121 | syntax, and we recommend familiarizing yourself with it. 122 | 123 | Here is the table that maps common markdown syntax to the OCamldoc counterpart: 124 | 125 | | Description | Markdown | OCamldoc | 126 | | ----------------------- | ---------------- | ---------------- | 127 | | Section header | `# text` | `{0 text}` | 128 | | | `## text` | `{1 text}` | 129 | | | `### text` | `{2 text}` | 130 | | | | etc. | 131 | | Section header with labels (for cross-referencing) | _N/A_ | `{0:mylabel text}` | 132 | | Bold text | `**text**` | `{b text}` | 133 | | Italic text | `*text*` | `{i text}` | 134 | | Link | `[text](url)` | `{{: url} text}` | 135 | | Inline code | \`code\` | `[code]` | 136 | | Code block | \`\`\`code\`\`\` | `{[code]}` | 137 | | Target-specific content | _N/A_ | `{%latex: text %}` | 138 | | Cross-reference | _N/A_ | `{!name}`, `{!module:name}`, `{!type:name}`, `{!section:name}`, etc. 139 | | Unordered lists | - foo
- bar
- baz | - foo
- bar
- baz | 140 | | Ordered lists | 1. foo
2. bar
3. baz | + foo
+ bar
+ baz | 141 | | Raw HTML | _Directly_ | `{%html: ... %}` | 142 | | Images | `![alt text](url)` | `{%html: %}`} 143 | 144 | Note that OCamldoc _the syntax_ is a different from OCamldoc _the tool_. The 145 | latter is a documentation generation tool that understands OCamldoc syntax. We 146 | are going to touch about documentation generation on the later section of this 147 | document. 148 | 149 | [ocamldocman]: https://caml.inria.fr/pub/docs/manual-ocaml/ocamldoc.html#sec349 150 | 151 | ## Recommendations 152 | 153 | This section introduces several recommendations that should be followed on 154 | writing library documentation. 155 | 156 | ### Write documentation in .mli files 157 | 158 | This item recommends putting documentation in `.mli` files. 159 | 160 | OCaml encourages people to separate the interface of a module from its 161 | implementation. Interface go to files with `.mli` extension, while 162 | implementation go to `.ml` files. Documentation is part of the interface 163 | of a module, hence it is logical to put it on `.mli`s, unburdened by 164 | implementation details. 165 | 166 | This approach also has the benefit of organizability: as OCaml is a strict 167 | language where order of declarations are important, putting docs in `.ml` 168 | files means you will have less flexibility in organizing your doc comments. 169 | `.mli` files do not have that restriction, and you may reorder at will. 170 | 171 | Note that this does _not_ mean you should not have comments on `.ml` files. 172 | Utilize comments on `.ml` files for implementation notes to help contributors. 173 | See the "Documentation <> Comments" section above. 174 | 175 | Example: 176 | 177 | ```ocaml 178 | (* ----- BAD ----- *) 179 | (* math.ml *) 180 | 181 | let add a b = a + b 182 | (** [add a b] returns the result of a + b *) 183 | 184 | 185 | (* ----- GOOD ----- *) 186 | (* math.ml *) 187 | 188 | let add a b = a + b 189 | 190 | (* math.mli *) 191 | 192 | val add : int -> int -> int 193 | (** [add a b] returns the result of a + b *) 194 | ``` 195 | 196 | 197 | ### Write introductory documentation for the toplevel module 198 | 199 | This item recommends to utilize the special first comment of the module, which 200 | is not attached to anything, to give introductory material for the module. 201 | 202 | Keep the first paragraph of the documentation concise and simple, typically 203 | one-line. Documentation tools such as [odig] may use the first line to generate 204 | a summary of the module. 205 | 206 | Unless the module is really trivial (which is not true for most of the time), 207 | the next paragraph(s) should explain in greater detail: 208 | 209 | 1. what capabilities the module provides, 210 | 2. why/when we should use it, 211 | 3. usage examples, and 212 | 4. links/reference to sections in the documentation. 213 | 214 | Example (taken from Daniel Bünzli's [Vg library][vg]—formatting 215 | removed): 216 | 217 | ```ocaml 218 | (** Declarative 2D vector graphics. 219 | 220 | Vg is a declarative 2D vector graphics library. In Vg, images are values 221 | that denote functions mapping points of the cartesian plane to colors. 222 | The library provides combinators to define and compose them. Renderers 223 | for PDF, SVG and the HTML canvas are distributed with the library. An 224 | API allows to implement new renderers. 225 | 226 | Consult the {{!basics}basics}, the {{!semantics}semantics} and 227 | {{!examples}examples}. 228 | 229 | Open the module to use it, this defines only modules and types in 230 | your scope and a single composition operator. *) 231 | ``` 232 | 233 | For usage examples, you may keep it on the introductory documentation or 234 | introduce a new section linked from it. See "Write usage examples". 235 | 236 | [odig]: https://github.com/dbuenzli/odig 237 | [vg]: https://github.com/dbuenzli/vg/blob/master/src/vg.mli 238 | 239 | ### Organize signatures into logical sections 240 | 241 | This item recommends to use sections liberally, particularly for modules with 242 | many exported modules, types, and values. By grouping them into sections, we 243 | gain the benefit of cohesive documentation elements being close to one another, 244 | bringing better clarity of how things are organized. 245 | 246 | The `.mli` file is basically a document much like a book, so feel free to 247 | organize the contents to be as coherent as possible without caring about the 248 | order of values in the implementation file. 249 | 250 | An example would be Simon Cruanes' [sequence] documentation, where the elements 251 | are neatly organized into sections, such as "Build a sequence", "Consume a 252 | sequence", etc. Another example is Daniel Bünzli's [Fmt][fmt] documentation. 253 | 254 | Note that you can also add paragraphs after every section headers should you 255 | deem it necessary. 256 | 257 | [sequence]: http://c-cube.github.io/sequence/sequence/Sequence/index.html 258 | [fmt]: http://erratique.ch/software/fmt/doc/Fmt.html 259 | 260 | ### Document all your signatures 261 | 262 | This item recommends that all signatures (modules, types, functions, etc.) 263 | should have a documentation attached to it. At the minimum, it should provide 264 | a one-liner that explains what the function does. A better effort would be to 265 | also add minimal usage examples for interesting functions. 266 | 267 | The rationale of this recommendation is that undocumented values tend to give 268 | the feeling of unfinished work and may prevent users from adopting the library. 269 | 270 | Example (inspired by the [Jane Street Style Guide][jststyle]): 271 | 272 | ```ocaml 273 | (* ----- BAD ----- *) 274 | 275 | val compare : string -> string -> int 276 | 277 | (* ----- BETTER ----- *) 278 | 279 | val compare : string -> string -> int 280 | (** Compares two strings *) 281 | 282 | (* ----- GOOD ----- *) 283 | 284 | val compare : string -> string -> int 285 | (** [compare x y] compares the strings [x] and [y] lexicographically. *) 286 | ``` 287 | 288 | The above example also shows that it is recommended to start a function 289 | documentation with a call to the function and naming the arguments (the 290 | `[compare x y]` part). This gives you the benefit of being able to refer to 291 | the variables in the doc text afterwards, especially when the function does 292 | not accept labeled args. 293 | 294 | The Jane Street Style Guide also has a reasonable point for writing docs for 295 | trivial functions: 296 | 297 | > If you really can’t find anything useful to add with a comment, it’s 298 | > acceptable to have a comment that is redundant with the type and name. 299 | 300 | The point is, we don't have to spend time to ask ourselves whether a 301 | signature item warrants a documentation string or not. Just do it, even 302 | if it feels entirely pointless and redundant it has to become a reflex. 303 | 304 | We might find the semantics of a signature item obvious based solely on 305 | its name, but that's just because _we_ implemented it. A reader not 306 | familliar with the implementation may not find it so obvious. 307 | 308 | [jststyle]: https://opensource.janestreet.com/standards/ 309 | 310 | ### Put documentation comments after signature elements 311 | 312 | This item recommends attaching documentation comments after the signature 313 | item they represent, as shown in the previous examples. 314 | 315 | OCaml supports putting documentation before or after signature elements. 316 | Note that this might be foreign to some users of other languages that are 317 | used to putting documentation _before_ the element they refer, but putting 318 | it after the element has several benefits: 319 | 320 | 1. When you scan `.mli`s your eyes anchor on type, val, etc. keywords. 321 | Once you spotted the one of interest, the documentation then follows 322 | in the scanning direction, rather than having to switch directions. 323 | 324 | 2. That's the way they end up being presented by documentation generators 325 | (OCamldoc, odoc, explained later in this document), so there will be 326 | less cognitive dissonance when you switch from one to the other. A 327 | documentation generator output will then just be a prettified view of 328 | your `.mli`. 329 | 330 | An exception to this rule is for the first toplevel documentation and for 331 | inner-modules documentation. For the latter, documentation should go 332 | before the module declaration. 333 | 334 | This rule has the unfortunate caveat for variants: if you don't document 335 | every case of the variant, then at least the last case would require to 336 | have an empty comment block `(** *)` so that the following block will 337 | be attached correctly to the whole variant. 338 | 339 | Example (taken from [Alcotest] documentation): 340 | 341 | ```ocaml 342 | type speed_level = [`Quick | `Slow] 343 | (** Speed level for a test. *) 344 | 345 | type 'a test_case = string * speed_level * ('a -> unit) 346 | (** A test case is an UTF-8 encoded documentation string, a speed 347 | level and a function to execute. *) 348 | 349 | val test_case: string -> speed_level -> ('a -> unit) -> 'a test_case 350 | (** [test_case n s f] is the test case [n] running at speed [s] using 351 | the function [f]. *) 352 | 353 | type 'a test = string * 'a test_case list 354 | (** A test is an US-ASCII encoded name and a list of test cases. *) 355 | 356 | exception Test_error 357 | (** The exception return by {!run} in case of errors. *) 358 | ``` 359 | 360 | [alcotest]: https://github.com/mirage/alcotest 361 | 362 | ### Write usage examples 363 | 364 | This item recommends that usage examples should be made required, especially 365 | on non-trivial modules and functions. 366 | 367 | Usage examples tend to give more clarity of how things work instead of relying 368 | users to stitch ideas together from the disconnected API reference. 369 | 370 | There are several ways to introduce usage examples in a module documentation: 371 | 372 | 1. Put it in the introductory toplevel documentation. This is preferable if the 373 | example is short enough and encompasses the usage of the whole module. 374 | 375 | 2. Put it in a different section (e.g. "Basics", "Examples", etc.), usually 376 | after the API reference, for longer examples and/or explanations. Requires 377 | to be linked from first toplevel documentation. 378 | 379 | 3. Put it in a function's documentation if the usage example is specific for 380 | a function. 381 | 382 | 4. Put it in separate manual (`.mld`) files if the usage examples span multiple 383 | toplevel modules. 384 | 385 | When introducing example code blocks, use the `{[...]}` construct. 386 | 387 | Small, runnable examples are good, but non-runnable illustrative examples are 388 | better than none. 389 | 390 | ### Properly document deprecations 391 | 392 | This item recommends properly documenting value deprecations. This includes: 393 | 394 | - Annotating the docs with `@deprecated` tag 395 | - Making note of what should be used instead (utilize cross-reference) 396 | - (Optional) Making note of why it is deprecated 397 | 398 | Deprecations are useful for moving the API forward, and annotating them can 399 | make it easier for users to actually migrate out of the deprecated values. 400 | 401 | Avoid adding `@deprecated` tag without providing alternative(s). 402 | 403 | ### Have meaningful README 404 | 405 | This item recommends putting informative material in project READMEs. 406 | 407 | Most libraries are hosted on public services such as GitHub, which by default 408 | display the README on the main page. Such pages are often the first entry point 409 | for users looking for a library to use. Hence, READMEs should be treated as 410 | such, and should contain information that help such users decide whether they 411 | should use a library. 412 | 413 | At the very least, READMEs should include: 414 | 415 | - The name of the library 416 | - A short-ish description of the library (preferably not only a one-liner) 417 | - Instruction to install the library 418 | - Link to manual, usage examples, and online API documentation 419 | 420 | It is good to have a (probably common) usage example written out on the README 421 | to give visitors illustrative idea of how the library work. In fact, if you have 422 | the resource, investing in making the README thorough is preferable, although 423 | that would mean extra effort of keeping the README and API documentation in 424 | sync. 425 | 426 | # 2. Generating documentation 427 | 428 | _This section is meant for library authors trying to preview their library's 429 | documentation, but the same methods can be used by library users._ 430 | 431 | There are several tools related to generating documentation, and we're going to 432 | give a quick look on how they interact together. 433 | 434 | ## OCamldoc 435 | 436 | OCamldoc is a tool to generate documentation from source files. The official 437 | user manual page [on documentation generator][ocamldoc] shows how to use it. 438 | 439 | Note that the use of OCamldoc the tool is a bit discouraged at the moment due 440 | to its deficiencies on handling some parts of the OCaml language, particularly 441 | in regards to linking and references between modules and libraries. 442 | 443 | [ocamldoc]: https://caml.inria.fr/pub/docs/manual-ocaml/ocamldoc.html 444 | 445 | ## odoc 446 | 447 | [odoc] is also a documentation generator that aims to address the aforementioned 448 | limitations of OCamldoc, and should be the go-to tool for documentation 449 | generation. 450 | 451 | That said, odoc is a low-level tool, and casual end-users should not need to 452 | call it directly. Instead, we rely on higher-level tools that knows how to use 453 | odoc to stitch together documentations, such as Dune, odig, and Topkg. 454 | 455 | Even so, you will need to install odoc on your switch. You can install odoc 456 | via opam: 457 | 458 | ``` 459 | opam install odoc 460 | ``` 461 | 462 | [odoc]: https://github.com/ocaml/odoc 463 | 464 | ## Dune (Jbuilder) 465 | 466 | [Dune] (formerly Jbuilder) is a build system by Jane Street. It supports 467 | generating documentations out of the box. To be able to generate documentation 468 | for your Dune-powered projects (usually notable by the presence of `jbuild` or 469 | `jbuild-workspace` files), you need to: 470 | 471 | - make sure that `jbuilder` and `odoc` are installed, and 472 | - make sure that a `.opam` file exists. 473 | 474 | If you haven't already, install Dune and odoc: 475 | 476 | ``` 477 | opam install jbuilder odoc 478 | ``` 479 | 480 | To generate the documentation, run this command: 481 | 482 | ``` 483 | jbuilder build @doc 484 | ``` 485 | 486 | The documentation will then be available at 487 | `_build/default/_doc/_html/index.html`. You may open it with your favorite web 488 | browser. 489 | 490 | For more on Dune, consult the [official docs][dunedocs]. 491 | 492 | [dune]: https://github.com/ocaml/dune 493 | [dunedocs]: http://jbuilder.readthedocs.io/en/latest/ 494 | 495 | ## odig 496 | 497 | [odig] is a documentation mining tool. Basically what it does is it scans the 498 | installed packages on your current opam switch and tries to build their 499 | documentations. What makes odig powerful is that, while Dune is good for 500 | generating documentation for a single package, odig can actually cross-reference 501 | modules and dependencies on the generated docs. 502 | 503 | To start using odig, install the required packages: 504 | 505 | ``` 506 | opam install odig odoc ocaml-manual 507 | ``` 508 | 509 | Then, tell odig to generate the documentation for *all* installed libraries 510 | using odoc. 511 | 512 | ``` 513 | odig odoc 514 | ``` 515 | 516 | Note that the above operation may take a while in switches with many packages! 517 | It may also trigger warnings when trying to generate documentation for some 518 | packages, but it can be safely ignored (for now). 519 | 520 | Next step, open the documentation in your browser via: 521 | 522 | ``` 523 | odig doc 524 | ``` 525 | 526 | You will be presented with the index of installed libraries that you can browse 527 | to get their API documentations. 528 | 529 | Note that odig also supports generating and showing docs for a specific 530 | library, by specifying the package name after the commands, e.g. to generate 531 | and show docs only for the `lwt` library: 532 | 533 | ``` 534 | odig odoc lwt 535 | odig doc lwt 536 | ``` 537 | 538 | Consult the [README page][odig] for more info. 539 | 540 | ## Topkg 541 | 542 | [Topkg] is a packager for distributing OCaml software. Among other neat 543 | features, it also supports generating documentation. 544 | 545 | On a Topkg-powered projects (notable by the presence of `pkg/pkg.ml` file), 546 | make sure that you have `topkg-care` (the Topkg CLI tool) installed: 547 | 548 | ``` 549 | opam install topkg-care 550 | ``` 551 | 552 | You can then invoke: 553 | 554 | ``` 555 | topkg doc 556 | ``` 557 | 558 | It will generate the documentation in the `_build/doc/api.docdir/` directory. 559 | 560 | Consult the [basics][topkgbasics] section of the Topkg API documentation for 561 | more info. 562 | 563 | [topkg]: https://github.com/dbuenzli/topkg 564 | [topkgbasics]: http://erratique.ch/software/topkg/doc/Topkg.html#basics 565 | 566 | # 3. Putting documentation online 567 | 568 | While generating docs locally may be beneficial in some cases, the requirement 569 | of having the libraries installed or the source available (and dev environment 570 | set up) to get the docs is not ideal. Consider users that are looking for a 571 | library that do X, having to build and install multiple packages that provides 572 | X-like capability. 573 | 574 | Hence, **putting documentation online should be required for all libraries that 575 | expect public use**. 576 | 577 | One way to achieve that, if your library project is hosted on GitHub, is to 578 | utilize the free [GitHub Pages][ghpages] feature. GitHub Pages support 579 | multiple publishing source, such as the `master` branch, `gh-pages` branch, 580 | and a `docs/` folder on `master` branch. Since `master` branch are reserved 581 | for library code, feel free to choose one of the latter two. Consult the 582 | [GitHub Pages documentation][ghpagesdocs] for more info. 583 | 584 | After you chose a publishing source, you can put the generated documentation 585 | achieved from the previous section there and push it. The docs should then be 586 | online in no time. 587 | 588 | If your project uses Topkg as a packager, the `topkg-care` tool also provides 589 | the capability of doing those menial tasks of setting up the branch and copying 590 | the files for you. It is as simple as invoking: 591 | 592 | ``` 593 | topkg distrib 594 | topkg publish doc 595 | ``` 596 | 597 | It's also worth noting that works are in progress to bring forth a centralized 598 | online documentation site (docs.ocaml.org, currently not available) for OCaml 599 | libraries, which will make browsing docs significantly easier. 600 | 601 | [ghpages]: https://pages.github.com/ 602 | [ghpagesdocs]: https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/ 603 | 604 | # Final words 605 | 606 | Hopefully this guide can help improve the documentation of the whole OCaml 607 | ecosystem. Great tools alone don't make good documentations—humans do. 608 | 609 | Keep calm and write docs! 610 | 611 | # Changelog 612 | 613 | - [2018-04-18] Add the "Put documentation comments after signature elements" recommendation (credit to @dbuenzli) 614 | - [2018-04-17] Initial draft 615 | --------------------------------------------------------------------------------