Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API documentation structure, voice & tone #79

Open
nikgraf opened this issue Oct 27, 2019 · 2 comments
Open

API documentation structure, voice & tone #79

nikgraf opened this issue Oct 27, 2019 · 2 comments
Labels
help wanted Extra attention is needed

Comments

@nikgraf
Copy link
Contributor

nikgraf commented Oct 27, 2019

I would like to define how the ideal documentation structure, voice and tone for a module & its functions look like.

Ideally this results in a very crisp guide on how to do this. Once this is defined we can focus on aligning all of them. (Aligning them already came up 4 times or so).

If you have ideas or good examples please chime in here as well.

One example for a guide I like is this one: https://package.elm-lang.org/help/documentation-format

@nikgraf nikgraf changed the title API example API documentation structure, voice & tone Oct 27, 2019
@nikgraf nikgraf added the help wanted Extra attention is needed label Oct 27, 2019
@baransu
Copy link
Contributor

baransu commented Oct 28, 2019

It's not exactly documentation format or guideline but in general whole HexDocs (Elixir centralized packages documentation) is a great example.

Example section: https://hexdocs.pm/elixir/writing-documentation.html

@jdeisenberg
Copy link
Contributor

Format for Examples

It would be useful to have a consistent format for code examples.

Types of Examples

Looking through the current documentation, there are several main types of examples. I'm labeling them with letters in parentheses for ease of reference.

First, the ones that give results in a comment:

(a) Belt.List.makeBy(5, i => i); /* [0, 1, 2, 3, 4] */
(b) Belt.Option.getWithDefault(None, "Banana"); /* Banana */
    Belt.Option.getWithDefault(Some("Apple"), "Banana"); /* Apple */
(c) /* returns 12.3 */
    Js.Float.fromString("12.3");
(d) [1, 2, 3]->Belt.List.take(1); /* Some([1]) */

Some examples of this type use Js.log():

(e) /* prints "7.71234e+1" */
   Js.log @@ Js.Float.toExponential(77.1234);

Another major type of example uses equality to show what the result should be:

(f) Js.Option.getWithDefault(1066, Some(15)) == 15;
    Js.Option.getWithDefault(1066, None) == 1066;
(g) let f = (x) => sqrt(float_of_int(x));
    Belt.Result.map(Ok(64), f) == Ok(8.0);

And these examples also have a Js.log() subset:

(h) Js.log(Belt.Float.toInt(1.0) === 1); /* true */
(i) Js.log(Js.String2.fromCharCode(65) == "A");
    Js.log(Js.String2.fromCharCode(0x3c8) == {js|ψ|js});
    Js.log(Js.String2.fromCharCode(0xd55c) == {js|한|js});

There are some examples that don’t provide any output of the expected result:

(j) let maybeGreetWorld = (maybeGreeting: Js.null(string)) =>
    Js.Null.bind(maybeGreeting, [@bs] greeting => greeting ++ " world!");

And some examples that are nearly full programs, such as this example for Js.Re.lastIndex():

(k) let re = [%re "/ab*/g"];
    let str = "abbcdefabh";

    let break = ref(false);
    while (! break^) {
      switch (Js.Re.exec_(re, str)) {
      | Some(result) =>
        Js.Nullable.iter(
          Js.Re.captures(result)[0],
          [@bs] match => {
            let next = string_of_int(Js.Re.lastIndex(re));
            Js.log("Found " ++ match ++ ". Next match starts at " ++ next);
          },
        )
      | None => break := true
      };
    };

Discussion

[This is all opinion. Let me know in responses what you think.]
First, I would strongly suggest that we not use examples like (k). Although it’s very complete, there’s too much information competing with the function we want to explain.

Example (j) doesn’t give the expected output, which might leave the reader in doubt as to what the result should be.

Examples (e), (h), and (i) are nice if you want to put the example into a program, run it, and see visible results. I am pretty sure I wrote example (i) for that purpose. The problem is that all you see when you run the program in (h) and (i) is a list of true and false results, which isn’t particularly useful. The use of @@ in example (e) is convenient but needs extra explanation. Example (h) uses === and (i) uses ==; we will have to make some decision about which one to use.

Comparing examples (a), (b), (c), and (d) with examples (f) and (g): it seems that (f) and (g) are more amenable to automated testing. Note the use of pipe first -> in example (d). Again, it’s convenient, but might need extra explanation.

Using Js.log()

While Js.log() is satisfying because it produces visible output, there are problems when you have examples like these:

Js.log(Js.List.cons(1, [2, 3, 4, 5])); // [1, 2, 3, 4, 5]
Js.log(Js.Option.map((. x) => (x * x), Some(3))); // Some(9)
Js.log(Js.Option.map((. x) => (x * x), None)); // None

You get output like this, which will confuse new developers unless you explain why it doesn’t look like what they expect:

[ 1, [ 2, [ 3, [Array] ] ] ] 
9
undefined

@ryyppy ryyppy pinned this issue Mar 18, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

3 participants