Is it necessary to warn about the @doc attribute for a private function

TL;DR:

defmodule Mod do
  @doc """
  Public docs
  """
  def public, do: ...

  # Private docs
  defp private, do: ...
end

that’s it!

While we can’t use @doc for documenting private functions, nothing stops us for using comments to document them. Similarly, if a module is meant to be private we’d mark it as @moduledoc false and document it by using comments. There are many such examples in Elixir itself and libraries.

I don’t think it’s fair to say that documenting internals is discouraged, it’s just we can’t use mechanisms like @doc and @moduledoc for it. @doc etc is used by the compiler to put documentation into beam chunks so that it can be later used by tooling like IEx, ExDoc etc to display public docs. Since private functions are, well, private, tools like IEx and ExDoc won’t seen them so it doesn’t make sense to use @doc etc for them. In fact the compiler could even inline private functions as an optimisation. We could special-case @doc not to put anything into beam chunk if it sees a private function, but that seems like needless complexity. Even if @doc wouldn’t warn, since the tooling won’t show private docs, the only way to see them is to look at the source code. And at the source code, is it really a big deal to use a code comment over a @doc?

9 Likes

Well, I never said a word against writing inline comments here and there and I do that myself on a daily basis. This is like a stick-it note to future self, and this is fine.

I am advocating the clear split, in our mentality as well, between the documentation and drop-in comments.

I’m sorry to hear that this has been your experience. Mine have been the exact opposite, I have found that Elixir community has an emphasis on comprehensive documentation that I have rarely seen elsewhere.

7 Likes

I think this is the crux of it, it is (to me at least) strange that the comments I’m putting in my code are being used to generate the documentation. I expect a block of comments above my code to be used for myself or other developers working on it, if I then want to generate outside documenation for other people to consume - say if I’ve created a libary or whatnot - then I want to explicitly do that.

It is a mental shift that the comments in the code are expected to be used outside of the code as well. Still not sure how I feel about this, and I just ignore warnings for it when I document private functions :slight_smile:

Yes, this is total non-sense. Our instance on docs and code comments have been clear since day one.

  1. Documentation is for users of your code (who do not necessarily have access to source)
  2. Code comments are for maintainers of the code (who are looking at the source right now)

It is really important to make this distinction because you often want to provide different information for those different “audiences”. Elixir makes use of both whenever necessary. Sometimes we even have a doc followed by extensive code comments. A user of the function does not care about implementation details but, if you are there in the source, trying to figure it out why it works like that, the information is there.

A private only exists in the source code, that’s why you can’t document something private. But you can certainly add code comments to it. Please do write code comments whenever you feel it is necessary.

18 Likes

This explanation makes better sense than others I’ve seen so far.

I get this also, but it doesn’t satisfy my want for one consistent way to document/comment functions. In languages like C#, Java, Rust, Go, Crystal, etc. there’s one consistent way. The documentation creation tools for web pages and etc, just weeds out the private function docs.

We can try to use semantics to say comments aren’t documentation or documentation aren’t comments, but at the end of the day any character that’s written in source code, be it statements or comments, are documentation for someone.

Have enough people brought this topic up that it could at least be considered by the dev team?

1 Like

And which of those language make a distinction between documentation and code comments? Otherwise they do not provide the distinction which is the root reason of this conversation and therefore I don’t believe it is an apt comparison.

The other language I know that provides a similar distinction to Elixir, which is Python, recommends in its official style guide to use comments for privates:

Write docstrings for all public modules, functions, classes, and methods. Docstrings are not necessary for non-public methods, but you should have a comment that describes what the method does. This comment should appear after the “def” line.

You just said my explanation makes better sense than all others, then why not give it a try? The first step then is to recognize that not all characters in the source code are documentation. To clarify, I understood what you meant, but because we are talking about semantics, then terminology matters.

Try to adopt the new terminology and then in the future you can write down your first hand experiences, what worked and what didn’t.

The other thing I have remembered is that documentation and comments are different contracts too. A documentation is a contract beyond the current source file. A code comment is tied to the current source. There is really a lot you can learn and express by separating those two concerns.

A lot of people asked for a JSON library in core and that is not happening. A lot of people asked for variations of the pipe operator and that is not happening either. Features are not driven by how many people ask for it. :slight_smile:

7 Likes

@doc comments are a qualitatively different thing from code comments. They document the API of a function. Code comments, document the how and why of the implementation. Wanting to use the same mechanism to perform two completely different tasks seems confused to me.

3 Likes

What about projects where these kind of users do not exist? My projects are mostly not libraries to be used by other people, rather, if there are other devs, they will have the source and may use the documentation to supplement that.

Issues to me that are caused by this enforcement of no @doc for private functions:

  • Inconsistent coding styles, doc vs just comment.
  • Private functions do not have descriptions to display in programming tools when suggesting or looking up docs of a function, since they do not use the structured @doc that is used for that. It would be useful to see the description of a private function without scrolling.
  • Extra work when moving function from private to public since comments need to be rewritten from one style to another.
2 Likes

So those “private” functions are basically public?

They are public inside that module. :stuck_out_tongue:

1 Like

This is another good point. Tooling picks up the @doc tags for functions, but doesn’t for regular comments.

Perhaps all of us are just looking at this the wrong way completely. Jose pointed out that the @doc attribute is like python’s docstrings. It’s a quick way to get documentation printed out, and you can run a parser to find example strings for tests. It is however lacking the power that the documentation/commenting systems of languages like Java and C# provide out of the box.

I tend to like strong, statically typed languages like Rust and C#, where a lot of what’s going on can be gleamed from the inputs to a function and it’s return type. To get anything close to that I need to use Dialyzer. Without Dialyzer I comment my functions more heavily than I would otherwise. I still comment them while using Dialyzer, I just don’t have to add what’s expected to go into and out of the function.

Let’s take the function below for example:

defp build_url(%{ url_params: params }) do
...
end

From that I can gleam that I’m taking in a map or struct that has url_params as a field. Yeah that’s great and simple, but really what I want devs coming after me to know is that we are using a Request struct in the pipeline. I know, I know, the function doesn’t have to take in a specific type and that’s all we really need, but is that really enough?

I could define it the longer way:

defp build_url(request) do
  url_params = Map.get(request, :url_params)
...
end

No, that’s not right either. What I really want to do is:

@doc """
Description: Using the url_params field from a Request struct, we build ... blah
Consider: Note that this function may create a string that is not url friendly if
the params provided have any of the following characters: 
< > # % { } | \ ^ ~ [ ] `. 

To guarantee a url friendly string, filter the params through the 
filter_url_params function first. 

Example:
url_string = %Request{ url_params: [ "comp": "list", "max_items": 40 ] }
|> filter_url_params
|> build_url

Param: a Request struct
Return: A string that may or may not be url friendly.
"""
defp build_url(%{url_params: url_params}) do
...
end

I don’t need a nice pretty web page created from that, but it is really helpful when the tools like VS Code or Intellij pick it up. They don’t/won’t do that with # comments.

1 Like

In that case, I would either surface it to the documentation of the parent function (the one that calls build_url), or move it to its own public function so I can properly document and test it.

For example, you could mention in the public function that any parameter containing < > # % { } | \ ^ ~ [ ] will be filtered out before building the URL. It may help you focus on the behaviour of the code instead of focusing on the implementation. But I can’t say for sure, I have only part of the context.

I’m sure this has probably been raised (and rejected) before but what might make commenting ‘easier’ and therefore as appealing as multi-line @doc blocks would be support for multi-line comments; something like /# and #/ perhaps.

1 Like

Jose, what happens when the compiler encounters a @doc attribute on a private function? Does it affect the compiler, or the results at all?

The behaviour could be changed to discard @doc but I think allowing @doc in privates would open up a bunch of other problems. For example, if you try to write a doctest, it won’t work, because you can’t invoke a private function. Oh, do you want to access the @doc in your editor or terminal? That won’t work either, because private functions do not necessarily exist after the module is compiled, they can be inlined, transformed or even fully removed. Allowing @doc would indicate that those two constructs (public and private functions) are somewhat at the same level while they quite clearly aren’t.

All of this has already been said, so I will politely bow out of the discussion as I don’t have anything new to add here. :slight_smile:

11 Likes

I like @doc's because it appears in my IDE when using a function call, comments do not (plus you can’t be sure what a ‘comment’ is attaching to unlike a @doc. Even when using private functions that detail some complex algorithm and what this specific part is doing is amazingly useful. I do # comment those currently but I get no intellisense about that where I would if it were @doc.

5 Likes

I agree with the request for a multi line comment format. I currently just keep the doc blocks for private functions and ignore the compiler warnings since I like the IDE color formatting and don’t like the looks of:

_ = “””
Comment block
“””

2 Likes

I have done the same private documentation style, but feels inconsistent to me.

There will be no general agreed solution for this in the Elixir community. No language has everything that everybody wants, sorry!

Move on.