4.2 KiB
モジュール
Ergでは、ファイル自体を1つのレコードとみなすことができます1。これをモジュールと呼びます。
# foo.er
.i = 1
# fooモジュールを定義するのはこのレコードを定義するのと構造的には同じ
foo = {.i = 1}
# bar.er
foo = import "foo"
print! foo # <module 'foo'>
assert foo.i == 1
モジュール型は分解代入が可能です。
{sin; cos} = import "math"
モジュールの可視性
ファイルだけでなく、ディレクトリもモジュールとなりえます。
ただしデフォルトでErgはディレクトリをErgモジュールとしては認識しません。認識させるには、__init__.er
という名前のファイルを作成します。
__init__.er
はPythonの__init__.py
と同じようなものです。
└─┬ bar
└─ __init__.er
これで、bar
ディレクトリはモジュールとして認識されます。bar
内にあるファイルが__init__.er
だけならばあまりディレクトリ構造にする意味はありませんが、複数のモジュールを束ねて一つのモジュールとしたい場合は便利です。すなわち、このような場合です。
└─┬ bar
├─ __init__.er
├─ baz.er
└─┬ qux
└─ __init__.er
# bar/__init__.er
.baz = import "./baz"
.qux = import "./qux"
bar
ディレクトリの外側からは以下のようにして使用できます。
bar = import "bar"
bar.baz.p!()
bar.qux.p!()
__init__.er
は単にディレクトリをモジュールとして機能させるだけのマーカーではなく、モジュールの可視性を制御する役割も持ちます。
Pythonとは違い、Ergのモジュールはデフォルトではインポートできないようになっています。
# __init__.er
# `./`はカレントディレクトリを指す。なくても良い
.baz = import "./baz"
qux = import "./qux"
.f x =
.baz.f ...
.g x =
qux.f ...
外からbar
モジュールをインポートしたとき、baz
モジュールはアクセス可能ですが、qux
モジュールはアクセス不可能になります。
モジュールの循環参照
Ergでは、モジュール間の循環的な依存関係を定義することができます。
# foo.er
bar = import "bar"
print! bar.g 1
.f x = x
# bar.er
foo = import "foo"
print! foo.f 1
.g x = x
しかし、手続き呼び出しによって作られた変数は、循環参照モジュールで定義することはできません。 これは、Ergが依存関係に従って定義の順番を並べ替えるからです。
# foo.er
bar = import "bar"
print! bar.x
.x = g!(1) # ModuleError: 手続き呼び出しで作られた変数は、循環参照モジュールで定義できない
# bar.er
foo = import "foo"
print! foo.x
.x = 0
また、エントリポイントであるErgモジュール(すなわち __name__ == "__main__"
であるモジュール)は循環参照の対象になることはできません。
1 モジュールとレコードの間に直接の部分型関係はないが、形式的にはモジュールがレコードの部分集合である。レコード内では属性の定義のみが行えるが、モジュール内ではあらゆる可能な式を置ける。↩