Julia 1.0 が出てから半年経って Julia に慣れてきた頃なのか、私のパッケージの作り方の記事が地味に見られているようなので、パッケージを作るときに知っておいて損はない Requires.jl について紹介します。
環境
- Julia 1.1.0
- Requires.jl v0.5.2
三種の神器
Julia のパッケージを作るときに役に立つパッケージはいくつかありますが、私にとっての三種の神器は Revise.jl, PkgTemplates.jl, Requires.jl です。
前半2つはパッケージ製作者目線でないと困るもので、Requires.jl はパッケージの利用者目線で使ってくれないと困るものです。
Requires.jl
Requires.jl はパッケージを効率よく読み込むためのパッケージです。
例えば次のようなパッケージを作っていたとします。
module SamplePackage using Plots f(x) = ... # Plots に依存しない関数 g(x) = ... # Plots に依存しない関数 function h(x) Plots.plot(...) end end # module
ここで利用者は関数 f
, g
はよく使うけれど、可視化のための関数 h
は滅多に使わないとしましょう。
ご存知の方も多いと思いますが Plots は Julia のパッケージの中でも重量級なので読み込みだけでも5秒近くかかります。仮にこのパッケージから Plots を抜いた場合の読み込み時間が 0.1秒だったとすると、利用者からするとろくに使いもしない関数のせいで読み込み速度が遅くされていると感じるでしょう。
Requires を使うと Plots を読み込む前は h
は利用できないけれど、読み込んだら h
が利用できるようになる、という風にすることができます。これにより無駄なパッケージの読み込みを抑えることができます。
使い方
公式の README を見ればわかると思いますが、オプション扱いにしたいパッケージを __init__()
に入れていきます。
function __init__() @require パッケージ名 = "パッケージのUUID" (左で指定したパッケージが読み込まれたら実行される部分) end
パッケージの UUID は General を直接見るか、]add
でパッケージを追加したあと Project.toml
を覗いてください。
書き方として重量級パッケージに依存する部分と依存しない部分をファイルで分けてしまうのが楽かなと個人的には思います。
例
# SamplePackage.jl module SamplePackage using Requires include("noplot.jl") function __init__() @require Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" include("plot.jl") end end # module
# noplot.jl export hello hello() = println("Hellow World!")
# plot.jl using .Plots export sinplot function sinplot() println("Plot sin fn.") plot(sin) end
ディレクトリ構成
SamplePackage/ ├── Manifest.toml ├── Project.toml └── src ├── noplot.jl ├── plot.jl └── SamplePackage.jl
実行例
julia> using SamplePackage julia> hello() Hellow World! julia> sinplot() ERROR: UndefVarError: sinplot not defined Stacktrace: [1] top-level scope at none:0 julia> using Plots julia> sinplot() # Plots を読み込むと使えるようになる。 Plot sin fn.
plot.jl
中の using .Plots
の .
を付け忘れないように!完全に Plots をオプション扱いにしてパッケージの依存関係に入れたくない場合、.
を忘れると以下のような警告が出ます。
julia> using Plots ┌ Warning: Package SamplePackage does not have Plots in its dependencies: │ - If you have SamplePackage checked out for development and have │ added Plots as a dependency but haven't updated your primary │ environment's manifest file, try `Pkg.resolve()`. │ - Otherwise you may need to report an issue with SamplePackage └ Loading Plots into SamplePackage from project dependency, future warnings for SamplePackage are suppressed.
読み込み時間の比較
上記のパッケージと以下のパッケージの読み込み時間を比較してみます。
module SamplePackage2 export hello, sinplot using Plots hello() = println("Hellow World!") function sinplot() println("Plot sin fn.") plot(sin) end end # module
読み込み時間
julia> @time using SamplePackage 0.336427 seconds (455.48 k allocations: 24.660 MiB) julia> @time using SamplePackage2 5.265099 seconds (6.52 M allocations: 366.485 MiB, 5.65% gc time)
SamplePackage
の方では Plots を読み込んでこないので読み込み時間を抑えることができました。
製作しているパッケージが計算が主でグラフに出すことはオプション機能とするならば、Requires.jl を使って Plots 依存の部分を分離すると利用者のストレスを減らすことができると思います。 他にも DifferentialEquations.jl などもヘビー級のパッケージですが、これらの重いパッケージに依存しない部分だけでも十分に利用価値があるならば、 Requires.jl を使って分離した方がユーザー思いのパッケージになると思います。