Strings between fields in struct: Why a valid code

In the source of the package SimpleArgParse.jl, the definition of
mutable struct ArgumentParser
contains field definitions intersperses by strings like the following:

struct Foo
    "x value"
    x::Int
    "x name"
    xname::String
end

The code above accepted by Julia. Why is it a valid code? Do the strings have any function?

2 Likes

Strings interspersed with functions and struct definitions (and values and some other things) are used to attach docstrings that are displayed when using the help prompt (accessed by beginning a line in the REPL with ?). But I’ll admit I had no idea it was possible to docstring a struct’s field.

julia> "docstring for Foo"
       struct Foo
           "docstring for Foo.x"
           x::Int
           "docstring for Foo.name"
           name::String
       end
Foo

help?> Foo
search: Foo floor fourthroot pointer_from_objref OverflowError RoundFromZero unsafe_copyto! functionloc

  docstring for Foo

help?> Foo.x
  docstring for Foo.x

help?> Foo.name
  docstring for Foo.name
2 Likes
julia> begin
       "why this"
        struct X
         "why that"
         y::Int
        end
       end
X

help?> X.y
  why that

help?> X
search: X xor exp Expr exp2 exit axes expm1 exp10 export extrema extrema! exponent Exception expanduser

  why this

julia> Meta.@lower begin
       "why this"
        struct X
         "why that"
         y::Int
        end
       end
...omitted the lowered struct code...
10 ┄ %37 = (Base.Docs.Binding)(Main, :X)
│    %38 = (Core.svec)("why this")
│    %39 = (Pair{Symbol, Any})(:y, "why that")
│    %40 = (Dict{Symbol, Any})(%39)
│    %41 = (Pair)(:fields, %40)
│    %42 = (Dict{Symbol, Any})(:path => "REPL[24]", :linenumber => 2, :module => Main, %41)
│    %43 = (Base.Docs.docstr)(%38, %42)
│    %44 = Core.apply_type(Union)
│    %45 = (Base.Docs.doc!)(Main, %37, %43, %44)
└───       return %45
))))

As implied, the lowered Base.Docs code vanishes if you don’t start with a string over the struct, even if there are strings over the fields:

julia> begin
        struct Y
         "why that"
         z::Int
        end
       end

help?> Y.z
  Y has fields z.

help?> Y
search: Y yield yieldto Sys Type hypot typeof Symbol TypeVar typemin typemax symlink symdiff typejoin symdiff! TypeError

  No documentation found.
...

Reasonably, ArgumentParser has an overall docstring:

"Command-line argument parser with key-value stores and attributes."
mutable struct ArgumentParser
2 Likes

Yes, my first guess was of course those are docstrings, but

help?> Foo.x
  Foo has fields x, and xname.

was not what I expected.

Now I see:

struct Foo
    "x value"
    x::Int
    "x name"
    xname::String
end

"this is Baz"
struct Baz
    "x value"
    x::Int
    "x name"
    xname::String
end
;

help?> Foo.x
  Foo has fields x, and xname.

help?> Baz.x
  x value

Is it actually documented somewhere in any form?

The Types section of Documentation · The Julia Language covers it, but it does not explain that you need a docstring for the overall struct to make field docstrings work, it just shows an example. Like mikmoore said, I also think this is a relatively obscure feature. If the fields are public, it’s beneficial to document them in the type’s docstring, and if the fields are internal, the public higher-level methods are documented instead.

I am curious how the upcoming public keyword will factor into this. REPL-accessible docstrings can be justified for internal details, and separating the docstrings of the fields and the overall struct do help with differentiating internal fields and the public struct type, but there’s an ambiguity there for public fields.

2 Likes