Wikifunctions:Funkční model
Wikifunkce jsou vícejazyčný katalog funkcí, do kterého může přispívat kdokoli, kdo může vytvářet, spravovat, volat a používat funkce. Každá funkce může mít několik implementací, např. v různých programovacích jazycích nebo pomocí různých algoritmů. Jedná se o "Wikipedii funkcí" a sesterský projekt provozovaný Wikimedia Foundation.
Tento dokument se zabývá datovým modelem a modelem vyhodnocování Wikifunkcí.
- V tomto popisu modelu se termíny psané velkými písmeny vztahují k termínům definovaným v glosáři.
- Před přečtením následujícího modelu bylo velmi užitečné přečíst si průvodce předchozího prototypu, abyste získali lepší představu o tom, co se zde děje.
Z1/ZObjecty
Wikifunkce jsou wiki. Stejně jako ve všech wiki je obsah Wikifunkcí uložen především ve wiki stránkách. Stránky Wiki lze upravovat jednotlivě, přesto si projekt jako celek musí zachovat určitou konzistenci. Stránky wiki by také měly být samostatně upravitelné, aniž byste museli rozumět všem ostatním stránkám.
Zavádíme ZObjecty, které reprezentují obsah Wikifunkcí. Každá stránka wiki hlavního jmenného prostoru Wikifunkce obsahuje přesně jeden ZObject typu Z2/Persistentní objekt. Ostatní jmenné prostory mohou obsahovat jiný obsah, například stránky pravidel, uživatelské stránky, diskusní stránky atd. ZObject lze serializovat jako objekt JSON.
ZObject se skládá ze seznamu dvojic klíč/hodnota.
- Každá hodnota v páru klíč/hodnota je ZObject.
- Values can be either a Z6/String, a Z9/Reference, or have any other type. Z6/String and Z9/Reference are called terminal values. They don't expand further.
- A Z6/String has exactly two keys, Z1K1/type with the value "Z6", and Z6K1/string value, with an arbitrary string.
- A Z9/Reference has exactly two keys, Z1K1/type with the value "Z9", and Z9K1/reference ID, with a string representing a ZID.
- Každý klíč se může na každém ZObjektu objevit pouze jednou (ale může se znovu objevit na vloženém ZObjektu).
ZObjecty jsou v podstatě abstraktní syntaktické stromy. Kdyby se dalo o projektu napsat TLDR, pravděpodobně by to bylo "něco jako LISP v JSONu". Cílem je poskytnout jednoduché uživatelské rozhraní, které umožní vytvářet a manipulovat s objekty ZObjekt prostřednictvím rozhraní wiki, a vytvořit tak kódovací prostředí, které může oslovit velké množství přispěvatelů a může se stát projektem Wikimedia s aktivní komunitou.
Každý ZObjekt musí mít klíč Z1K1/type (typ) s hodnotou, která se vyhodnocuje jako Z4/Typ.
Pro víceméně čitelné označení ZID používáme zápis ZID/label (štítek), kde ZID je ZObjekt id nebo klíč takového objektu a label je (anglicky psané) označení připojené k tomuto jazykově neutrálnímu id nebo klíči.
The representation of a ZObject that is represented only as records of key/value pairs, and that only ends in terminal nodes, is called a normal form. The normal form of a ZObject is usually the one used for evaluation.
Canonical form
In order to make ZObjects more readable and more compact, we usually store and transmit them in the so-called canonical form.
Canonical forms use three syntactic transformations: for Z9/references, Z6/strings, and Z881/lists.
Canonical References
A reference refers to a ZObject by its ZID. A ZID starts with the letter Z, followed by a natural number. The normal form of a reference looks as follows (here, and throughout the document, we display ZObjects always twice: on the left hand side in the labelized version, i.e. where all ZIDs and key IDs are replaced with an English label, and on the right hand side with the ZIDs and key IDs unreplaced).
{
"type": "reference",
"reference id": "natural number"
}
|
{
"Z1K1": "Z9",
"Z9K1": "Z10"
}
|
The canonical form replaces that object with a single string with the ZID. The canonical form of the above reference is thus the following:
"natural number"
|
"Z10"
|
Note that the normal and canonical form have the same meaning.
Canonical Strings
A String is a sequence of Unicode Code Points, representing usually a word or a text. It can include spaces and any other character besides control characters.
The normal form of a String looks as follows. Note that the value of the second key is indeed the string, and not the labelized version of a ZID, as can be seen on the right hand side.
{
"type": "string",
"string value": "Wikifunctions"
}
|
{
"Z1K1": "Z6",
"Z6K1": "Wikifunctions"
}
|
Strings can usually be canonicalized by just their string instead of the whole object. The above string can be canonicalized as follows.
"Wikifunctions"
|
"Wikifunctions"
|
Note that Strings that start with a capital Latin letter and are followed by a natural number need to be escaped, as they would otherwise clash with the normal representation of a Reference.
For example, the String "Z1"
would have the following representation, both in the normal and canonical form.
This is because otherwise it would be ambiguous whether "Z1"
refers to the String Z1 or is a reference to the object with the ZID Z1.
{
"type": "string",
"string value": "Z1"
}
|
{
"Z1K1": "Z6",
"Z6K1": "Z1"
}
|
Canonical lists
Lists are represented through so-called Benjamin Arrays. Their description can be found below in the section Z881/Typed lists.
Serializace
ZObjekt[1] je serializován do své kanonické reprezentace JSONu, přičemž jako klíče jsou použity abstraktní klíče ZID ("Z1K1" atd.) a pro hodnoty buď:
Alternativní čitelnější reprezentaci lze získat nahrazením abstraktních klíčů a ZID jejich popisky v daném jazyce, tzv. "labelizovanou" reprezentací. The labelized version is often ambiguous and can not always be translated to the machine-readable representation.
V následující tabulce je uveden příklad objektu ZObjekt reprezentujícího celé kladné číslo 2. Vlevo vidíme ZObjekt textovaný v angličtině, uprostřed textovaný v němčině a vpravo vidíme ZObjekt pomocí ZID.
{
"type": "natural number",
"base 10 representation": "2"
}
|
{
"Typ": "natürliche Zahl",
"Dezimaldarstellung": "2"
}
|
{
"Z1K1": "Z10",
"Z10K1": "2"
}
|
Jak vidíte, popisky nemusí být v angličtině, ale mohou být v kterémkoli z více než 300 jazyků, které "Wikifunkce" podporují.
Normalizace
Při zpracování ZObjektů vyhodnocovacím programem se všechny ZObjekty změní na normalizovanou verzi sebe sama. Normalizovaná verze je podobná deserializované verzi, ale nespoléháme se na žádnou implicitnost, pokud jde o interpretaci textové hodnoty jako Z6/String (Řetězec) nebo Z9/Reference, ale všechny jsou vyjádřeny jako explicitní ZObjekty.
To znamená, že normalizovaná reprezentace ZObjektu je strom, kde všechny listy jsou buď typu Z6/String (Řetězec), nebo Z9/Reference.
To také znamená, že všechny Z10/Lists (Seznamy) jsou reprezentovány jako ZObjekty[1], nikoli jako pole. Kromě toho musí mít všechny objekty Z10/List (Seznam) klíče (Z10)K1/head (hlava) a (Z10)K2/tail (ocas). Prázdný seznam (včetně tail seznamu s jedním prvkem) je Z13/Empty (Prázdný)[2].
Následující normalizovaný tvar představuje celé kladné číslo 2.
{
"type": {
"type": "reference",
"reference id": "natural number"
},
"base 10 representation": {
"type": "string",
"string value": "2"
}
}
|
{
"Z1K1": {
"Z1K1": "Z9",
"Z9K1": "Z10"
},
"Z10K1": {
"Z1K1": "Z6",
"Z6K1": "2"
}
}
|
Normalizované pohledy se používají pouze jako vstupy pro vyhodnocovací stroj. Zajišťují, že vstup pro vyhodnocení je vždy jednotný a snadno zpracovatelný a že vyžaduje minimální množství speciálních případů.
Trvalé a dočasné
Každý ZObjekt nejvyšší úrovně uložený na wiki stránce Wikifunkce je Z2/Persistentní objekt (trvalý). ZObjekty, které nejsou uloženy na vlastní wiki stránce, se nazývají přechodné ZObjekty.
Každý trvalý ZObjekt musí mít Z2K1/id (id), ZID, které odpovídá názvu stránky wiki, kde je uložen. Předpokládejme, že existuje ZObjekt pro kladné celé číslo 2, který jsme viděli dříve, a že je uloženo na stránce Z382/two (dva). Takto by to mohlo vypadat (všimněte si, že ZID nemusí být nutně ty, které budeme používat ve Wikifunkcích).
{
"type": "persistent object",
"id": {
"type": "string",
"string value": "Z702"
},
"value": {
"type": "natural number",
"base 10 representation": "2"
},
"label": {
"type": "multilingual text",
"texts": [
"monolingual text",
{
"type": "monolingual text",
"language": "English",
"text": "two"
},
{
"type": "monolingual text",
"language": "German",
"text": "zwei"
}
]
}
}
|
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z702"
},
"Z2K2": {
"Z1K1": "Z10",
"Z10K1": "2"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "two"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "zwei"
}
]
}
}
|
All JSON objects stored on Wikifunctions are stored in Unicode normalization form C. All values before evaluation must also be Unicode-normalized to Unicode normal form C.
Objekt Z2/Persistent (Trvalý) poskytuje metadata pro objekt ZObjekt vložený do hodnoty Z2K2/value (hodnota).
Z2K3/label (štítek) je ZObjekt typu Z12/multilingual text (vícejazyčný text), který má jeden Z3/Key (Klíč), Z12K1/texts (texty), ukazující na Z10/list (seznam) Z11/monolingual text (jednojazyčný text) ZObjektů (pamatujte, že Z10/list (seznam) je v reprezentaci JSON reprezentován jako pole). Štítek umožňuje štítkování.
Na objektu Z2/Persistent (Trvalý) existují další klíče Z3/Keys (Klíče), které poskytují metadata, jež slouží k dokumentaci, prezentaci ve wiki a ke zlepšení nalezitelnosti. Všechny jsou definovány na objektu Z2/Persistent (Trvalý).
Z9/Reference
Z9/Reference je odkaz na Z2K2/value of the ZObject (hodnota ZObjektu) s daným ID a znamená, že tato Z2K2/value (hodnota) by zde měla být vložena. Jako příklad uveďme následující pole s jedním prvkem:
"two"
|
"Z702"
|
Tento prvek je zkratka Z9/Reference, která by v rozšířené podobě vypadala takto (jak je vysvětleno v části o deserializaci):
{
"type": "reference",
"reference id": "two"
}
|
{
"Z1K1": "Z9",
"Z9K1": "Z702"
}
|
A protože se jedná o referenci, je třeba ji nahradit Z2K2/value (hodnotou) Z2/Persistent (Trvalý) se ZID Z382 (jak je uvedeno výše), tj. vypadalo by to takto:
{
"type": "natural number",
"base 10 representation": "2"
}
|
{
"Z1K1": "Z10",
"Z10K1": "2"
}
|
All three JSON representations in this section have the same meaning for Wikifunctions. They all refer to the natural number 2.
Všimněte si, že pokud má Z8/Funkce typ argumentu Z2/Persistent object (Persistentní objekt), pak se místo Z2K2/value (hodnota) dosazuje samotný Z2/Persistent object (Persistentní objekt).
Z4/Typy
Typy jsou ZObjekty typu Z4/Type (Typ). ZObjekty typu se nazývají instance tohoto typu. Takže Z382/two (dva), které jsme viděli výše, bylo instancí typu Z10070/positive integer (kladné celé číslo).
A Type tells us how to interpret a value. Typ nám poskytuje prostředky pro kontrolu platnosti objektu ZObjekt daného typu. Typ obvykle deklaruje klíče, které jsou k dispozici pro jeho instance, a funkci, která se používá k ověřování instancí.
Zde je (zjednodušený) typ pro celá kladná čísla.
{
"type": "persistent object",
"id": {
"type": "String",
"string value": "Z10"
},
"value": {
"type": "type",
"identity": "natural number",
"keys": [
"key",
{
"type": "key",
"value type": "string",
"key id": "Z10K1",
"label": {
"type": "multilingual text",
"texts": [
"monolingual text",
{
"type": "monolingual text",
"language": "English",
"text": "base 10 representation"
},
{
"type": "monolingual text",
"language": "German",
"text": "Dezimaldarstellung"
}
]
}
}
],
"validator": "validate natural number"
},
"label": {
"type": "multilingual text",
"texts": [
"monolingual text",
{
"type": "monolingual text",
"language": "English",
"text": "natural number"
},
{
"type": "monolingual text",
"language": "German",
"text": "natürliche Zahl"
}
]
}
}
|
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z10"
},
"Z2K2": {
"Z1K1": "Z4",
"Z4K1": "Z10070",
"Z4K2": [
"Z3",
{
"Z1K1": "Z3",
"Z3K1": "Z6",
"Z3K2": "Z10K1",
"Z3K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1251",
"Z11K2": "base 10 representation"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1254",
"Z11K2": "Dezimaldarstellung"
}
]
}
}
],
"Z4K3": "Z110"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1251",
"Z11K2": "positive integer"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1254",
"Z11K2": "natürliche Zahl"
}
]
}
}
|
Aby bylo jádro Type snáze viditelné, podívejme se pouze na Z4/Type (Typ) a odstraňme štítky:
{
"type": "type",
"identity": "natural number",
"keys": [
"key",
{
"type": "key",
"value type": "string",
"keyid": "Z10K1"
}
],
"validator": "validate natural number"
}
|
{
"Z1K1": "Z4",
"Z4K1": "Z10",
"Z4K2": [
"Z3",
{
"Z1K1": "Z3",
"Z3K1": "Z6",
"Z3K2": "Z10K1"
}
],
"Z4K3": "Z110"
}
|
Typ Z10070/positive integer (kladné celé číslo) definuje v Z4K2/keys (klíče) novou Z3/Key (Klíč) Z10070K1/base 10 representation (reprezentace desítkové soustavy), kterou jsme použili výše v případě reprezentujícím číslo 2.
Z4K3/validator (validátor) ukazuje na Z8/Funkci, která přijímá instanci jako argument a vrací Z10/List (Seznam) Z5/errors (chyb). Pokud je Z10/List (Seznam) prázdný, instance prošla validací. V daném případě by Z8/Funkce provedla následující kontroly:
- V instanci je kromě metadat pouze jeden Key (klíč), Z10070K1/base 10 representation (reprezentace desítkové soustavy).
- Hodnota reprezentace desítkové soustavy má typ Z6/String (Řetězec).
- Reprezentace desítkové soustavy obsahuje pouze číslice.
- Reprezentace desítkové soustavy nezačíná číslem 0, pokud nemá délku 1.
Všimněte si, že všechny tyto kontroly jsou prováděny pomocí Z8/Funkcí, které poskytují přispěvatelé, a že všechny Types (Typy) mohou přispěvatelé definovat a upravovat. Typ čísla, který zde používáme, není nijak pevně zapsán.
Instance může používat klíče, které nejsou definovány v Type (Typu). Záleží na funkci validátoru, zda to povolí, nebo ne. Například instance Z7/Function call (Volání funkce) často používají klíče, které nejsou definovány ve Z7/Function call (Volání funkce), jak je vidět v části Z7/Function call (Volání funkce). U většiny validátorů se však očekává, že budou vyžadovat, aby byly definovány všechny klíče.
Několik věcí je však zakódováno natvrdo, například chování Z7/function call (volání funkce). O tom později.
Z3/Klíče
Všechny klíče musí mít písmeno K následované přirozeným číslem a může jim předcházet ZID. Pokud jim předchází ZID, nazývají se Global Keys (globální klíče), pokud ne, nazývají se Local Keys (lokální klíče). Například následující dvě reprezentace jsou ekvivalentní.
{
"Z1K1": "Z7",
"Z7K1": "Z781",
"Z781K1": "Z702",
"Z781K2": "Z702"
}
|
{
"Z1K1": "Z7",
"Z7K1": "Z10000",
"K1": "Z702",
"K2": "Z702"
}
|
Global Keys (Globální klíče) jsou pojmenované argumenty, zatímco Local Keys (lokální klíče) jsou poziční argumenty.
- Pravidlem je používat Global Keys (globální klíče), kdykoli je to možné.
- Local Keys (Lokální klíče) se používají hlavně tehdy, když se Z8/Funkce nebo Z4/Type (Typ) vytváří za běhu, a proto nemůže mít Global Keys (globální klíče), protože vytvořená Z8/Funkce nebo Z4/Type (Typ) není trvalá.
Global Key (Globální klíč) je vždy definován na objektu ZObjekt, ke kterému se vztahuje část ZID jeho ID.
In their definition, a Key contains information of what type of object it must contain (Z3K1), the global ID that identifies that key (Z3K2), its human-readable labels (Z3K3) and whether this key is an identity field (Z3K4).
An identity key allows objects to store their persisted ID. For example, Functions and Types contain an identity field. Also planned types, particularly enumerations, such as grammatical features, require identity.
Enumerations
An enumeration is a type that has a limited number of values. Examples of enumerations are the type Boolean (with the values "false" and "true"), or Integer sign (with the values "positive", "neutral" or "negative"). Enumerations are also an important tool to support linguistic functions.
Every enumeration type has one key that is marked as an identity field. For example, let's look at the Type definition for Boolean (removing key labels for simplicity):
{
"type": "Type",
"identity": "Boolean",
"keys": [
"Key",
{
"type": "Key",
"value type": "Boolean",
"key id": "Z40K1",
"label": { /* "identity" */ },
"is identity": "True"
}
],
...
}
|
{
"Z1K1": "Z4",
"Z4K1": "Z40",
"Z4K2": [
"Z3",
{
"Z1K1": "Z3",
"Z3K1": "Z40",
"Z3K2": "Z40K1",
"Z3K3": { /* "identity" */ },
"Z3K4": "Z41"
}
],
...
}
|
All limited values for Boolean will assign their ZID as the value for their identity/Z40K1 key. See, for example, True, which contains its identity and additionally the multilingual data (name, description, aliases) as part of the Persistent Object keys.
{
"type": "Persistent object",
"identity": {
"type": "String",
"value": "Z41"
},
"value": {
"type": "Boolean",
"identity": "True"
},
"labels": {
"type": "Multilingual text",
"texts": [
"Monolingual text",
{
"type": "Monolingual text",
"language": "English",
"text": "true"
}
]
}
}
|
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z41"
},
"Z2K2": {
"Z1K1": "Z40",
"Z40K1": "Z41"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "true"
}
]
}
}
|
Z8/Funkce
V definici Z10070/positive integer (kladné celé číslo) jsme viděli první odkaz na Z8/Funkce, Z10559/validate positive integer (validovat kladné celé číslo). Zde použijeme mnohem jednodušší funkci Z144/add (sečíst). Z144/add (sečíst) je funkce Z8/Funkce, která přijímá dvě Z10070/positive integer (kladná celá čísla) a vrací Z10070/positive integer (kladné celé číslo).
Zobrazujeme pouze hodnotu.
{
"type": "function",
"arguments": [
"argument declaration",
{
"type": "argument declaration",
"argument type": "natural number",
"key id": "Z781K1",
"label": { ... }
},
{
"type": "argument declaration",
"argument type": "natural number",
"key id": "Z781K2",
"label": { ... }
}
],
"return type": "natural number",
"tests": [
"testers",
"add one and zero",
"add two and two"
],
"implementations": [
"implementation",
"+ in Python",
"recursive addition",
"+ in JavaScript"
],
"identity": "add"
}
|
{
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z10",
"Z17K2": "Z781K1",
"Z17K3": { ... }
},
{
"Z1K1": "Z17",
"Z17K1": "Z10",
"Z17K2": "Z781K2",
"Z17K3": { ... }
}
],
"Z8K2": "Z10",
"Z8K3": [
"Z20",
"Z711",
"Z712"
],
"Z8K4": [
"Z14",
"Z721",
"Z722",
"Z723"
],
"Z8K5": "Z144"
}
|
Pro zachování stručnosti jsme z deklarací Z17/Argument (argument) odstranili Z17K3/labels (štítky), které jsou identifikovány pomocí Z17K2/key ID (klíčové ID). Ale stejně jako Z3/Keys (Klíče) na Z4/Types (Typech) mají popisky ve všech podporovaných jazycích. Keys (Klíče) jsou Global (globální), když je Z8/Funkce trvalá (persistentní), a lokální, když je dočasná (transient).
Funkce je specifikována prostřednictvím (vynechané) dokumentace, ale také prostřednictvím deklarací K3/tests (testy) a K1/type (typ) v argumentech a K2/return type (návratový typ). Kromě toho, protože funkce může mít několik Z14/Implementations (Implementace) (viz níže), Implementations (implementace) se navzájem potvrzují.
Z8/Funkce nesmějí mít vedlejší účinky měnící stav.
Z7/Volání funkce
Následující ZObjekt představuje volání funkce. V druhém řádku vidíme kompaktnější reprezentaci volání funkce, která používá syntaxi známější pro volání funkcí.
{
"type": "function call",
"function": "add",
"left": "two",
"right": "two"
}
|
{
"Z1K1": "Z7",
"Z7K1": "Z781",
"Z781K1": "Z702",
"Z781K2": "Z702"
}
|
add(two, two)
|
Z781(Z702, Z702)
|
Při použití literálů místo trvalých ZObjektů pro argumenty by to vypadalo takto.
- Všimněte si, že literály vytváříme pomocí konstruktoru Z10070/positive integer (kladné celé číslo).
- Takto lze volat všechny Z4/Types (Typy) a zadat hodnotu pro každý z jejich klíčů.
- Nejedná se o Z7/Function call (Volání funkce), ale o zápis objektu daného Z4/Type (Typu).
{
"type": "function call",
"function": "add",
"left": {
"type": "natural number",
"base 10 representation": "2"
},
"right": {
"type": "natural number",
"base 10 representation": "2"
}
}
|
{
"Z1K1": "Z7",
"Z7K1": "Z781",
"Z781K1": {
"Z1K1": "Z10",
"Z10K1": "2"
},
"Z781K2": {
"Z1K1": "Z10",
"Z10K1": "2"
}
}
|
add(natural number<"2">, natural number<"2">)
|
Z781(Z10<"2">, Z10<"2">)
|
When this Z7/Function call gets evaluated, it results as expected in the number four.
{
"type": "natural number",
"base 10 representation": "4"
}
|
{
"Z1K1": "Z10",
"Z10K1": "4"
}
|
natural number<"4">
|
Z10<"4">
|
Evaluace se provádí opakovaně, dokud není dosaženo cílového výsledku.
Z14/Implementace
Každá Z8/Funkce může mít několik různých Z14/Implementations (Implementací). Existují tři hlavní typy Z14/Implementation (Implementací): vestavěné (builtins), Z16/code (kód) nebo kompozice (composition) jiných Z8/Funkcí.
Vezměme si funkci Z144/add (sečíst) a podívejme se na pět různých Z14/Implementations (Implementací).
Vestavěné implementace
Vestavěná implementace říká evaluátoru, tj. runtime, aby vrátil příslušný výsledek vyhodnocení. Vestavěné prvky jsou pevně zakódovány ve vyhodnocovacím modulu. Z14K4/builtin (vestavěné) odkazuje na pevně zakódované builtin-ID (které by obvykle mělo být ZID Z2/Persistent object (Persistentní objekt), ale nemusí).
{
"type": "implementation",
"implements": "add",
"builtin": "Z791"
}
|
{
"Z1K1": "Z14",
"Z14K1": "Z781",
"Z14K4": "Z791"
}
|
Evaluátor by si byl vědom všech svých vlastních vestavěných modulů a mohl by je libovolně používat. Přesto je užitečné deklarovat vestavěný příkaz, abyste je mohli volat přímo.
Z16/Code
Implementace v Z16/Code (Kód) představuje fragment kódu v daném programovacím jazyce.
{
"type": "implementation",
"implements": "add",
"code": {
"type": "code",
"language": "javascript",
"source": "function add(left, right) {
return left + right;
}"
}
}
|
{
"Z1K1": "Z14",
"Z14K1": "Z781",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z600",
"Z16K2": "function Z781(Z781K1, Z781K2) {
return Z781K1 + Z781K2;
}"
}
}
|
{
"type": "implementation",
"implements": "add",
"code": {
"type": "code",
"language": "python",
"source": "def add(left, right):
return left + right"
}
}
|
{
"Z1K1": "Z14",
"Z14K1": "Z781",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z610",
"Z16K2": "def Z781(Z781K1, Z781K2):
return Z781K1 + Z781K2"
}
}
|
Evaluátor by věděl, jak transformovat zadané ZObjekty reprezentující argumenty do podporovaných programovacích jazyků, jak provést zadaný fragment kódu a jak pak výsledek transformovat zpět do ZObjektu reprezentujícího výsledek.
Nakonec bude překlad ZObjektů na nativní hodnoty podporovaných programovacích jazyků řešen uvnitř samotných Wikifunkcí (což bude vyžadovat nový návrhový dokument). Do té doby podporujeme Z16/Code (Kód) pouze pro argumenty a návratové typy, které mají pevně zakódovanou podporu evaluátoru.
Z46/Deserializer
A Z46/Deserializer takes a ZObject of a specific Type and turns it into a value for a given programming language.
For example, the following Z46/Deserializers takes a ZObject of type Z10/Natural number and turns it into a JavaScript BigInt value.
{
"type": "deserializer",
"identity": "to BigInt",
"type": "Natural number"
"converter": {
"type": "code",
"language": "javascript",
"source": "function deserialize( value ) {
return BigInt( value.decimal_representation.string_value );
}"
},
"native type": "BigInt"
}
|
{
"Z1K1": "Z46",
"Z46K1": "Z787",
"Z46K2": "Z10",
"Z46K3": {
"Z1K1": "Z16",
"Z16K1": "Z600",
"Z16K2": "function Z787(Z787K1) {
return BigInt( Z787K1.Z10K1.Z6K1 );
}"
},
"Z46K4": "BigInt"
}
|
The Z46K4/native type says in which type the deserializer will result. This allows us to use natural implementations like above for addition.
Z64/Serializer
The reverse operation of a Z46/Deserializer is the Z64/Serializer. A Z64/Serializer takes a value in the given programming language and turns it into a ZObject of the requested type.
{
"type": "serializer",
"identity": "from BigInt",
"type": "Natural number"
"converter": {
"type": "code",
"language": "javascript",
"source": "function serialize( value ) {
return {
'type': {
'type': 'reference',
'reference id': 'natural number'
},
'base 10 representation': {
'type': 'string',
'string value': value.toString()
}
}
}"
},
"native type": "BigInt"
}
|
{
"Z1K1": "Z64",
"Z64K1": "Z789",
"Z64K2": "Z10",
"Z64K3": {
"Z1K1": "Z16",
"Z16K1": "Z600",
"Z16K2": "function Z789(Z789K1) {
return {
'Z1K1': {
'Z1K1': 'Z9',
'Z9K1': 'Z10'
},
'Z10K1': {
'Z1K1': 'Z6',
'Z6K1': Z789K1.toString()
}
}
}"
},
"Z64K4": "BigInt"
}
|
Kompozice
Nejpřenosnější (ale často také nejpomalejší) Z14/Implementation (Implementace) se dosahuje skládáním jiných Z8/Funkcí.
Uvádíme jak implementaci ZObjektu, tak čitelnější zápis založený na syntaxi volání funkcí.
{
"type": "implementation",
"implements": "add",
"composition": {
"type": "function call",
"function": "if",
"condition": {
"type": "function call",
"function": "is zero",
"arg": {
"type": "argument reference",
"reference": "right"
}
},
"consequent": {
"type": "argument reference",
"reference": "left"
},
"alternative": {
"type": "function call",
"function": "add",
"left": {
"type": "function call",
"function": "successor",
"arg": {
"type": "argument reference",
"reference": "left"
}
},
"right": {
"type": "function call",
"function": "predecessor",
"arg": {
"type": "argument reference",
"reference": "right"
}
}
}
}
}
|
{
"Z1K1": "Z14",
"Z14K1": "Z781",
"Z14K2": {
"Z1K1": "Z7",
"Z7K1": "Z802",
"Z802K1": {
"Z1K1": "Z7",
"Z7K1": "Z782",
"Z782K1": {
"Z1K1": "Z18",
"Z18K1": "Z781K2"
}
},
"Z802K2": {
"Z1K1": "Z18",
"Z18K1": "Z781K1"
},
"Z802K3": {
"Z1K1": "Z7",
"Z7K1": "Z781",
"Z781K1": {
"Z1K1": "Z7",
"Z7K1": "Z783",
"Z783K1": {
"Z1K1": "Z18",
"Z18K1": "Z781K1"
}
},
"Z781K2": {
"Z1K1": "Z7",
"Z7K1": "Z784",
"Z784K1": {
"Z1K1": "Z18",
"Z18K1": "Z781K2"
}
}
}
}
}
|
if(
|
Z802(
|
Tato kompozice se opírá o řadu dalších Z8/Funkcí: Z145/is zero (je nula), Z146/successor (nástupce), Z147/predecessor (předchůdce), Z31/if (jestli), a co je nejzajímavější - sám sebe. Je zcela v pořádku, když Z14/Implementation (Implementace) rekurzivně volá svou vlastní Z8/Funkci. Všimněte si však, že evaluátor nemusí volat Z14/Implementation (Implementace) rekurzivně - evaluátor si může v každém kroku rekurze zvolit libovolnou implementaci.
To je všechno, jen ne rychlé - ale umožňuje nám to použít dobře pochopitelný formalismus a jeho velmi jednoduchou implementaci, abychom zajistili, že ostatní implementace Z144/add (sečíst) jsou správné - pravda, pro sčítání je to asi méně zajímavé, ale dokážeme si představit, že existují Z8/Funkce, které mají zjevně správnější implementace a mnohem důmyslnější a rychlejší implementace. Wikifunkce mohou tyto implementace vzájemně testovat, a tím nám poskytnout určitý pocit jistoty ohledně jejich správnosti.
Example evaluation
In the following we evaluate the above composition. We start with the following Z7/function call (we only stick to the functional syntax due to its brevity).
add(Natural number<"2">, Natural number<"2">)
|
Z781(Z10<"2">, Z10<"2">)
|
We replace the function call with the composition given above, and replace the arguments with the given values. That results in the following code.
if(
is zero(Natural number<"2">),
Natural number<"2">,
add(
successor(Natural number<"2">),
predecessor(Natural number<"2">)
)
)
|
Z802(
Z782(Z10<"2">),
Z10<"2">,
Z781(
Z783(Z10<"2">),
Z784(Z10<"2">)
)
)
|
We evaluate the Z782/is zero(Z10/Natural number<"2">)
to the Z40/Boolean value of Z42/false (since 2 is not zero).
That results in:
if(
false,
Natural number<"2">,
add(
successor(Natural number<"2">),
predecessor(Natural number<"2">)
)
)
|
Z802(
Z42,
Z10<"2">,
Z781(
Z783(Z10<"2">),
Z784(Z10<"2">)
)
)
|
This allows us to replace the call to Z802/if with the Z802K3/alternative, since the Z802K1/condition is false. That results in:
add(
successor(Natural number<"2">),
predecessor(Natural number<"2">)
)
|
Z781(
Z783(Z10<"2">),
Z784(Z10<"2">)
)
|
The Z783/successor function just adds one to a number, and the Z784/predecessor function removes one. Any of these functions may or may not be implemented in code or in some other way, this does not really matter. If we replace both these function calls, we get to the following call:
add(
Natural number<"3">,
Natural number<"1">
)
|
Z781(
Z10<"3">,
Z10<"1">
)
|
Again we substitute the call to Z781/add with its composition, and replace the arguments with the new values. That results in:
if(
is zero(Natural number<"1">),
Natural number<"3">,
add(
successor(Natural number<"3">),
predecessor(Natural number<"1">)
)
)
|
Z802(
Z782(Z10<"1">),
Z10<"3">,
Z781(
Z783(Z10<"3">),
Z784(Z10<"1">)
)
)
|
We again check if the value given to Z782/is zero (it is not, it is one). So we replace the call to Z782/is zero again with Z42/false.
if(
false,
Natural number<"3">,
add(
successor(Natural number<"3">),
predecessor(Natural number<"1">)
)
)
|
Z802(
Z42,
Z10<"3">,
Z781(
Z783(Z10<"3">),
Z784(Z10<"1">)
)
)
|
Since the Z802K1/condition is again false, we replace the call to Z802/if with the Z802K3/alternative.
add(
successor(Natural number<"3">),
predecessor(Natural number<"1">)
)
|
Z781(
Z783(Z10<"3">),
Z784(Z10<"1">)
)
|
Again we replace the function calls to Z783/successor and Z784/predecessor with the respective results, one number more, one number less.
add(
Natural number<"4">,
Natural number<"0">
)
|
Z781(
Z10<"4">,
Z10<"0">
)
|
We are now again at the point where we replace the call to Z781/add with its composition. That results in:
if(
is zero(Natural number<"0">),
Natural number<"4">,
add(
successor(Natural number<"4">),
predecessor(Natural number<"0">)
)
)
|
Z802(
Z782(Z10<"0">),
Z10<"4">,
Z781(
Z783(Z10<"4">),
Z784(Z10<"0">)
)
)
|
The call to Z782/is zero now got the argument Z10/natural number<"0"> which is indeed zero. So the call to Z782/is zero results in a Z41/true. That results in:
if(
true,
Natural number<"4">,
add(
successor(Natural number<"4">),
predecessor(Natural number<"0">)
)
)
|
Z802(
Z41,
Z10<"4">,
Z781(
Z783(Z10<"4">),
Z784(Z10<"0">)
)
)
|
The Z802/if function call now has a Z41/true Z802K1/condition, which means we replace the whole call with the Z802K2/consequence, not the Z802K3/alternative. That results in:
Natural number<"4">
|
Z10<"4">
|
This is a fixpoint, i.e. it does not change when evaluated, and thus is the result of our function call.
2 and 2 is 4.
Pořadí evaluace
Pořadí evaluace záleží na evaluátorovi. Protože všechny Z8/Funkce nesmějí mít vedlejší účinky, povede to vždy ke stejnému výsledku. Nerozumná strategie evaluací však může vést k tomu, že evaluátor provede mnohem více výpočtů, než je nutné, nebo dokonce k tomu, že evaluace nebude ukončena. Z1445/add recursive (sečíst rekurzivně) nám poskytuje příklad, který může skončit nekonečnou smyčkou, pokud se pokusíme o úplné pořadí evaluace:
Pro rekurzivní volání Z31/if (jestliže) v Z1445/add (sečíst) by nebylo rozumné nejprve vyhodnotit všechny tři argumenty a pak vrátit buď druhý, nebo třetí argument. V závislosti na prvním argumentu Z31K1/condition (podmínka) stačí vrátit buď Z31K2/consequent (následné), nebo Z31K3/alternative (alternativní). Nikdy se nestane, že bychom potřebovali vyhodnotit druhý i třetí argument.
Ve skutečnosti bychom mohli vrátit i druhý nebo třetí argument bez evaluace. Nezapomeňte, že evaluátor stejně vyhodnotí každý výsledek znovu, dokud není dosaženo fixního bodu. Z31/if (jestliže) lze tedy implementovat postupně, vynechat irelevantní větev a vrátit relevantní větev jako nevyhodnocený ZObjekt[1].
Strategie líné (lazy) evaluace se obecně doporučuje, ale například pokud chce evaluátor použít implementaci založenou na Z16/Code (Kód), nemusí to být proveditelné. A pak se evaluátor může rozhodnout, že nejprve posoudí argumenty a teprve potom vnější volání. Nakonec se nabízí možnost experimentovat s různými strategiemi evaluace.
Z20/Testy
Z20/Tests (Testy) jsou ZObjekty[1], které volají Z7/function call (volání funkce) a poté testují výsledek pomocí Z8/Funkce. Pokud funkce Z8/Funkce vrátí hodnotu Z54/true (pravda), Z20/Test projde, jinak neprojde.
Testy slouží k zajištění toho, aby se všechny Z14/Implementations (Implementace) chovaly tak, jak mají, a měly by být považovány za podobné jednotkovým testům. Z8/Funkce by měla obsahovat seznam všech Z20/Tests (Testů), které musí být splněny, aby byla Z14/Implementation (Implementace) v souladu. Různé verze Z14/Implementation (Implementace) lze navíc vzájemně testovat pro zajištění konzistence.
{
"type": "tester",
"function": "add",
"call": {
"type": "function call",
"function": "add",
"left": "two",
"right": "two"
},
"result validator": {
"type": "function call",
"function": "equivalent natural number",
"right": "four"
}
}
|
{
"Z1K1": "Z20",
"Z20K1": "Z781",
"Z20K2": {
"Z1K1": "Z7",
"Z7K1": "Z781",
"Z781K1": "Z702",
"Z781K2": "Z702"
},
"Z20K3": {
"Z1K1": "Z7",
"Z7K1": "Z788",
"Z788K2": "Z704"
}
}
|
In this case we evaluate first the Z20K2/call, which is Z781/add(Z702/two, Z702/two)
, resulting in Z10/Natural number<"4">
.
That, in turn, is then used in the Z20K3/result validator, where it is injected as the first argument, resulting in Z788/natural number equality(Z10/Natural number<"4">, Z704/four)
.
That call should result in Z41/true, and thus the Z20/Tester should pass.
Generické typy
Obecný typ je realizován Z7/Function call (Voláním funkce) na Z8/Funkci, která přijímá nějaké argumenty a vrací Z4/Type (Typ).
Například Z10022/Pair (Dvojice) je funkce, která přijímá jako parametry dva Z4/Type (Typy), jeden pro první a druhý prvek, a vrací inline Z4/Type (Typ). Pokud tedy chceme vytvořit pair (dvojici) Z10070/Positive integers (kladných celých čísel), zavoláme Z10022/Pair(Z10070/Positive Integer, Z10070/Positive Integer) a výsledkem bude Z4, které můžeme použít pro pole Z1K1 objektu ZObjekt.[1]
{
"type": {
"type": "function call",
"function": "typed pair",
"first": "natural number",
"second": "natural number"
},
"first": "one",
"second": "two"
}
|
{
"Z1K1": {
"Z1K1": "Z7",
"Z7K1": "Z882",
"Z882K1": "Z10",
"Z882K2": "Z10"
},
"K1": "Z701",
"K2": "Z702"
}
|
Výsledkem Z7/Function call (Volání funkce) je dynamicky vytvořený Z4/Type (Typ), který zajistí, že oba prvky Pair (Dvojice) mají správný Z4/Type (Typ). Výsledek Z7/Function call (Volání funkce) vypadá takto.
{
"type": "type",
"identity": {
"type": "function call",
"function": "typed pair",
"first": "natural number",
"second": "natural number"
},
"keys": [
"key",
{
"type": "key",
"id": "K1",
"value type": "natural number"
},
{
"type": "key",
"id": "K2",
"value type": "natural number"
}
],
"validator": "validate typed pair"
}
|
{
"Z1K1": "Z4",
"Z4K1": {
"Z1K1": "Z7",
"Z7K1": "Z882",
"Z882K1": "Z10",
"Z882K2": "Z10"
},
"Z4K2": [
"Z3",
{
"Z1K1": "Z3",
"Z1K2": "K1",
"Z3K1": "Z10"
},
{
"Z1K1": "Z3",
"Z1K2": "K2",
"Z3K1": "Z10"
}
],
"Z4K3": "Z892"
}
|
To také vysvětluje Z4K1/identity field (pole identity) na Z4/Type (Typu): popisuje, jak byl Z4/Type (Typ) vytvořen, a umožňuje nám přístup k argumentům použitým při vytváření Typu. Deklarativní uchovávání těchto informací je velmi užitečné pro statickou validaci volání funkce a pro porovnávání typů.
Pokud chceme funkci Z10022/Pair (Dvojice), která neomezuje Z4/Type (Typ) jednoho nebo obou svých prvků, mohli bychom zavolat funkci Z10022/Pair se Z1/ZObjekt[3] jako jedním nebo oběma argumenty.
Z10/Seznamy
Zde je list (seznam) dvou strings (řetězců).
[
"string",
"a",
"b"
]
|
[
"Z6",
"a",
"b"
]
|
Pokud to převedeme na ZObjekty, vypadá to následovně.
{
"type": {
"type": "function call",
"function": "typed list",
"elementtype": "string"
},
"head": "a",
"tail": {
"type": {
"type": "function call",
"function": "typed list",
"elementtype": "string"
},
"head": "b"
}
}
|
{
"Z1K1": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z6"
},
"K1": "a",
"K2": {
"Z1K1": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z10K1": "Z6"
},
"K1": "b"
}
}
|
Všimněte si, že při deserializaci JSON pole literálů je třeba nejprve zkontrolovat, zda mají všechny prvky pole stejný Z1K1/type (typ). Pokud ano, vytvoříme Z10/List (Seznam) s použitím tohoto typu jako argumentu. Pokud ne, vytvoříme Z10 pomocí Z1/ZObjekt[3] jako argumentu.
[
"object"
]
|
[
"Z1"
]
|
Z22/Evaluation result
A Z7/Function call executed in Wikifunctions always returns an object of type Z22/Evaluation result.
An Evaluation result object always contains the value returned from the execution of a Function Call and a collection of metadata gathered during the evaluation. Here's an example of a successful response:
{
"type": "evaluation result",
"result": "Hello, World!",
"metadata": {
"type": {
"type": "function call",
"function": "typed map",
"key type": "string",
"value type": "object"
},
"map": [
{
"type": "function call",
"function": "typed pair",
"first type": "string",
"second type": "object"
},
{
"type": {
"type": "function call",
"function": "typed pair",
"first type": "string",
"second type": "object"
},
"key": "orchestrationDuration",
"value": "139 ms"
}
]
}
}
|
{
"Z1K1": "Z22",
"Z22K1": "Hello, World!",
"Z22K2": {
"Z1K1": {
"Z1K1": "Z7",
"Z7K1": "Z883",
"Z883K1": "Z6",
"Z883K2": "Z1"
},
"K1": [
{
"Z1K1": "Z7",
"Z7K1": "Z882",
"Z882K1": "Z6",
"Z882K2": "Z1"
},
{
"Z1K1": {
"Z1K1": "Z7",
"Z7K1": "Z882",
"Z882K1": "Z6",
"Z882K2": "Z1"
},
"K1": "orchestrationDuration",
"K2": "139 ms"
}
]
}
}
|
If the evaluation is unsuccessful, the response field will contain Z24/Void, while the metadata field will contain an "error" key with the details of the failure. This is an example of a failed Evaluation result object:
{
"type": "evaluation result",
"result": "void",
"metadata": {
"type": {
"type": "function call",
"function": "typed map",
"key type": "string",
"value type": "object"
},
"map": [
{
"type": "function call",
"function": "typed pair",
"first type": "string",
"second type": "object"
},
{
"type": {
"type": "function call",
"function": "typed pair",
"first type": "string",
"second type": "object"
},
"key": "errors",
"value": {
"type": "error",
"error type": "unspecified error",
"error value": {
"type": {
"type": "function call",
"function": "errortype to type",
"errortype": "unspecified error"
},
"error information": "Some error happened"
}
}
}
]
}
}
|
{
"Z1K1": "Z22",
"Z22K1": "Z24",
"Z22K2": {
"Z1K1": {
"Z1K1": "Z7",
"Z7K1": "Z883",
"Z883K1": "Z6",
"Z883K2": "Z1"
},
"K1": [
{
"Z1K1": "Z7",
"Z7K1": "Z882",
"Z882K1": "Z6",
"Z882K2": "Z1"
},
{
"Z1K1": {
"Z1K1": "Z7",
"Z7K1": "Z882",
"Z882K1": "Z6",
"Z882K2": "Z1"
},
"K1": "errors",
"K2": {
"Z1K1": "Z5",
"Z5K1": "Z500",
"Z5K2": {
"Z1K1": {
"Z1K1": "Z7",
"Z7K1": "Z885",
"Z885K1": "Z500"
},
"Z500K1": "Some error happened"
}
}
}
]
}
}
|
These examples are just condensed versions of the real Evaluation result objects and only contain one example key in the Metadata field. In real examples, the metadata collection will return all the metrics gathered by the backend services, including the run's duration, CPU usage, and memory usage.
For a more detailed description of all the possible metadata returned in the Evaluation result object, see the Function call metadata guide in Mediawiki.
Z5/Chyby
Z7/Function call (Volání funkce) může vždy vést k Z5/Error (Chybě). To se může týkat více či méně neopravitelných případů (tj. dělení nulou nebo vyčerpání paměti se řeší stejně).
Z5 is a generic type. Each instance of Z5 references the ZID of an error type (in Z5K1/error type), and that error type determines the type of Z5K2/error value, and the keys that will be present therein. Each error type is an instance of Z50/error type, and ZIDs Z500–Z599 are reserved for error types.
Additional information is available at Abstract Wikipedia/Representation of errors.
Z99/Quote
Z99/Quote is used as a wrapper around another ZObject, to indicate that it should not be evaluated ("resolved"). (This is similar to quoting in Lisp.) Z99/Quote has a single key, Z99K1/quotation, of type Z1/Object.
To illustrate, some parts of error objects (instances of Z5/Error) are quoted when they get created during the execution of a function call. For example, an error of type Z507/Error in evaluation includes a copy of the entire function call whose execution caused the error (as the value of Z507K1/function call). Because this function call is very likely to be malformed in some way, we ensure that no further attempt is made to evaluate it, by quoting it inside the error object. (Thus, the type of Z507K1 is declared as Z99, and its value is always quoted.)
We use the following guidelines for when to use Z99/Quote:
- Quote a ZObject when we believe it may be invalid in some way.
- But do not quote Z1K1 by itself. If its value is in doubt, quote the entire object that contains it.
- Quote resolvable keys (keys whose values contain instances of Z7, Z9, or Z18) that might inappropriately be ingested as the input to a function.
- Quote when resolving a value might cause a catastrophe (e.g. infinite recursion).
Note: as our resolution strategy evolves, it's possible that (3) and (4) could become unnecessary.
Nefunkcionální funkce
Zatímco žádná Z8/Funkce nesmí mít vedlejší účinky, některé Z8/Funkce nemusí být zcela funkční. Tj. mohou vracet různé hodnoty, když jsou volány se stejnými parametry. Typickými příklady takové Z8/Funkce je "return a random number" (vrátit náhodné číslo), "return the current time" (vrátit aktuální čas) nebo "return a value from Wikidata" (vrátit hodnotu z Wikidat).
This might change in the future. Tato problematika bude řešena v pozdějším dokumentu.
Zx/Sum typy
Zvláště užitečným obecným typem je Zx/Sum type (Sum typ), který přijímá seznam Z4/Types (Typů) a vrací Z4/Type (Typ), který může přijmout přesně jednu instanci kteréhokoli z daných typů.
To také umožní použití nepovinných parametrů ve volání funkcí.
Tato problematika bude řešena v pozdějším dokumentu.
Vybrané otázky a úkoly
- Potřebujeme "povinný/volitelný" pro klíče někde na začátku? - ne
- Nahrazení výchozího nastavení v Z3/Key (Klíč) pomocí Zx/Sum (Sum)? (Nebo to alespoň uvést do souladu s deklarací Z17/argument (argumentu))
- Mohlo by být ponecháno na později, pokud zatím nepotřebujeme výchozí nastavení na Z3.
- Make a note that all is Unicode and that all is the normalization required by MediaWiki
- Rewrite intro to start with normal and then canonicalize