Documenting elements of a struct

How do I document the members of a struct so that Documenter generates documentation for them?

"""
This is an X
"""
struct X
    "This is a"
    a::String
    "This is b"
    b::Char
    "This is c"
    c
end

Fwiw some people feel that struct properties should be considered private and shouldn’t be publicly documented, preferring accessor functions instead.

1 Like

I have no idea if there’s a better/canonical way, but I’ve done something like this in the past:

"""
This is an `X`

# Fields
- a: First letter of the English alphabet
- b: Second letter of the English alphabet
- c: C is for cookie
"""
struct X
    a::String
    b::Char
    c
end

This may be really frowned upon though, I don’t know :grin:

3 Likes

How would you want to access it? Keep in mind that once I do X.a, that’s just a String. Julia has no knowledge that X.a came from the struct X, without metaprogramming.

True; but that’s irrelevant to the documentation, which is about what to expect when doing X.a.

True, ? X.a could be smart.

1 Like

That would be fine if they were private (and required accessors) but as it is I can X.a, so it should be possible to document what to expect when doing that (e.g. ? X.a should work).

1 Like

There is a difference between being considered private and enforcing that the attributes are externally inaccessible. Julia considers some things nonpublic but doesn’t enforce their inaccessibility.

1 Like

Maybe I’m not (noob) seeing the wisdom of making it impossible to document (in the usual way) something that’s “considered” non-public if it in fact is publicly accessible.

It doesn’t make much sense to me that we conflate “documented” with “guaranteed to be forward compatible”, but here we are.

GitHub - JuliaDocs/DocStringExtensions.jl: Extensions for Julia's docsystem. has FIELDS and TYPEDFIELDS which can be used for automatically embedding field docs in the main docstring:

julia> using DocStringExtensions

julia> """
       This is an X

       $(FIELDS)
       """
       struct X
           "This is a"
           a::String
           "This is b"
           b::Char
           "This is c"
           c
       end
X

help?> X
search: X xor exp Expr exp2 exit axes expm1 exp10 export EXPORTS extrema exponent Exception expanduser ExponentialBackOff max Text nextpow nextind maximum nextprod maximum!

  This is an X

    •  a
       This is a

    •  b
       This is b

    •  c
       This is c

julia> """
       This is an X

       $(TYPEDFIELDS)
       """
       struct X
           "This is a"
           a::String
           "This is b"
           b::Char
           "This is c"
           c
       end
X

help?> X
search: X xor exp Expr exp2 exit axes expm1 exp10 export EXPORTS extrema exponent Exception expanduser ExponentialBackOff max Text nextpow nextind maximum nextprod maximum!

  This is an X

    •  a::String
       This is a

    •  b::Char
       This is b

    •  c::Any
       This is c
12 Likes

That looks good.

But how do I use that to build docs. When I

cd MY_PKG
(...) pkg> activate .
julia>  using Revise
julia>  include("docs/make.jl")

(after add DocStringExtensions to docs and putting using DocStringExtensions in make.jl, both of which I assume are necessary) I get ERROR: LoadError: UndefVarError: TYPEDFIELDS not defined.

using DocStringExtensions needs to be placed in your package where you are using TYPEDFIELDS, not in docs/make.jl. (It also needs to be installed in your package, not your docs subdirectory’s Project.toml.)

2 Likes

Oh wow. So (again noob) I’m definitely not understanding something here: adding a dependency to my code I order to document it (especially considering how otherwise tidy the separate doc environment is) seems wrong.

I’m clearly trying to do something I’m not supposed to do.

The docs/Project.toml environment is for building static documentation based on the contents of a package. That’s not the only place we want to be able to access docstrings for packages though. We also want to be able to query docstrings from a live REPL/editor. The “abbreviation” objects, such as TYPEDFIELDS, need to be visible inside your package where your docstrings are located to be able to do their thing, so putting them into a separate environment is not possible. They have to be imported into the package’s module to work.

3 Likes

Yeah I see that now. Clearly my attempt to automatically document parts of structs in the way I’m used to isn’t Julian. (Again, novice here.) Having to make a documentation package a dependency is definitely (Julian or otherwise) not what I want to do.

For the most part its not bad to add lightweight dependencies to a package. The mentioned package doesn’t just add a mechanism to generate documentation, but also annotates the struct with a docstring accessible from help and the like. If I were a user of your package, I would like getting that help in my REPL, no matter a small dependency. B.t.w. I agree with you that a public accessible thing should be easily documented. I’d say go for that package

Also, I just had a look at the source code, the src seems to be about ~1000 loc and the only secondary dependency is LibGit2

5 Likes

I’m totally in agreement that any publicly accessible thing should be document (and a bit confused about the assertion that any such thing should ever be “considered” private), but I just can’t bring myself to include a dependency that isn’t strictly related to what my package does — especially when the same result can be achieved (if trivially) without introducing such a dependency.

It’s good to not add lots of uneccesary dependencies (it can help with compilation time etc), so if you find the alternative sufficient, then stick with that. On the other hand, packages are there to be used, and Julia makes barrier to entry for dependencies really low. Julia also started moving lots of base code into seperate packages, like Move SharedArrays to a package · Issue #23713 · JuliaLang/julia · GitHub and encourages the community to expand and use the package infrastructure.

So basically all in moderation. You seem to know what you aim for, so stick with that

2 Likes

Yeah I’ve got no problem with dependences. But putting a documentation dependence in the package itself feels wrong.