Decodal/doc/manual/souce/language/modules-and-imports.md

2.6 KiB

モジュールと import

import は外部ファイルを読み込み、そのファイルの評価結果を返す。

構文

import "./config.dcdl"

import specifier は文字列リテラルとする。 パスリテラル構文は採用しない。

モジュール

import 先はモジュール単位で読み込まれる。 ただし、モジュール全体を即時評価する必要はない。 各フィールドは thunk として保持され、必要になったときだけ評価される。

top-level に field 定義列を書いた module は、recursive module scope を作る。 つまり、top-level field は同じ module の他の top-level field から識別子として参照できる。

schema = {
    hoge = String;
};

result = schema;

この場合、result の右辺の schema は同じ module の top-level field schema を参照する。 通常の object literal 内の field を暗黙に recursive scope にするかは別仕様とする。

SourceLoader

import specifier の解決は処理系 core ではなく host 側の SourceLoader が行う。 CLI では、specifier を現在の module path からの相対 path として解決する。 組み込み利用では、resource table や static source map など、filesystem 以外の loader を使える。

module cache の key は loader が返す安定 key を使う。 CLI では canonical path を key とする。

循環 import

モジュール間に循環参照があっても、必要なフィールドの依存関係が循環していなければ評価できる。

例:

# main.dcdl
{
    schema = {
        hoge = String;
    };

    result = (import "./func.dcdl")(schema);
}
# func.dcdl
(input: (import "./main.dcdl").schema) =>
{
    # ...
}

func.dcdlmain.dcdl を import しているが、参照しているのは main.schema である。 main.schemamain.result に依存していなければ、この循環 import は成立する。

import の評価単位

実装上は、以下の単位で管理するのが自然である。

Module main
  schema -> thunk
  result -> thunk

Module func
  root -> thunk

各 thunk は一度だけ評価して memoize する。 評価中に同じ thunk へ戻った場合は循環依存としてエラーにする。

import 失敗

以下は import 失敗として扱う。

  • ファイルが存在しない。
  • ファイルが読めない。
  • import 先の構文解析に失敗する。
  • import 先の評価で必要な値がエラーになる。
  • 実装が禁止する import 循環に該当する。