├── .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 | `` | `{%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 |
--------------------------------------------------------------------------------