Is there a way to document records with fields?

Hi,

I am switching some API comments from a edoc-style to the newer -doc attributes, and was wondering whether, although records are mostly syntactic-level beasts, there was a way of documenting them, including their fields? Could not find information about it in EEPs 48 and 59.

If such information was not to end up in BEAM files, at least it could be used by source-level tools / developers themselves. And when documenting APIs, one surely would like to document records as well (whether or not this information is stored in BEAM files being then just a “technical detail”).

Thanks in advance for any information,
Best regards,

Olivier.

With edoc we create types for records and they get documented well:

-record(aka_cred,
      {k :: binary(),
      opc :: binary(),
      dif = 0 :: integer()}).
-type aka_cred() :: #aka_cred{}.
1 Like

As @vances writes, the best way is to create a type for the record and then document that type. If you want to document the individual fields I suggest maybe creating a type for each field value and documenting that? Or just put a bullet list with all the fields and their documentation.

Thanks Vances and Lukas for your answers.
From a user point of view, being able to document directly and clearly record fields seems quite welcome (defining ad hoc types being maybe needlessly convoluted?), and indeed it could be done with edoc - so it may look somewhat like a regression not being able to do it in a straightforward manner when relying on -doc attributes.

Even if this would not have any impact on BEAM chunks, along with -doc and -moduledoc attributes, maybe introducing a -fielddoc attribute (whose content would just be ignored by the compiler) may be of interest? Often larger, complex APIs involve records being passed around, and documenting them well is of great importance, and may help checks, auto-complete facilities, code generation, etc.

(BTW, a bit in the same idea, being able to document/type the precise key/value entries supported by a given “type of map” could be of use, a bit like TypedDict does, in Python)

Type specifications for map() are described in the Reference Manual:

Map :: #{}                                   %% denotes the empty map
     | #{AssociationList}

AssociationList :: Association
                 | Association, AssociationList

Association :: Type := Type                  %% denotes a mandatory association
             | Type => Type                  %% denotes an optional association

They appear similarly to records:

-type statedata() :: #{dha =>  pid() | undefined,
      cco => pid() | undefined,
      did => 0..4294967295 | undefined,
      ac => tuple() | undefined,
      tr_state => idle | init_sent | init_received | active,
      scf => sccp_codec:party_address() | undefined,
      ssf => sccp_codec:party_address() | undefined}.

While we’re at it, another edoc best practice which seems undocumented is that to get pretty printed function arguments you need to use when like this:

-spec init(Args) -> Result
   when  
      Args :: [term()],
      Result :: {ok, State, Data} | {ok, State, Data, Actions}
            | ignore | {stop, Reason},
      State :: state(),
      Data :: statedata(),
      Actions :: Action | [Action],
      Action :: gen_statem:action(),
      Reason :: term().

Honestly my pet peeve is not being able to use binary literals in type specs, be it for map keys/values or record values. This seems like something that would really improve developer experience in a significant way.

Sad-face

3 Likes