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