erg/doc/JA/syntax/26_module.md
2023-06-07 00:27:19 +09:00

4.2 KiB
Raw Blame History

モジュール

badge

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 モジュールとレコードの間に直接の部分型関係はないが、形式的にはモジュールがレコードの部分集合である。レコード内では属性の定義のみが行えるが、モジュール内ではあらゆる可能な式を置ける。

Previous | Next