ドキュメント

Julia には組み込みのドキュメントシステムがあり、パッケージ開発者とユーザーは関数や型といったオブジェクトにドキュメントを追加できます。このシステムは Julia 0.4 で追加されました。

基本的な構文は簡単です: トップレベルにおいてオブジェクト (関数・マクロ・型・インスタンス) の直前にある任意の文字列がそのオブジェクトのドキュメントと解釈されます (このドキュメントを 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 missing, compute
the Bar index between all pairs of columns of `x`.

# Examples
```julia-repl
julia> bar([1, 2], [1, 2])
1
```
"""
function bar(x, y) ...

ドキュメントを書くときは次の簡単な慣習に従うことを推奨します。上の例もこの慣習に従っています。

  1. ドキュメントの最初に関数のシグネチャを示す。このシグネチャは四つのスペースでインデントして、Julia コードとして認識されるようにする。

    これは mean(x::AbstractArray) のような Julia コードにあるシグネチャまたはそれを簡単にしたものです。省略可能引数は可能ならデフォルトの値と共に f(x, y=1) のように示してください (実際の Julia の構文と同様です)。デフォルト値を持たない省略可能引数は角括弧を使って f(x[, y])f(x[, y[, z]]) と表記します。こうする代わりに複数行に分けて書いても構いません: 省略可能引数が付いたシグネチャと付いていない (いくつかの) シグネチャを一行ずつ示すということです。この方法は一つの関数が持つ複数の関連するメソッドをドキュメントするときにも利用できます。関数がたくさんのキーワード引数を受け取るときは <keyword arguments> というプレースホルダーを f(x; <keyword arguments>) などとシグネチャに書いておいて、後で # Arguments という節に完全なリストを示してください (以下の 4. も参照してください)。

  2. 関数が行うこともしくはオブジェクトが表現することを説明する一行の説明を、単純化したシグネチャのブロックの後に含める。もし必要なら、詳しい説明を改行を挟んだ二つ目の段落で行う。

    関数をドキュメントするときの一行の説明は命令形 ("Do this" や "Return that" など) にすべきで、そのとき三人称現在単数を表す s を付けるべきではありません ("Returns the length ..." と書かないでください)。最後はピリオドで終えてください。関数の意味を簡単にまとめられないなら、小さく分割するべきかもしれません (これはどんなときも絶対に従うべき要件ではありません)。

  3. 同じことを繰り返さない (do not repeat yourself)。

    関数の名前はシグネチャで与えられるので、ドキュメントの文章を "The function bar..." などと始める必要はありません。要点を直接述べてください。同様にシグネチャに引数の型が付いているなら、説明でそれに触れるのは冗長です。

  4. 引数リストは本当に必要なときにだけ提供する。

    簡単な関数ではたいてい、引数の役割は関数が行う処理の説明で直接示した方が明快です。引数リストを示しても、他の場所で提供された情報を繰り返すだけでしょう。しかし大量の引数 (特にキーワード引数) を持つ複雑な関数では、引数リストを提供するのは悪くないアイデアです。その場合には、関数の概要を説明した後に # Arguments ヘッダーを入れ、そこに一つの引数に一つの要素を対応させた箇条書きで引数リストを示してください。リストには引数の型と (もしあれば) デフォルト値を含めるべきです:

    """
    ...
    # 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> というプロンプトに続く入力と期待される出力が Julia REPL に似たフォーマットで並びます。

    情報

    doctest は Documenter.jl を使って実行できます。詳しくは Documenter.jl のマニュアルを参照してください。

    例えば次の docstring には変数 a を定義する入力と、それに対して Julia REPL から期待される出力が書かれています:

    """
    Some nice documentation here.
    
    # Examples
    ```jldoctest
    julia> a = [1 2; 3 4]
    2×2 Array{Int64,2}:
     1  2
     3  4
    ```
    """
    
    注意

    doctest では rand などの乱数発生器が絡む関数の呼び出しを避けるべきです。異なる Julia セッションで一貫した出力が得られないためです。もし乱数の生成に関連する機能を doctest で使いたい場合には、独自の MersenneTwister (あるいは他の疑似乱数生成器) を定義してテストする関数に渡すというのが一つの手です。

    オペレーティングシステムのワードサイズ (Int32 または Int64) やパス分離文字 ('/' または '\') も doctest の再現可能性に影響します。

    doctest では空白が重要なことに注意してください! 例えば整形された配列の出力をずらすと、doctest は失敗します。

    Julia マニュアルと API ドキュメントに含まれる doctest は make -C doc doctest=true で全て実行できます。これによりサンプルコードが全て動作することを確認できます。

    期待される出力結果の最後に [...] を付けると、Julia は出力の比較をそこでやめます。例外が発生する doctest でスタックトレースを無視させるときに有用です (スタックトレースは Julia コードの行番号など変更される可能性のある情報を含みます)。例を示します:

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

    テストできないサンプルコードは ```julia で始まるフェンスブロックに書いて、生成されるドキュメントで正しくハイライトされるようにしてください。

    Tip

    可能なときは必ずサンプルコードを自己充足的かつ実行可能にして、読者が他の部分を参照することなくコードを試せるようにしてください。

  7. コードと数式はバックティックで表す。

    Julia の識別子やコードの一部は必ずバックティック ` で囲み、ハイライトを有効化してください。同様に LaTeX 構文で書かれた数式は二つのバックティック `` で囲みます。LaTeX 構文ではエスケープシーケンスより Unicode 文字を優先させてください: 例えば ``\\alpha = 1`` ではなく ``α = 1`` とするべきです。

  8. 最初と最後の行には """ だけを書く。

    つまり、次のように書いてください:

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

    次のようには書かないでください:

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

    この規則に従うと、docstring の始めと終わりが分かりやすくなります。

  9. 周りのコードの行制限に合わせる。

    docstring はコードと同じツールで編集されるので、同じ慣習が適用されるべきです。一行を 92 文字以内とするのが推奨されます。

  10. 独自型が関数を実装するときに必要な情報を # Implementation 節に提供する。

    この実装詳細はユーザーではなく開発者に向けたものであり、オーバーライドすべき関数や適切なフォールバックが自動的に用意される関数などを説明します。こういった説明は関数の振る舞いの説明と分けた方がよいでしょう。

  11. 長い docstring では、ドキュメントを # Extended help ヘッダーで分ける。

    こうすると通常の help モードではこのヘッダーより上の内容が表示され、式の前に ? を付けると完全な help が表示されようになります (? foo ではなく ?? foo とするということです)。

ドキュメントへのアクセス

REPL または IJulia でドキュメントにアクセスするには、? の後に関数やマクロの名前を入力し、エンターを押します。例えば

?cos
?@time
?r""

とすると、それぞれ関数・マクロ・文字列マクロのドキュメントが表示されます。Juno では Ctrl-J, Ctrl-D を使うとカーソルが指すオブジェクトのドキュメントが表示されます。

関数とメソッド

Julia の関数は複数の実装を持つことができ、それぞれの実装をメソッド (method) と呼びます。総称関数は一つの用途を持つのが良い習慣ですが、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...)
    # ... [implementation sold separately] ...
end

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

When applied to strings, concatenates them.
"""
function *(x::AbstractString, y::AbstractString, z::AbstractString...)
    # ... [insert secret sauce here] ...
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 関数を使って連結されます。もちろん catdoc 関数は独自型に対してオーバーライドできます。

高度な使い方

@doc マクロは第一引数と第二引数の組を META という名前のモジュールごとに存在する辞書に登録します。

ドキュメントの作成を簡単にするために、パーサーは @doc という名前のマクロを特別扱いします: もし @doc の呼び出しが引数を一つだけ持ち、このマクロの直後に改行と何らかの式が続くなら、パーサーはその式をマクロの第二引数に設定します。例えば次の構文では、二つの引数を持った @doc の呼び出しとしてパースされます:

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

この機能を使えば、通常の文字列リテラルでない式 (例えば raw"") を docstring に利用できます。

ドキュメントを取得するとき、@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

と書くと、condition()true になったときにだけ f(x) へドキュメントが追加されます。ブロックを抜けて 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::String
end

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

x = MyType("x")
y = MyType("y")

とすると、?x は "Documentation for MyType with value x" となり、?y は "Documentation for MyType with value y" となります。

構文ガイド

ドキュメントを付けられる Julia の構文全てに対するドキュメントの付け方をここに示します。

以下の例では "..." が docstring を表すとします。

文字列の補完とエスケープ

文字 $\ は docstring でもそれぞれ文字列補間とエスケープシーケンスの開始文字としてパースされます。ただし文字列マクロ raw"" を使えばエスケープが必要なくなります。文字列補間を含む Julia のソースコードや LaTeX ソースを入れるときに便利です:

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

関数とメソッド

"..."
function f end

"..."
f

関数 f"..." という docstring を追加します。一つ目の書き方が推奨されますが、二つ目の書き方でも意味は同じです。

"..."
f(x) = x

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

"..."
f(x)

メソッド f(::Any)"..." という docstring を追加します。

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

二つのメソッド f(::Any)f(::Any, ::Any)"..." という docstring を追加します。

マクロ

"..."
macro m(x) end

マクロの定義 @m(::Any)"..." という docstring を追加します。

"..."
:(@m)

@m という名前のマクロに "..." という docstring を追加します。

"..."
abstract type T1 end

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

"..."
struct T3
    ...
end

T1, T2, T3"..." という docstring を追加します。

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

docstring として型 T"..." を、フィールド T.x"x" を、フィールド T.y"y" をそれぞれ追加します。mutable struct 型でも同様です。

モジュール

"..."
module M end

module M

"..."
M

end

モジュール M"..." という docstring を追加します。Module の上に docstring を加える一つ目の書き方が推奨されますが、二つの書き方は等価です。

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

baremodule M

import Base: @doc

"..."
f(x) = x

end

baremodule の上に docstring を書くと、そのモジュールで @doc が自動的にインポートされます。baremodule 式がドキュメントされていないときはモジュール内部で明示的なインポートが必要です。空の baremodule はドキュメントできません1

グローバル変数

"..."
const a = 1

"..."
b = 2

"..."
global c = 3

"..." という docstring を束縛 a, b, c に追加します。

束縛はモジュールに含まれるシンボルへの参照を保持するだけであり、参照される値は保持しません。

情報

const による定数の定義が他の定義に対する別名を与えるためだけに使われるなら、その別名はドキュメントせずに実際に処理を行う関数の方をドキュメントしてください。例えば Base÷div の別名となっています。

ドキュメントされていないオブジェクトの別名にドキュメントを付けたとしても、ドキュメントシステム (? モード) は別名に対する docstring を元のオブジェクトに対する docstring として返すことはありません。

例えば、次の書き方が推奨されます:

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

次の書き方は推奨されません:

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

"..." という docstring を sym に関連付いた値に追加します。ただし、sym が定義される場所でドキュメントを行うことが推奨されます。

複数オブジェクト

"..."
a, b

"..." という docstring を ab に追加します。ab は両方ともドキュメント可能な式である必要があります。この構文は次と等価です:

"..."
a

"..."
b

同様の方法で好きなだけ多くの式を同時にドキュメントできます。関数の非改変バージョン f と改変バージョン f! など、似た機能を持つ複数の関数をまとめてドキュメントするのにこの構文は適しています。

マクロによって生成されたコード

"..."
@m expression

"..." という docstring を @m expression を展開して得られる式に追加します。この構文を使えば @inline, @noinline, @generated をはじめとした任意のマクロで装飾された式を、装飾の付かない式と同じようにドキュメントできます。

マクロを書くときは、docstring を自動的にサポートするのは単一の式を生成するマクロだけであることに注意してください。マクロが複数の部分式を含むブロックを返す場合には、ドキュメントされるべき部分式に @__doc__ マクロで印を付ける必要があります。

例えば @enum マクロは @__doc__ を使って Enum のドキュメントを可能にしています。@enum の定義を見れば @__doc__ の正しい使い方の見当が付くでしょう。

Core.@__doc__ ── マクロ

@__doc__(ex)

マクロが返す式に含まれるドキュメントされるべき式に印を付けるための低水準マクロです。複数の式にこの印を付けると、印の付いた全ての式に同じ docstring が適用されます。

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

このマクロを呼び出す式がドキュメントされていないとき、@__doc__ は何もしません。


  1. 訳注: こう書いてあるが、"..." baremodule M end とすることはできる (参照: #13067, #13109)。[return]

広告