すべての関数は 3 つの種別のいずれかを持ちます。種別は fn の前に書き、言語がコードにできることを制限するのはこの種別だけです。
fn open_config() { nav::call(Config) } // Turing-complete, runtime#[scene] pushdown fn Confession() -> Answer { … } // the storyconst fn fade_in(mut screen: Screen) -> Screen { … } // pure, compile time| 種別 | 使えるもの | 評価 |
|---|---|---|
fn | 物語変数以外のすべて | 実行時 |
pushdown fn | 実行時には有限型のみ、物語変数。ステップは #[scene] の中だけ | 実行時、物語の中で |
const fn | すべての型。物語変数・設定値・メタ・シグナル・I/O は不可 | 必要な場所ではコンパイル時。実行時に呼ぶこともできる |
コンパイラが種別を推論することはありません。const と書いた関数だけが const です。
呼び出し規則
| 呼び出し元 ↓ / 呼び出し先 → | fn | pushdown fn | const fn |
|---|---|---|---|
fn | 可 | 不可。エントリーシーンの開始だけができる | 可 |
pushdown fn | 不可 | 可 | 可。後述 |
const fn | 不可 | 不可 | 可 |
この表に反する呼び出しは E3001 です。物語の中ではさらにシーンと即時手続きを分ける規則があり、ステップの実行(E3002)、別のシーンの呼び出し(E3003)、乱数の取得(E3024)は #[scene] だけができます。
関数ポインタやクロージャの型は種別を持つので、規則はそれらを通しても成り立ちます。pushdown fn() -> R 型の値は pushdown fn しか指せないため、物語がポインタ経由でチューリング完全なコードに到達することはありません(値、参照、クロージャを参照)。
種別ごとのまとめ:
| 種別 | 呼べるもの | 実行時に保持できるもの | ステップ | 物語変数 |
|---|---|---|---|---|
fn | fn、const fn。エントリーシーンを開始できる | 何でも | 不可 | 不可 |
#[scene] pushdown fn | シーン、即時手続き、const fn(定数または有限の引数) | 有限型 | 可 | 読み書き可 |
pushdown fn(即時手続き) | 即時手続き、const fn。再帰不可、ループは for のみ | 有限型 | 不可 | 読み書き可 |
const fn | const fn | 何でも | 不可 | 不可 |
pushdown fn
pushdown fn は物語の一部です。次のことができます。
#[scene] を付けた pushdown fn はシーンです。プレイヤーの入力まで物語を中断するステップを実行できるのも、自分自身を含む他のシーンを呼べるのもシーンだけです。シーン内のすべてのループと呼び出しの循環は、ステップを通らなければなりません(停止性を参照)。#[scene] のない pushdown fn は即時手続きです。ステップを実行せず、再帰せず、有界なループしか持たないため、入力なしに必ず終わります。どちらにしても、2 つの入力の間のコードは必ず終わります。
シーンは Kotlin の suspend 関数のように直接呼び合います。.await はありません。呼び出しの奥のどこかでステップに達すると物語全体が中断し、入力があると同じ地点から再開します。
const fn
const fn は純粋かつ決定的で、結果は引数だけで決まります。物語変数(E3007)、設定値、メタ(E3013)、シグナルは読めず、I/O もありません。内部はチューリング完全で、ループ、再帰、Rust の整数、Rat、Decimal、String、Vec を使えます。
コンパイラは、定数、レンダー関数、物語からの呼び出しなど、結果がコンパイル時に必要な場合に const fn を評価します。評価には 1,000,000 評価ステップの固定予算があり(暫定:コンパイラ設定になる可能性があります)、使い切るとその値を必要とした呼び出しで E4003 になります。fn のコードから実行時に呼んだ const fn は通常の関数呼び出しです。
物語から const fn を呼ぶ
pushdown fn は、すべての引数が定数式か有限型である場合に const fn を呼べます。
- すべての引数が定数。 コンパイラは呼び出しをコンパイル時に評価し、その値で置き換えます(畳み込み)。値はどの型でも構いません。
fade(ms(500))がこの例です。 - 一部の引数が有限型の実行時の値。 コンパイラは関数を表化します。呼び出しが受け取りうる引数の値のすべての組み合わせで関数を評価します。これは、ありうるすべての入力で呼び出しがエラーなく終わり、有限の結果を返すことのコンパイル時の証明です。プレイ中は、実際の引数で関数が普通に呼ばれます。結果は有限型でなければなりません(
E3008)。引数の組み合わせが 65,536 を超える呼び出し(暫定:コンパイラ設定になる可能性があります)はE4004です。
const fn mood(affection: Int<0, 5>, met: bool) -> Mood { match (affection, met) { (4..=5, true) => Mood::Warm, (_, true) => Mood::Polite, _ => Mood::Stranger, }}
#[scene]pushdown fn Greeting() { match mood(AFFECTION_MIO, MET_MIO) { // tabulated: 6 × 2 entries Mood::Warm => s!(t!["\"There you are!\""]), Mood::Polite => s!(t!["\"Hello.\""]), Mood::Stranger => s!(t!["\"…Who are you?\""]), }}書き手にとっては、物語の補助ロジックを bool、列挙型、Int<lo, hi> のような小さな有限の引数を持つ普通の const fn として書けばよい、ということです。関数内のエラー(オーバーフロー、ゼロ除算、終わらないループ)は、それを引き起こす引数の値についてコンパイル時に報告され、プレイ中に起きることはありません。
fn
普通の fn はチューリング完全な実行時コードで、ゲームシステム、その UI、セーブ、設定、メタを担います。pushdown fn を呼べず、物語変数も読めません。物語に入る唯一の方法はエントリーシーンの開始で、実行中の物語を見る唯一の手段は Narrative ハンドルです。
有限型
pushdown fn のすべてのローカル変数、引数、戻り値、物語変数の型は有限でなければなりません。型が有限であるとは、値が有限個でコンパイラがそれを列挙できることです。
| 有限 | 有限でない |
|---|---|
bool、()、! | Rust の整数(u8、i32 など)、Rat、Decimal |
Int<lo, hi> | String、Vec<T> |
| ヴァリアントが有限なデータだけを持つ列挙型 | Screen などのビルトインのハンドル型 |
有限型の構造体、タプル、配列 [T; N] | 第一級のクロージャ |
有限な T の Option<T> | シグナル、設定値、メタの値 |
Text(値はプログラム中の定数テキスト) | 自分自身を含む型(再帰型) |
ファイルのアセット型 ImageAsset、AudioAsset、VoiceAsset、VideoAsset、FontAsset(値はプログラム中のアセット定数) | EntityId、Part、Mark、Duration、Color(Rust の整数を持つ)。ColorShader<A> などのシェーダー型 |
pushdown fn、scene fn、const fn のポインタ(値はプログラム中の名前付き関数) | |
有限な T の &T、&mut T(引数として) |
任意の型の定数式は、コンパイル時に畳み込まれるので pushdown fn の中にも書けます。標準ライブラリの型の完全な一覧は STD リファレンスのstd アイテムのコンパイラ規則にあります。
なぜこれで成り立つのか
エンジンは物語に 2 つの性質を求めます。すべてのステップが終わること、そして「MET_B が false のままこのステップに到達できるか」のような問いにコンパイラが答えられることです。どちらも有限なデータ上のプッシュダウンシステムで成り立ち、種別は物語をちょうどそのようなものにします。
- 物語は閉じている。
pushdown fnが呼べるのはpushdown fnとconst fnだけで、物語から呼ばれたconst fnは定数に畳み込まれるか、表化されます。つまり、受け取りうるすべての入力で有限の結果を返して終わることが証明されます。 - 入口はエントリーだけ。 チューリング完全なコードが物語に入るのはエントリーシーンを開始するときだけで、その引数は有限で、セーブに記録され、解析はそのすべての値について成り立ちます。戻ってくるのはエントリーシーンの戻り値と、物語が書くだけで読まないメタだけです。
- フレームは有限のまま。 スタックフレームは戻り先と有限なローカル変数を持ちます。物語の参照とクロージャは第二級なので、状態の一部になることはありません。
- 関数ポインタは有限。 名前付き関数は有限個なので、ポインタは有限集合の値です。
こうしてシステムとプレゼンテーションは自由に計算できます。物語の解析はそれらに依存しません。背景は哲学:構成による解析可能性にあります。
最終更新日 2026年10月29日