# 関数 関数は構造を受け取り、構造を返す式として扱う。 ## 構文 ```dcdl (part: { greet = String; target = String; }) => "${part.greet}! ${part.target}" ``` 関数呼び出しは通常の呼び出し構文で行う。 ```dcdl mk_hw({ greet = "Hello"; target = "World"; }) ``` 複数引数の構文は候補として以下を想定する。 ```dcdl ( input_a: { hoge = Int & >= 0; }, input_b: { fuga = 20; } ) => { # ... } ``` ## 関数の意味 関数は値として扱える。 ただし、最終データとして関数値を出力できるかどうかは別途定める。 設定ファイルを materialize する段階では、未適用の関数値は出力不能な値として扱うのが自然である。 ## 評価方針 関数は以下の方針を基本とする。 - 関数は純粋である。 - 関数はレキシカルスコープを持つ。 - 関数は定義時の環境を参照として保持する。 - 引数は必要になるまで評価しない。 - フィールドに束縛された関数呼び出し結果は、そのフィールド評価結果として memoize される。 - 任意の関数呼び出しそのものをグローバルに memoize することは必須ではない。 関数値の内部モデル例: ```text Function { params: Vec body: ExprId env: EnvRef } ``` ## 関数と重さ 関数の文法・パーサー自体は大きくない。 実装上の重さは、主に評価モデルと意味論から発生する。 注意点: - クロージャが環境を掴む。 - 引数を lazy にするか strict にするかを決める必要がある。 - 関数呼び出し結果をどこまで memoize するかを決める必要がある。 - 再帰関数を許可するかを決める必要がある。 - 関数値同士の `&` をどう扱うかを決める必要がある。 - 関数値を object に入れたとき、materialize 可能かを決める必要がある。 - import 循環と関数適用が絡んだときの cycle detection が必要になる。 軽量に保つため、初期仕様では以下の制限を検討できる。 - 関数は pure。 - lexical closure は許可。 - 引数は thunk として渡す。 - 関数本体は必要になるまで評価しない。 - 関数値は opaque。 - 関数同士の `&` は、同一関数参照以外は conflict。 - 関数値は最終データとして出力できない。 - 再帰は cycle error として扱う、または v1 では禁止する。