Документация Engee

Документация

Доступ к документации

Для доступа к документации в REPL или IJulia введите ? перед названием макроса или функции и нажмите клавишу Enter. Например,

?cos
?@time
?r""

отобразят документацию по соответствующей функции, макросу и строковому макросу. В большинстве сред Julia существует способ обратиться к документации напрямую.

  • VS Code отображает документацию при наведении указателя мыши на имя функции. Вы также можете осуществлять поиск по документации в боковой панели Julia.

  • В среде Pluto можно открыть панель Live Docs (Документация в Интернете) в правом нижнем углу.

  • В среде Juno сочетание клавиш Ctrl-J, Ctrl-D позволяет отобразить документацию для объекта, на котором находится курсор.

Написание документации

Встроенная система документации Julia позволяет пользователям и разработчикам пакетов легко документировать функции, типы и другие объекты.

Базовый синтаксис очень простой: любая строка, стоящая непосредственно перед объектом (функцией, макросом, типом или экземпляром), интерпретируется как документирующая этот объект (такие строки называются docstring). Обратите внимание, что между docstring и документируемым объектом не должно быть никаких пустых строк или комментариев. Простейший пример:

"Tell whether there are too foo items in the array."
foo(xs::Array) = ...

Документация интерпретируется в виде Markdown, поэтому вы можете использовать отступы и границы блоков кода для отделения примеров кода от основного текста. Строго говоря, любой объект может быть ассоциирован с любым другим объектом в качестве его метаданных. Markdown просто используется по умолчанию, но можно создавать и другие строки макросов и передавать их макросу @doc.

Поддержка Markdown реализована через стандартную библиотеку Markdown. Полный перечень поддерживаемого синтаксиса см. в документации.

Вот более сложный пример по-прежнему на основе Markdown:

"""
    bar(x[, y])

Compute the Bar index between `x` and `y`.

If `y` is unspecified, compute the Bar index between all pairs of columns of `x`.

# Примеры
```julia-repl
julia> bar([1, 2], [1, 2])
1
```
"""
function bar(x, y) ...

В примере выше продемонстрирован ряд простых стандартов, которым рекомендуется следовать при написании документации.

  1. Вверху документации всегда указывайте сигнатуру функции, отбивая ее четырьмя пробелами, чтобы она выводилась в виде Julia-кода.

    Она может быть указана идентично сигнатуре в коде Julia (например, mean(x::AbstractArray)) или в упрощенном виде. По возможности необязательные аргументы должны быть представлены со значениями по умолчанию (т. е. f(x, y=1)) согласно фактическому синтаксису Julia. Необязательные аргументы, у которых нет значения по умолчанию, следует помещать в квадратные скобки (т. е. f(x[, y]) и f(x[, y[, z]])). Другим вариантом является использование нескольких строк: одной без необязательных аргументов, а других — с их указанием. Такое решение также можно использовать для документирования нескольких связанных методов определенной функции. Если функция принимает множество именованных аргументов, достаточно включить в сигнатуру замещающий текст <keyword arguments> (т. е. f(x; <keyword arguments>)), а затем привести полный список аргументов в разделе # Arguments (см. пункт 4 ниже).

  2. После блока упрощенной сигнатуры опишите одной строкой назначение функции или объекта. При необходимости введите второй абзац с дополнительными сведениями, отделив его пустой строкой.

    Однострочное описание функции в документации должно представлять собой одно предложение в форме третьего лица единственного числа без подлежащего («Выполняет…​», «Возвращает…​» и т. п.). В конце описания ставится точка. Если кратко описать назначение функции сложно, может иметь смысл разделить его на несколько составных частей (однако это не является обязательным для каждого случая).

  3. Не повторяйтесь.

    Поскольку сигнатура уже включает имя функции, незачем начинать документацию со слов «Функция bar…​», лучше переходите сразу к сути. Аналогичным образом, если в сигнатуре уже указаны типы аргументов, упоминать эти типы в описании будет избыточным.

  4. Приводите перечень аргументов, только когда это действительно необходимо.

    Если функция простая, обычно достаточно указать роль аргументов прямо в описании ее назначения. Список аргументов будет лишь повторять сведения, уже приведенные в другом месте. Однако для сложных функций с множеством аргументов (особенно именованных аргументов) привести их перечень будет полезно. В этом случае вставьте маркированный список аргументов (со знаком маркера -) под заголовком # Arguments (Аргументы) после общего описания функции. В списке должны быть указаны типы аргументов и их значения по умолчанию (при наличии).

    """
    ...
    # Аргументы
    - `n::Integer`: the number of elements to compute.
    - `dim::Integer=1`: the dimensions along which to perform the computation.
    ...
    """
  5. Приведите ссылки на связанные функции.

    Иногда могут иметься функции с близким функционалом. Чтобы их было проще найти, приведите краткий список таких функций в абзаце See also (См. также).

    See also [`bar!`](@ref), [`baz`](@ref), [`baaz`](@ref).
  6. Добавьте любые примеры кода в раздел # Examples.

    Примеры кода по возможности следует приводить в блоке doctest. Блок кода doctest отделяется границей (см. Блоки кода), начинается с ```jldoctest и содержит произвольное число команд julia> с входными и ожидаемыми выходными данными, имитирующих REPL Julia.

    Для блоков doctest используется Documenter.jl. Более подробную документацию см. в руководстве Documenter.

    Например, в следующих строках docstring показано определение переменной a и ожидаемый результат, выводимый в REPL Julia.

    """
    Some nice documentation here.
    
    # Примеры
    ```jldoctest
    julia> a = [1 2; 3 4]
    2×2 Array{Int64,2}:
      1  2
      3  4
    ```
    """

    В блоках doctest не рекомендуется вызывать rand и другие функции, связанные с генерацией случайных чисел, так как их вывод в разных сеансах Julia будет различаться. Если вам нужно продемонстрировать какой-то функционал, связанный с генерацией случайных чисел, то, как один из вариантов, вы можете явно создать собственный RNG-объект (см. Random), сгенерировать его значения и передать его указанным в doctest функциям.

    Воспроизводимость некоторых doctest также зависит от размера системного слова в ОС (Int32 или Int64) и от используемых разделителей пути (/ или \).

    В doctest имеют значение пробелы! Например, возможен сбой doctest из-за некорректного выравнивания структурной распечатки массива.

    Затем вы можете использовать make -C doc doctest=true, чтобы выполнить все блоки doctest в Руководстве Julia и документации по API-интерфейсам и убедиться, что ваш пример работает.

    Если необходимо показать усечение выводимых результатов, можно ввести [...] на строке, где проверка должна прекратиться. Это позволяет скрыть трассировку стека (содержащую непостоянные ссылки на строки кода Julia), когда doctest показывает выдачу исключения, например:

    ```jldoctest
    julia> div(1, 0)
    ERROR: DivideError: integer division error
    [...]
    ```

    Примеры, не подлежащие тестированию, следует записывать в виде отделенных границей блоков кода, начинающихся с ```julia, чтобы они корректно выделялись в создаваемой документации.

    По возможности примеры должны быть изолированными и рабочими, чтобы читатель мог опробовать их, не включая в них никаких зависимых элементов.

  7. Используйте знаки обратного штриха для идентификации кода и выражений.

    Чтобы использовать выделение, всегда помещайте идентификаторы и фрагменты кода Julia между знаками обратного штриха (`). Уравнения в синтаксисе LaTeX можно отделять двойным обратным штрихом (``).

Для этого нужно использовать символы Юникода, а не escape-последовательность LaTeX, т. е. ``α = 1`` , а не ``\\alpha = 1``.

  1. Начальные и конечные символы """ должны размещаться на отдельной строке. То есть необходимо вводить:

    """
    ...
    
    ...
    """
    f(x, y) = ...

    Не следует использовать:

    """...
    
    ..."""
    f(x, y) = ...

    Так будет понятнее, где начинаются и заканчиваются строки docstring.

  2. Учитывайте ограничения длины строк в соседствующем коде.

    Строки docstring редактируются теми же средствами, что и код, поэтому для них должны соблюдаться те же стандарты. Рекомендуется, чтобы ширина строк не превышала 92 символа.

  3. В разделе # Implementation (Реализация) предоставьте сведения, необходимые для реализации функции в пользовательских типах. Эти данные предназначены не для пользователей, а для разработчиков, т. к. они поясняют, например, какие функции должны переопределяться, а какие автоматически используют подходящие резервные варианты. Такие сведения лучше всего отделять от основного описания работы функции.

  4. При большом объеме docstring рекомендуется разделять документацию с помощью заголовка # Extended help (Расширенная справка). В обычном режиме справки отображается только материал над этим заголовком. Для доступа к полным справочным данным добавьте еще один знак «?» в начале выражения (т. е. вместо «?foo» введите «??foo»).

Функции и методы

Функции в Julia могут иметь множество реализаций, которые называются методами. Хотя, как правило, рекомендуется использовать универсальные функции, имеющие строго одно назначение, Julia позволяет отдельно документировать различные методы, если это необходимо. Обычно следует документировать только наиболее универсальные методы или даже непосредственно саму функцию (т. е. объект, создаваемый без каких-либо методов с помощью function bar end). Документировать конкретные методы следует, только если их поведение отличается от более универсальных. В любом случае в документации не должны повторяться сведения, уже доступные где-то еще. Пример:

"""
    *(x, y, z...)

Multiplication operator. `x * y * z *...` calls this function with multiple
arguments, i.e. `*(x, y, z...)`.
"""
function *(x, y, z...)
    # ... [реализация приведена в другом месте] ...
end

"""
    *(x::AbstractString, y::AbstractString, z::AbstractString...)

When applied to strings, concatenates them.
"""
function *(x::AbstractString, y::AbstractString, z::AbstractString...)
    # ... [использует здесь особый код] ...
end

help?> *
search: * .*

  *(x, y, z...)

  Multiplication operator. x * y * z *... calls this function with multiple
  arguments, i.e. *(x,y,z...).

  *(x::AbstractString, y::AbstractString, z::AbstractString...)

  When applied to strings, concatenates them.

При получении документации для универсальной функции выполняется конкатенация метаданных для каждого метода с помощью функции catdoc, которую, конечно же, можно переопределять для пользовательских типов.

Расширенное использование

Макрос @doc связывает содержимое своего первого аргумента со вторым аргументом и помещает его в словарь META для конкретных модулей.

Для упрощения написания документации анализатор обрабатывает макрос @doc особым образом: если в вызове @doc приведен один аргумент, но за ним после одного разрыва строки следует другое выражение, то это выражение считается вторым аргументом макроса. Таким образом, следующий синтаксис будет проанализирован как вызов @doc с двумя аргументами:

@doc raw"""
...
"""
f(x) = x

Это позволяет использовать в качестве docstring не только обычные строковые литералы, но и другие выражения (например, строку макроса raw"").

При использовании макроса @doc (равно как и функции doc) для получения документации происходят поиск метаданных, относящихся к данному объекту, по всем словарям META и возврат соответствующих метаданных. Возвращаемый объект (например, какое-либо содержимое Markdown) по умолчанию отображается в отформатированном виде. Подобный принцип работы также упрощает взаимодействие с системой документации через код. Вот как можно повторно использовать одну и ту же документацию для разных версий функции:

@doc "..." foo!
@doc (@doc foo!) foo

Пример использования с функциями метапрограммирования Julia:

for (f, op) in ((:add, :+), (:subtract, :-), (:multiply, :*), (:divide, :/))
    @eval begin
        $f(a,b) = $op(a,b)
    end
end
@doc "`add(a,b)` adds `a` and `b` together" add
@doc "`subtract(a,b)` subtracts `b` from `a`" subtract

При записи документации не в блоках верхнего уровня, например внутри begin, if, for или let, она добавляется в систему по мере обработки этих блоков. Пример:

if condition()
    "..."
    f(x) = x
end

Этот код добавляет документацию для f(x), когда условие condition() выполняется (true). При этом, даже если функция f(x) выходит за границы области после блока, ее документация сохраняется.

Для упрощения создания документации можно использовать возможности метапрограммирования. При использовании интерполяции строк внутри docstring необходим дополнительный символ $, как показано для $($name):

for func in (:day, :dayofmonth)
    name = string(func)
    @eval begin
        @doc """
            $($name)(dt::TimeType) -> Int64

        The day of month of a `Date` or `DateTime` as an `Int64`.
        """ $func(dt::Dates.TimeType)
    end
end

Динамическая документация

Иногда для определенных экземпляров типа необходима документация не по самому типу, а отдельная, зависящая от значений полей экземпляра. В таких случаях вы можете добавить метод к Docs.getdoc для своего пользовательского типа, который возвращает документацию в зависимости от экземпляра. Например:

struct MyType
    value::Int
end

Docs.getdoc(t::MyType) = "Documentation for MyType with value $(t.value)"

x = MyType(1)
y = MyType(2)

Команда ?x отобразит «Documentation for MyType with value 1», а ?y — «Documentation for MyType with value 2».

Руководство по синтаксису

В этом руководстве приведен подробный обзор того, как добавлять документацию ко всем синтаксическим конструкциям Julia, для которых это возможно.

В следующих примерах "..." соответствует произвольным строкам docstring.

Символы $ и \

Символы $ и \ будут по-прежнему проанализированы как интерполяция строк или начало escape-последовательности, в том числе в строках docstring. Чтобы не использовать для них escape-коды, можно использовать строку макроса raw"" вместе с макросом @doc. Это удобно, когда строки docstring содержат LaTeX или примеры исходного кода Julia с интерполяцией:

@doc raw"""
```math
\LaTeX
```
"""
function f end

Функции и методы

"..."
function f end

"..."
f

Добавляет строку docstring "..." для функции f. Предпочтительной является первая версия синтаксиса, однако обе являются эквивалентными.

"..."
f(x) = x

"..."
function f(x)
    x
end

"..."
f(x)

Добавляет строку docstring "..." для метода f(::Any).

"..."
f(x, y = 1) = x + y

Добавляет строку docstring "..." для двух методов: f(::Any) и f(::Any, ::Any).

Макросы

"..."
macro m(x) end

Добавляет строку docstring "..." для определения макроса @m(::Any).

"..."
:(@m)

Добавляет строку docstring "..." для макроса с именем @m.

Типы

"..."
abstract type T1 end

"..."
mutable struct T2
    ...
end

"..."
struct T3
    ...
end

Добавляет строку docstring "..." для типов T1, T2 и T3.

"..."
struct T
    "x"
    x
    "y"
    y
end

Добавляет строку docstring "..." для типа T, "x" для поля T.x и "y" для поля T.y. Также применимо к типам mutable struct.

Модули

"..."
module M end

module M

"..."
M

end

Добавляет строку docstring "..." для модуля M. Синтаксис с добавлением docstring над Module является предпочтительным, однако оба варианта эквивалентны.

"..."
baremodule M
# ...
end

baremodule M

import Base: @doc

"..."
f(x) = x

end

При документировании baremodule путем размещения docstring над выражением в модуль автоматически импортируется макрос @doc. Если выражение модуля не документируется, такой импорт необходимо осуществлять вручную.

Глобальные переменные

"..."
const a = 1

"..."
b = 2

"..."
global c = 3

Добавляет строку docstring "..." для привязок (Binding) a, b и c.

Привязки (Binding) позволяют сохранить ссылку на определенный символ (Symbol) в модуле (Module), не сохраняя непосредственное значение по ссылке.

Когда определение const только задает псевдоним другого определения, например как в случае с функцией div и ее псевдонимом ÷ в Base, документировать нужно не псевдоним, а саму функцию.

Если задокументирован псевдоним, а не фактическое определение, то при поиске фактического определения система документации (режим ?) не будет возвращать строки docstring, связанные с псевдонимом.

Например, следует писать:

"..."
f(x) = x + 1
const alias = f

Не следует использовать:

f(x) = x + 1
"..."
const alias = f
"..."
sym

Добавляет строку docstring "..." для значения, связанного с sym. Однако рекомендуется документировать объект sym там, где находится его определение.

Множество объектов

"..."
a, b

Добавляет строку docstring "..." для элементов a и b. Оба должны быть выражениями, для которых возможна документация. Этот синтаксис эквивалентен следующему.

"..."
a

"..."
b

Таким способом можно документировать любое число выражений. Данный синтаксис удобен при наличии двух связанных функций, например неизменяемой и изменяемой версий f и f!.

Сформированный макросами код

"..."
@m expression

Добавляет строку docstring "..." для выражения, формируемого при раскрытии @m expression. Это позволяет документировать выражения, перед которыми стоят @inline, @noinline, @generated или любые другие макросы, так же, как выражения без макросов.

При написании макросов следует учитывать, что только макросы, формирующие одно выражение, автоматически поддерживают docstring. Если макрос возвращает блок, содержащий несколько вложенных выражений, то для документирования вложенного выражения нужно отметить его макросом @__doc__.

Макрос @enum использует @__doc__ для возможности документирования Enum. Рассмотрите его определение как пример использования @__doc__ должным образом.

# Core.@__doc__Macro

@__doc__(ex)

Низкоуровневый макрос, используемый, чтобы помечать выражения, возвращаемые макросом, которые должны быть задокументированы. Если помечено более одного выражения, то к каждому выражению применяется одна и та же строка docstring.

macro example(f)
    quote
        $(f)() = 0
        @__doc__ $(f)(x) = 1
        $(f)(x, y) = 2
    end |> esc
end

@__doc__ не действует, если макрос, использующий его, не задокументирован.