92 lines
2.5 KiB
Markdown
92 lines
2.5 KiB
Markdown
# 関数
|
|
|
|
関数は構造を受け取り、構造を返す式として扱う。
|
|
|
|
## 構文
|
|
|
|
```n
|
|
(part: {
|
|
greet = String;
|
|
target = String;
|
|
}) =>
|
|
"${part.greet}! ${part.target}"
|
|
```
|
|
|
|
関数呼び出しは通常の呼び出し構文で行う。
|
|
|
|
```n
|
|
mk_hw({
|
|
greet = "Hello";
|
|
target = "World";
|
|
})
|
|
```
|
|
|
|
複数引数の構文は候補として以下を想定する。
|
|
|
|
```n
|
|
(
|
|
input_a: {
|
|
hoge = Int & >= 0;
|
|
},
|
|
input_b: {
|
|
fuga = 20;
|
|
}
|
|
) =>
|
|
{
|
|
# ...
|
|
}
|
|
```
|
|
|
|
## 関数の意味
|
|
|
|
関数は値として扱える。
|
|
ただし、最終データとして関数値を出力できるかどうかは別途定める。
|
|
設定ファイルを materialize する段階では、未適用の関数値は出力不能な値として扱うのが自然である。
|
|
|
|
## 評価方針
|
|
|
|
関数は以下の方針を基本とする。
|
|
|
|
- 関数は純粋である。
|
|
- 関数はレキシカルスコープを持つ。
|
|
- 関数は定義時の環境を参照として保持する。
|
|
- 引数は必要になるまで評価しない。
|
|
- フィールドに束縛された関数呼び出し結果は、そのフィールド評価結果として memoize される。
|
|
- 任意の関数呼び出しそのものをグローバルに memoize することは必須ではない。
|
|
|
|
関数値の内部モデル例:
|
|
|
|
```text
|
|
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 では禁止する。
|