Why doesn’t the Julia compiler automatically insert the function’s signature at the beginning of each docstring? It boggles my mind that the Julia documentation suggests:
Always show the signature of a function at the top of the documentation, with a four-space indent so that it is printed as Julia code.
Why is the developer supposed to do this?? The compiler is the perfect tool to automate it:
- It knows everything about the function/method being documented: all the types, all the arguments, all the default values - the entire function/method signature.
- It’ in the perfect position to correctly format the function signature and insert it into the docstring because it’s responsible for attaching docstrings to functions anyway.
Python does it right:
>>> def func(a: int, b: float) -> str:
... "Do important calculation"
... return a - b
...
>>> help(func)
Help on function func in module __main__:
func(a: int, b: float) -> str
Do important calculation
As expected, the docstring begins with the function signature, obviously, because I need to know how to call the function properly!
Now look at Julia:
julia> function func(a::Int, b::Float64)::String
a - b
end
func (generic function with 1 method)
help?> func
No documentation found.
func is a Function.
# 1 method for generic function "func" from Main:
[1] func(a::Int64, b::Float64)
@ REPL[4]:1
Huh, so the compiler already knows how to insert function signatures into docstrings! However, it didn’t include the return type (why??), even though I explicitly specified it in my code.
Now add a docstring:
julia> "Unhelpful docstring" function foo() 5 end
foo
help?> foo
search: foo floor fourthroot pointer_from_objref OverflowError RoundFromZero
Unhelpful docstring
Yep, the docstring is unhelpful, and Julia chose to remove the automatically generated function signature from the output, which doesn’t help at all.
Now there’s the great DocStringExtensions.jl
package which inserts function signatures and type signatures into your docstrings:
julia> using DocStringExtensions
julia> "$TYPEDSIGNATURES" function bar(a::Int, b=4; c::Bool=true) 5 end
bar
help?> bar
search: bar baremodule SubArray GlobalRef clipboard BitArray backtrace BitMatrix
bar(a::Int64; ...) -> Int64
bar(a::Int64, b; c) -> Int64
This is much better, but:
-> Int64
is not how Julia specifies return types. Why is the syntax in the docs different from the syntax of the programming language the documentation describes?
- The second signature doesn’t show the arguments’ default values. How can I find these default values?
- It also doesn’t show types of keyword arguments, even though my code said
c::Bool=true
.
I’d prefer these signatures instead:
bar(a::Int64; ...)::Int64
bar(a::Int64, b=4; c::Bool=true)::Int64
Basically, just copy the signature from my code.
As an example of good documentation formatting, I think the Wolfram language does it well. Take a look at this: Plot: Visualize or graph a function—Wolfram Documentation - it’s beautiful, it shows “function signatures” and examples, it tries to link to other relevant documentation pages and each page is structured in the same way:
- Initial blue box which shows “function signatures” and very short descriptions of what each signature does.
- “Details and Options” is exactly what it says it is.
- “Examples” provides concise usage examples
- “Basic examples” is one of the first things you see on each documentation page. It gets straight to the point and shows what the function does.
- “Scope” goes more in-depth
- “Options” discusses the function’s options and provides examples.
- “Applications” shows what you can do using this function, what kind of problems you can solve with it.
- “Properties & Relations” links this function to other functions: “look, this function is actually a special case of this one! If you wanna do this, use another function!” This is Wolfram’s answer to point 3 in the OP.
Compare this to ?CairoMakie.lines
:
help?> CairoMakie.lines
lines(positions)
lines(x, y)
lines(x, y, z)
Creates a connected line plot for each element in (x, y, z), (x, y) or
positions.
NaN values are displayed as gaps in the line.
Attributes
==========
...
Now, what kind of things can I pass as positions
? A vector of… something? A tuple of vectors? Some kind of custom struct? What can x
and y
be? Probably vectors, but what else? Can they actually be vectors? What’s z
? The docstring doesn’t really explain how to use this function.