モジュール

badge

Ergでは、ファイル自体を1つのレコードとみなすことができます。これをモジュールと呼びます。

# foo.er
.i = 1
# fooモジュールを定義するのはこのレコードを定義するのとほとんど同じ
foo = {.i = 1}
# bar.er
foo = import "foo"
print! foo # <module 'foo'>
assert foo.i == 1

モジュール型はレコード型でもあるので、分解代入が可能です。 モジュールの場合は最後の...を省略できます。

# same as {sin; cos; ...} = import "math"
{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.er

barディレクトリの外側からは以下のようにして使用できます。

bar = import "bar"

bar.baz.p!()
bar.qux.p!()

__init__.erは単にディレクトリをモジュールとして機能させるだけのマーカーではなく、モジュールの可視性を制御する役割も持ちます。

# __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 = "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__" であるモジュール)は循環参照の対象になることはできません。