Juliaのモジュール
いつも自分でよくわからなくなるので、マニュアルをまとめてみる。
モジュールは、Juliaのコードの単位で、階層化可能な名前空間である。
module モジュール名
中身
end
-
一般にモジュールの中身はインデントしない。全部インデントすることになってしまうから。
-
モジュール名とファイル名には一般に対応関係は無い。1つのファイルに複数のモジュールを
書くこともあるし、下のようにして1つのモジュールを複数に分けることもある。
module SomeModule
include("file1.jl")
include("file2.jl")
end
- モジュール名は型の名前と同じで、
UpperCamelCase
のように、大文字で始めるキャメルケースで書き、
型の名前との競合を防ぐため、可能なら複数形にする。
module FastThings
struct FastThing
...
end
end
修飾名
モジュールの名前空間はネストし階層化することができる。
module A
y = 0
module B
x = 0
end
end
ある名前の親モジュールを参照するにはparentmodule
関数を用いる。
julia> parentmodule(UnitRange)
Base
- モジュールの外から名前にアクセスするには、
Base.UnitRange
のようにモジュール名を前につなげる。デリミタは.
。 - シンボルのみの名前の場合はパーザの都合で
:
も必要。Base.:+
のように書く。また==
などの一部の演算子はBase.:(==)
のようにカッコも必要。
export
モジュールが公開する名前はexport
リストに書く。exportは通常モジュールの冒頭に書くがそうしなければならないわけではない。また、このリストを複数書くこともできる。ここにモジュールの提供する外部インターフェイスを記述することが期待されている。
ただし、エクスポートされていない名前でも修飾名を使えばいつでもアクセスできるので、モジュールに隠蔽の機能はない。このあたり実にJuliaっぽい。export
リストにある名前は、後述のusing
を用いた際に名前空間に取り込まれるというだけで、import
だけ使っている場合にはあまり意味がない。
module A
export x, y
x = 0
y = 1
z = 2
end
このように書くと、モジュールA
のx
とy
は公開されているがzは公開されていない状態になる。
julia> using .A
julia> x
0
julia> y
1
julia> z
ERROR: UndefVarError: z not defined
x
とy
はアクセスできるようになったが、z
は出来ていない。ただし修飾名を使えばz
にもアクセスできる。
julia> A.z
2
ここでusing .A
は、同じレベルにあるモジュールA
を利用するという宣言。using A
で良さそうな気がするがだめ。モジュールA
は、implicitなモジュールMain
のサブモジュールMain.A
として定義されている。using
でなにもつけないと絶対パス指定になるので、A
だけでは指定できないということらしい。using Main.A
ならOK。
モジュールとパッケージ
Juliaにはモジュールと独立にパッケージがある。パッケージはひとかたまりになったライブラリで、特定の構造を持つディレクトリに存在し、トップレベルにパッケージと同名のモジュールを持たなければならない。また、Project.toml
というファイルがなければならない。
パッケージを作るには、Julia REPLのパッケージモードのcreate
コマンドを用いると簡単。REPLから]
をタイプするとパッケージモードになる。
pkg> generate HelloWorld
こうすると、HelloWorld
というディレクトリの中にProject.toml
とsrc/HelloWorld.jl
が作られる。REPLから;
をタイプするとシェルモードに入るので、そこで確認してみよう。.jl
ファイルにはテンプレートがすでにできている。
shell> tree HelloWorld
HelloWorld
├── Project.toml
└── src
└── HelloWorld.jl
1 directory, 2 files
shell> cat HelloWorld/src/HelloWorld.jl
module HelloWorld
greet() = print("Hello World!")
end # module
shell>
ここでは、ディレクトリ名がパッケージ名と同じになっているが、これは必須ではない。任意のディレクトリ名が使用できる。
これでパッケージが一応できたわけだが、これだけでは使用できない。
julia> import HelloWorld
ERROR: ArgumentError: Package HelloWorld not found in current path:
Juliaにこのパスにこのようなライブラリがあることを知らせる必要がある。それには、パッケージモードでactivate
コマンドを用いる。
(@v1.7) pkg> activate HelloWorld
Activating project at `~/juliatest/HelloWorld`
これでimportできるようになった。
julia> import HelloWorld
[ Info: Precompiling HelloWorld [9c493a92-d783-4ef9-b670-e336ee782ab6]
julia> HelloWorld.greet()
Hello World!
importとusing
Juliaにはモジュールやパッケージを使用可能にする方法として、import
とusing
の2つが用意されている。
import
はパッケージを読み込むために用いる。同じソースコード上のモジュールはすでに読み込まれているので、改めてimport
することにはあまり意味はない。
using
はimport
の機能に加えて、そのモジュールでexport
されている名前を、現在の名前空間に読み込む機能を持つ。
julia> module A
export x
x = 1
end
Main.A
julia> x
ERROR: UndefVarError: x not defined
julia> import .A
julia> x
ERROR: UndefVarError: x not defined
julia> using .A
julia> x
1
名前を指定した読み込み
明示的に読み込む名前を指定することもできる。exportされていなくても読み込める。これはimportでもusingでも同じようにできる。
julia> module A
x = 1
end
Main.A
julia> import .A: x
julia> x
1
この書き方だと、なぜかモジュール名自体は名前空間に取り込まれない。
julia> module A
module B
x = 1
end
end
Main.A
julia> using .A.B: x
julia> B
ERROR: UndefVarError: B not defined
こういう場合は using .A.B: x, B
のように書けばいいらしい。
import でも usingでも全く効果が同じなわけではなく、importでないと駄目なケースがある。モジュール内の関数にメソッドを追加するには、importでないといけない。
julia> module A
function x(i::Int64) x end
end
Main.A
julia> import .A: x
julia> function x(i::Float64) x end
x (generic function with 2 methods)
これをusingでやろうとすると、こうなる。
julia> using .A: x
julia> function x(i::Float64) x end
ERROR: error in method definition: function A.x must be explicitly imported to be extended
Stacktrace:
[1] top-level scope
@ none:0
[2] top-level scope
@ REPL[3]:1
不用意に定義を追加できないようにという趣旨なのだろう。でも、x
は見えているのにメソッドが追加できないということなので、どうも見え方にも種類があるらしい。ややこしい話だ。そもそも、定義を追加するときには明示的にパッケージを指定するようにしたほうが良さそう。
julia> using .A
julia> function A:x(i::Float64) x end
as によるリネーム
名前空間に読み込むときにすでに同名の名前があると衝突して読み込めない。その場合にはas
を用いてリネームする。
julia> module A
x = 0
end
Main.A
julia> x = 1
1
julia> import .A: x
WARNING: import of A.x into Main conflicts with an existing identifier; ignored.
julia> import .A: x as x0
julia> x, x0
(1, 0)
モジュールをまるごとリネームすることもできる。
julia> import .A as X
julia> X.x
0