# WIP IDL WIP IDLは、Objectが実装するInterfaceを記述するための小さなinterface definition languageである。 一つのWIP IDL documentが一つのInterfaceを定義する。Documentに宣言された関数の集合が、そのInterfaceのOperation集合となる。Operation名、parameter、return type、documentationはIDL内の一つの宣言としてまとめ、別のOperation descriptorへ重複して記述しない。 WIP IDLのsourceそのものをInterface定義のcanonicalな交換形式とする。HostとClientの間でJSON SchemaやIDLを変換したJSON ASTを交換することは前提としない。Clientはsourceをparseして内部表現を構築し、必要に応じてAI Tool schemaやGUI formへ投影する。 この文書では、WIP IDLの初期文法と型を定義する。 ## 設計方針 - 一つのdocumentで一つのInterfaceを定義する。 - Document内のOperation declarationを、そのInterfaceのOperation一覧とする。 - Operationを関数として、parameterとreturn typeを一体で表現する。 - 文法を小さく保ち、一般的なschema validation languageにはしない。 - Wire上の値表現はTransport Bindingが定める。 - JSON SchemaはWIPの交換形式にせず、必要なClientが生成する。 - Worldspaceのエントリは、そのpathを表す`entry`型として扱う。 ## Source WIP IDL sourceはUTF-8 textとする。ASCII space、tab、LFによる空白は、tokenを分離する場合を除いて意味を持たない。 `//`から行末までは通常のcommentとし、parserは無視する。 `///`から行末まではdocumentation commentとする。Operationまたはtype declarationの直前に連続するdocumentation commentは、そのdeclarationのdocumentationとしてparserが保持する。 ```wip /// Returns entries related to the target Object. operation get_related_items() -> { items: [entry], }; ``` IdentifierはASCIIの英字または`_`で始まり、以降にASCIIの英数字または`_`を含められる。 ```text identifier = (ALPHA | "_") { ALPHA | DIGIT | "_" }; ``` Keyword、primitive type名、Worldspace固有型名はidentifierとして使用できない。 Named typeにはUpperCamelCase、Operation、parameter、field、enum case、union caseにはsnake_caseを使用する。 ## Grammar 以下のEBNFは空白とcommentを省略して示す。 ```ebnf document = { declaration } ; declaration = [ documentation ], ( type-declaration | operation-declaration ) ; documentation = doc-comment, { doc-comment } ; type-declaration = "type", type-name, "=", type-definition, ";" ; type-definition = type-expression | enum-type | union-type ; operation-declaration = "operation", identifier, "(", [ parameter, { ",", parameter }, [ "," ] ], ")", "->", type-expression, ";" ; parameter = identifier, [ "?" ], ":", type-expression ; type-expression = primitive-type | worldspace-type | type-name | record-type | list-type ; record-type = "{", [ record-field, { ",", record-field }, [ "," ] ], "}" ; record-field = identifier, [ "?" ], ":", type-expression ; list-type = "[", type-expression, "]" ; enum-type = "enum", "{", identifier, { ",", identifier }, [ "," ], "}" ; union-type = "union", "{", union-case, { ",", union-case }, [ "," ], "}" ; union-case = identifier, [ "(", type-expression, ")" ] ; primitive-type = "unit" | "boolean" | "integer" | "number" | "string" | "bytes" | "json" ; worldspace-type = "entry" ; type-name = identifier ; ``` 同じdocument内でtype名またはOperation名を重複して宣言してはならない。同じOperation内でparameter名を重複してはならない。Named typeは同じdocument内で宣言されたtypeを参照する。 ## Primitive types | Type | 意味 | WIP over HTTPSでの表現 | 制約 | | --- | --- | --- | --- | | `unit` | 値を持たない | JSON `null` | Optional parameter / fieldの省略とは区別する | | `boolean` | 真偽値 | JSON boolean | — | | `integer` | 符号付き整数 | 小数部を持たないJSON number | `-9007199254740991`以上、`9007199254740991`以下 | | `number` | IEEE 754 binary64値 | JSON number | NaN、positive infinity、negative infinityは不可 | | `string` | Unicode string | JSON string | — | | `bytes` | 任意のoctet列 | RFC 4648 Section 4のbase64を格納したJSON string | paddingを含む | | `json` | opaqueな任意のJSON value | JSON value | Clientは内部構造をschemaとして解釈しない | `integer`の範囲は、異なる言語のClient間で正確に交換できる範囲に制限している。この範囲を超える整数は`integer`としてencodeしない。`i64`、`u64`、`bigint`等の追加型とlosslessなwire表現は将来の拡張として扱う。 `json`は外部APIの応答など構造を事前に固定できない値のためのescape hatchであり、通常のOperationではより具体的な型を優先する。 ## Composite types | Type | 構文 | 意味 | WIP over HTTPSでの表現 | | --- | --- | --- | --- | | Record | `{ field: Type }` | 名前付きfieldの集合 | JSON object | | List | `[Type]` | 同じ型の順序付き値 | JSON array | | Named type | `type Name = Type;` | 型への名前付け | 参照先と同じ表現 | | Enum | `enum { first, second }` | payloadを持たないcaseの集合 | case名のJSON string | | Union | `union { case(Type), empty }` | discriminatorを持つcaseの集合 | `$case`を持つJSON object | ### Record Recordは名前付きfieldの集合である。 ```wip type QueryResult = { items: [entry], next_cursor?: string, }; ``` `?`を持たないfieldはrequired、`?`を持つfieldはoptionalである。Optionalはparameterまたはrecord fieldにのみ適用し、一般化しない。 WIP over HTTPSではrecordをJSON objectとしてencodeする。Optional fieldに値がない場合はfield自体を省略する。FieldをJSON `null`にすることは省略と同じ意味ではなく、そのfieldの型が`unit`または`json`として`null`を許す場合に限る。 宣言されていないfieldをrecordに含めてはならない。 ### List Listは同じ型の0個以上の順序付き値を表す。 ```wip [entry] ``` WIP over HTTPSではJSON arrayとしてencodeする。 ### Named type `type`宣言によって型へ名前を付けられる。 ```wip type IssueList = [entry]; ``` Aliasを参照する値のwire表現は、参照先の型と同じである。 ### Enum Enumはpayloadを持たないcaseの集合である。 ```wip type Relation = enum { parent, sibling, related, }; ``` WIP over HTTPSではcase名をJSON stringとしてencodeする。 ```json "sibling" ``` ### Union Unionはdiscriminatorを持つcaseの集合である。Caseは0個または1個のpayloadを持つ。 ```wip type LookupResult = union { found(entry), not_found, ambiguous([entry]), }; ``` WIP over HTTPSでは`$case` discriminatorを持つJSON objectとしてencodeする。Payloadを持つcaseは`value` fieldに値を格納する。 ```json { "$case": "found", "value": "/items/123" } ``` Payloadを持たないcaseには`value`を含めない。 ```json { "$case": "not_found" } ``` ## Worldspace type | Type | 意味 | WIP over HTTPSでの表現 | | --- | --- | --- | | `entry` | 同じWorldspace内のエントリを指すcanonical absolute path | JSON string | ```json "/items/123" ``` Wire上では通常の`string`と同じ表現だが、WIP IDL上で`entry`と宣言された位置にある値だけをClientがエントリとして認識する。通常の`string`やopaqueな`json`に含まれるpathらしい文字列を、Clientがエントリとして推測してはならない。 Operation Resultから`entry`を発見したClientは、そのpathを一時的に利用し、必要に応じて`fetch`または`fetch_tree`する。複数のpathを効率的に取得する場合は、Clientが必要なpathを選択した後、Transport Bindingが提供するbatchを利用する。Operation Resultへエントリ公開情報を先行同梱しない。 Operationが返したpathは、Operationの実行対象の子に限定されない。親、兄弟、関連Objectなど、Worldspace内の任意のエントリを指してよい。Operationの実行対象と返されたpathの間に、暗黙のTree edgeや意味的関係を推論してはならない。 `fetch`が返すエントリ公開情報と、`fetch_tree`が返すindexableなtreeはCore Protocolのresponseであり、WIP IDLの値型としては定義しない。 ## Operation declarations Operationは名前付きparameterとreturn typeを一つの関数宣言として定義する。 ```wip type Relation = enum { parent, sibling, related, }; /// Returns entries related to the target Object. operation get_related_items( relation: Relation, limit?: integer, ) -> { items: [entry], next_cursor?: string, }; ``` Operationのparameterは、WIP over HTTPSでは一つのJSON objectへencodeする。Parameter名がobjectのfield名になる。位置引数は定義しない。 引数を持たないOperationは空のparameter listで宣言し、空のJSON objectをargumentsとして渡す。 ```wip operation refresh() -> unit; ``` Return typeには任意のtype expressionまたはNamed typeを使用できる。ただし、将来fieldを追加する可能性があるOperationではrecordを推奨する。 `///` documentation commentは、AIやユーザーがOperationの用途を判断するための短いdescriptionとして扱う。長文documentationをIDLへ埋め込むことは想定しない。 ## Effect annotation(検討案) Operationがdomain-levelな副作用や失敗を発生させ得ることを、関数宣言上のeffect rowとして表す案を検討する。この節は確定仕様ではなく、前掲のEBNFにもまだ含めない。 構文は、return typeの後に`! { ... }`を付ける形を候補とする。 ```wip operation get_item( id: string, ) -> Item ! {}; operation update_item( id: string, value: Item, ) -> Item ! { write, fail, }; ``` | Annotation | 意味の候補 | | --- | --- | | `! {}` | 宣言上、domain-levelな副作用およびfailureを持たない | | `write` | 外部から観測可能な副作用を発生させ得る | | `fail` | `E`型のdomain failureを返し得る | | annotationなし | Effect情報が未指定 | Network error、authorization失敗、target消失、Protocol decode errorなど、すべてのremote callで起こり得るProtocol errorは`fail`に含めない。`fail`はOperation固有のdomain failureだけを表す候補とする。 Effect annotationは、ClientによるOperationの説明や、HTTP batchへ安全に含められるOperationの判定材料として利用できる可能性がある。ただし、具体的なbatch可否規則はまだ確定しない。 この案はKokaのeffect rowやUnisonのability requirementのように、関数が起こし得るeffectをsignatureへ表す記法を参考にしている。ただしWIP IDLへ導入するのは宣言上のannotationだけであり、effect handler、`perform`、continuation、resume、handlerへのnetwork転送、algebraic effect runtimeは導入しない。 ## Interface identity and sharing 一つのWIP IDL documentが一つのInterfaceを定義する。ObjectはOperation定義ではなく、そのInterfaceへの参照を持つ。 複数のObjectが同じInterface参照を共有できる。Clientは未知のInterfaceだけを`fetch_interface`で取得し、参照ごとにIDL sourceとparse済み表現をcacheできる。 Hostは主体のroleや権限に応じて異なるOperation集合を投影する場合、異なるWIP IDL documentとInterface参照を返してよい。Interfaceは頻繁に変化するOperationの実行可否を表すものではない。 Interface参照をcontent-addressed digestにすることが想定されるが、参照形式、sourceのcanonicalization、digest algorithmはTransport Bindingで定義する。 ## Interface exchange HostはInterfaceについて、少なくとも次をClientへ提供する。 | 項目 | 内容 | | --- | --- | | Interface reference | Objectが保持し、`fetch_interface`の対象となる参照 | | Language identifier | 初期versionは`wip-idl/1` | | Source | WIP IDL source | | Digest | Cacheと同一性確認に利用するdigest | 交換されるInterface定義の正本はWIP IDL sourceである。HostはJSON SchemaやIDL ASTを併記する必要はない。 ClientはIDLを内部ASTへparseし、必要に応じてAI provider用JSON Schema、GUI form、言語固有の型などを生成できる。 ClientはObjectのInterface参照をcacheと比較し、未知のInterfaceだけを`fetch_interface`する。複数のInterface取得はTransport Bindingでbatchできる。HostはClientのcache状態を推測せず、ObjectのresponseへInterface定義を先行同梱しない。 ## 未解決事項 - Sourceのcanonicalizationとdigestの厳密な計算方法。 - Interface参照の具体的な形式とscope。 - String length、numeric range、pattern等のconstraintを導入するか。 - Recursive named typeを許可するか。 - `i64`、`u64`、`bigint`、`decimal`等を追加するか。 - `entry`が参照するpathのcanonicalization規則。