ADR-0018: Hierarchical Modules, Implicit Prelude, Re-exports, and Qualified Module Access
此内容尚不支持你的语言。
Status
Section titled “Status”Accepted
Context
Section titled “Context”ADR-0017では、Sobakasu標準ライブラリのモジュールをSobakasuで記述し、StandardLibrary~配下から検出し、useだけで解決し、呼び出し元の単一のUdonプログラムへ統合することを定めた。また、Sobakasuのモジュール解決をADR-0016のextern境界から分離した。その後、ADR-0017は、論理名をマニフェストへ列挙する代わりに規約に基づくパスへ直接マッピングするよう修正された。
このモデルでは、モジュール階層、モジュールシンボル、子の可視性、再エクスポート、正規の公開パス、暗黙のPreludeを定義していなかった。そのため、すべてのライブラリ宣言に明示的な宣言インポートが必要であり、物理ファイルの存在が公開されていることと混同される可能性があり、privateな実装モジュールを安定した親APIの背後に隠すことができなかった。
標準ライブラリには、数値やUnityラッパーなどの具体的な領域を設計する前に、汎用的なモジュール機構が必要である。min、max、abs、sinのような一般的な関数名は、利便性だけを理由にグローバルへ注入してはならない。関連する操作には、修飾されたモジュール名を通じてアクセスできるようにするべきである。
Decision
Section titled “Decision”規約に基づく物理階層
Section titled “規約に基づく物理階層”モジュール一覧のマニフェストは使用しない。論理モジュール名は、.を/へ置換し、.sobakasuを付加することで、StandardLibrary~配下の物理ソースファイルへ直接マッピングする。
論理名は、StandardLibrary~配下のパスへ決定的にマッピングされる。
unity -> unity.sobakasuunity.game_object -> unity/game_object.sobakasuunity.transform -> unity/transform.sobakasuリゾルバーは有効なSobakasuの論理識別子だけを受け入れ、自らパスを導出し、ディレクトリを列挙しない。したがって、任意の絶対パスやルート外への逸脱は表現できない。字句上の親ファイルが存在しないモジュールはルートとなる。これにより、example.mathのような独立したモジュールが維持される。親ファイルが存在する場合、そのモジュールは子となり、自身のファイルが存在するだけでは接続されない。
コンパイラは、Preludeについて1つの特別な組み込み規約を持つ。
prelude -> StandardLibrary~/prelude.sobakasuこのファイルが存在する場合、暗黙のPreludeとして読み込まれる。存在しない場合、暗黙のPreludeはない。将来、パッケージ名、バージョン、依存関係メタデータによって新しいマニフェストが必要になる可能性はあるが、後続のADRがこの決定を変更しない限り、モジュール名とパスは規約に基づくものとする。
モジュールグラフとmod
Section titled “モジュールグラフとmod”既存の子ファイルをモジュールグラフへ接続できるのは、親の宣言だけである。
mod child;pub mod child;mod child;は、直接の子をprivateとして接続する。親はその子を使用でき、そのpublicな宣言を再エクスポートできるが、呼び出し元はその子を通じて宣言元のパスを使用できない。
pub mod child;は、直接の子を接続し、その子のパスを公開する。外部パスのすべてのモジュールセグメントはpublicでなければならない。modの重複、modとpub modの混在、子の欠落、ファイルの欠落、複数の親、依存関係の循環は診断される。宣言順序はセマンティクスに影響せず、子の宣言はuse依存関係より先に処理される。
一般的な相対インポート、self、super、modにおける任意のパス、インラインモジュール本体は導入しない。読み込まれたモジュール内では、規約に基づく直接の子のいずれかを指す先頭パスセグメントを、use/pub useから使用できる。これはmodによって確立される子の名前空間であり、一般的な相対パス機能ではない。
インポートと再エクスポート
Section titled “インポートと再エクスポート”既存のドット区切りのuse構文を、グループ化インポートやglobインポートを追加せずに拡張する。
use unity;use unity.GameObject;use unity.GameObject as Object;
pub use game_object.GameObject;pub use math;モジュールパスとの完全一致はModuleSymbolをインポートし、module.memberとして使用される。宣言で終わるパスは、その宣言を単純名としてインポートする。これらは別個の操作である。
pub useは、現在のモジュールへ同じ導入を行うとともに、導入された名前をそのモジュールから公開する。モジュールまたはpublicな宣言を再エクスポートできる。したがって、親はprivateな実装の子を隠し、より短く安定したパスを通じて宣言を公開できる。
mod game_object;pub use game_object.GameObject;ここでは、unity.game_object.GameObjectへはアクセスできないまま、unity.GameObjectをpublicにできる。再エクスポートは、宣言や関数本体を複製しない。すべてのパスは、同一の宣言シンボルの識別情報を保持する。
宣言元の識別情報と正規の公開パスは別のものである。
declaration identity: unity.game_object.GameObjectcanonical public path: unity.GameObject正規パスには最短の公開パスを使用し、同じ長さの場合は序数に基づく辞書順を決定的なタイブレーカーとする。重複または曖昧な再エクスポートは診断される。
モジュールシンボルと修飾付きアクセス
Section titled “モジュールシンボルと修飾付きアクセス”読み込まれた各モジュールは、その論理名と単純名、親、子、ソースの識別情報、可視性、public宣言インデックス、公開パス情報を含むModuleSymbolを持つ。モジュールシンボルはコンパイル時の名前空間であり、ランタイム値にはならない。
module.memberについて、BinderはModuleSymbolレシーバーと通常の値レシーバーを区別する。モジュールレシーバーは、public関数、publicな外部型バインディング、publicな子モジュール、再エクスポートを解決する。通常の値レシーバーは、引き続き型/メンバー解決を通る。存在しないメンバー、privateな宣言、privateなモジュール、接続されていない子ファイルには、それぞれ異なる診断を行う。
ADR-0025 の ConstantSymbol も同じ declaration index、use、qualified access、pub use、canonical public path、Prelude の規則へ参加する。constant 専用の module system は作らない。
モジュール名と再エクスポートパスは、Binderによって完全に解決される。IR Lowererが受け取るのは、選択済みの関数、型、メソッド、externシンボルだけである。UASM Assemblerが受け取るのは解決済みIRだけであり、ファイルシステム、モジュールグラフ、Preludeの規約を読み取ることはない。
暗黙のPrelude
Section titled “暗黙のPrelude”Preludeは、1つの通常のSobakasuモジュールである。そのpublicなエクスポートは、エントリー/ユーザーモジュールに対する優先度の低い暗黙のBinderスコープを形成する。コンパイラは生成したuseテキストを先頭へ追加しないため、ソースのspan、診断、構文木の識別情報、ソースキャッシュが維持される。
Preludeは次のものには注入されない。
- 公式の
StandardLibrary~ルート配下のモジュール - Preludeモジュール自体
標準ライブラリの依存関係はmod、use、pub useを通じて明示されたままとし、ブートストラップ時の循環を回避する。
名前検索では、より具体的な既存のローカル規則を維持しつつ、次の優先順位を使用する。
- ローカル変数とパラメーター
- 現在のモジュール内の宣言
- 現在のモジュールが宣言した子
- 明示的なエイリアス
- 明示的な宣言またはモジュールのインポート
- 暗黙のPreludeエクスポート
- 古い機能によって引き続き維持されている限定的な互換性フォールバック
したがって、ユーザー宣言と明示的なインポートはPreludeの名前をシャドーイングする。Preludeのエクスポートが曖昧な場合、恣意的に選択せず診断する。
Preludeは、基礎的な宣言を単純名としてエクスポートでき、機能領域をモジュール名としてエクスポートできる。すべての便利関数をグローバルに展開する方針を定めるものではない。数値演算や今後拡大するその他の領域では、修飾付きモジュールアクセスを優先するべきである。最終的なモジュール名、API、シグネチャ、型、オーバーロード規則は、この決定の範囲外である。
外部境界と各層の責務
Section titled “外部境界と各層の責務”use、mod、pub use、Prelude、修飾付きモジュールアクセスは、Sobakasuモジュールだけを解決する。CLR名前空間、CLR型、Unity API、VRChat API、externカタログへフォールバックすることはない。標準ライブラリのラッパーがその境界を越えるのは、ADR-0016のextern式と外部型バインディングを通じてのみである。staticなCLRクラスのバインディングと新しいSobakasuのstaticクラス構文は、このADRの範囲外である。
責務は次のように分割する。
- Module Resolver/コンパイラドライバーは、論理名を規約に基づくパスへマッピングし、ルートと組み込みのPreludeパスを特定し、
mod/use/再エクスポートの依存関係を一度だけ読み込み、グラフを構築し、解析済みソースをキャッシュし、循環を報告する。 - Parserは
mod、pub mod、use、pub useを認識し、ドット区切りのメンバーアクセスを通常の構文として保持し、不正な宣言の後で回復する。 - Binderは
ModuleSymbolオブジェクトを作成し、宣言をインデックス化し、可視性を適用し、インポート/再エクスポートとPrelude検索を実行し、正規の公開パスを選択し、シンボルの識別情報を維持し、修飾されたメンバーを解決する。 - IR Lowererは、解決済みの意味ノードだけを受け取る。
- UASM Assemblerは、解決済みIRだけを受け取る。
このADRはADR-0017を拡張する。ADR-0017の標準ライブラリの場所、到達可能性に基づく統合、単一プログラムモデル、extern境界を置き換えるものではない。
Alternatives
Section titled “Alternatives”- 生成した
useテキストをすべてのソースの先頭へ注入する。ソース位置と構文の識別情報が変化し、ソースごとの診断とキャッシュが複雑になるため却下した。 - 規約に基づくパスのすべてのファイルをpublicとして扱う。物理的な存在によってAPIの安定性が決まり、privateな実装モジュールを隠せなくなるため却下した。
- パスだけに基づいてすべての子を自動的に接続する。依存関係と可視性が暗黙的になり、未使用の子ファイルがコンパイルへ影響する可能性があるため却下した。
- Preludeのすべてのメンバーをグローバルな単純名としてエクスポートする。一般的な名前が衝突し、機能の所有元が不明確になるため却下した。
- IRまたはUASMでモジュールを再度解決する。ADR-0003のフェーズごとの責務に違反し、ソースレベルの診断が遅れるため却下した。
- CLR名前空間とstaticクラスをSobakasuモジュールとして模倣する。ADR-0016/ADR-0017の外部境界を越え、言語APIをCLRレイアウトへ結び付けるため却下した。
Rationale
Section titled “Rationale”決定的なパスマッピングにより、ソースの識別情報、診断、キャッシュの無効化、Unityパッケージのレビューを予測可能にできる。親による明示的なmod宣言により、依存関係グラフと公開範囲をレビュー可能にできる。モジュールと宣言の可視性を分離することで、実装パスを公開せずにファイル単位で整理できる。再エクスポートにより、1つの意味的な宣言を維持しながら、安定した公開APIを提供できる。優先度の低いBinderのPreludeにより、ソーステキストを変更したり、汎用的な名前でグローバルスコープをあふれさせたりすることなく、定型的な記述を削減できる。
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- 標準ライブラリコードを、明確なファイル/ディレクトリ階層へ分割できる。
- 規約に基づくパスの検出、グラフへの接続、公開状態はそれぞれ独立しており、テスト可能である。
- privateな子から提供される宣言を、安定した親のパスを通じて再エクスポートできる。
- 修飾付きモジュールにより、グローバルな名前を汚染せずに便利なAPIを提供できる。
- Preludeによる利便性を提供しながら、ユーザー宣言と明示的なインポートの優先順位を維持できる。
- 宣言の識別情報は、宣言元、再エクスポート、Prelude、明示的なインポートの各パスで安定したままとなる。
- IRとUASMは、ファイルシステムとモジュール検索の詳細から独立したままとなる。
Negative
Section titled “Negative”- リゾルバーとBinderは、より複雑なグラフ、publicメンバーのインデックス、パスメタデータを維持する必要がある。
- ライブラリ作成者は、ファイルパスと
mod宣言の同期を保たなければならない。 - 可視性、接続されていない子、再エクスポートの競合、循環について、より多くの診断が必要となる。
- パッケージ外のユーザーモジュール検出、一般的な相対インポート、glob/グループインポート、インラインモジュール、ディスクキャッシュ、具体的な数値API、staticなCLRクラスのバインディングは、引き続き今後の課題である。