Wikifunctions:Funktionsmodell

From Wikifunctions
This page is a translated version of the page Wikifunctions:Function model and the translation is 100% complete.

Wikifunctions ist ein mehrsprachiger Katalog von Funktionen, zu dem jeder beitragen und wo jeder Funktionen erstellen, pflegen, aufrufen und benutzen kann. Jede Funktion kann mehrere Implementierungen haben, z. B. in verschiedenen Programmiersprachen oder mit unterschiedlichen Algorithmen. Es ist eine “Wikipedia von Funktionen” und ein von der Wikimedia-Stiftung betriebenes Schwesterprojekt.

Dieses Dokument deckt das Datenmodell und das Auswertungsmodell von Wikifunctions ab.

In dieser Modellbeschreibung beziehen sich großgeschriebene Begriffe auf Begriffe, die im Glossar definiert sind.
Die Lektüre des Walkthroughs eines vorherigen Prototyps ist sehr hilfreich, um ein besseres Gespür dafür zu bekommen, was hier vor sich geht, bevor du das folgende Modell liest.

Z1/ZObjekte

Wikifunctions ist ein Wiki. Wie in allen Wikis wird der Inhalt von Wikifunctions vorwiegend in Wiki-Seiten gespeichert. Wiki-Seiten können einzeln bearbeitet werden, dennoch muss das Projekt als Ganzes eine gewisse Konsistenz bewahren. Ebenso sollte man Wiki-Seiten einzeln bearbeiten können, ohne die ganzen anderen Seiten verstehen zu müssen.

Wir führen ZObjekte zur Darstellung des Inhalts von Wikifunctions ein. Jede Wiki-Seite des Hauptnamensraums von Wikifunctions enthält genau ein ZObjekt des Typs Z2/Persistentes Objekt. Andere Namensräume können anderen Inhalt haben wie zum Beispiel Richtlinienseiten, Nutzerseiten, Diskussionsseiten etc. Ein ZObjekt kann als JSON-Objekt serialisiert werden.

Ein ZObjekt besteht aus einer Liste von Schlüssel/Wert-Paaren.

  • Jeder Wert in einem Schlüssel/Wert-Paar ist ein ZObjekt.
  • Werte können entweder eine Z6/Zeichenkette, eine Z9/Referenz oder ein beliebiger anderer Typ sein. Z6/Zeichenkette und Z9/Referenz werden als terminale Werte bezeichnet. Sie expandieren nicht weiter.
  • Eine Z6/Zeichenkette hat genau zwei Schlüssel, Z1K1/Typ mit dem Wert "Z6" und Z6K1/Zeichenkettenwert mit einer beliebigen Zeichenkette.
  • Eine Z9/Referenz hat genau zwei Schlüssel, Z1K1/Typ mit dem Wert "Z9" und Z9K1/Referenz-ID, wobei eine Zeichenkette eine ZID darstellt.
  • Jeder Schlüssel kann in jedem ZObjekt nur einmal vorkommen (aber er kann in einem eingebetteten ZObjekt erneut erscheinen).

ZObjekte sind im Grunde abstrakte Syntax-Bäume. Gäbe es ein ZLNG ("Zu Lang; Nicht Gelesen") des Projekts, wäre es wahrscheinlich "so was wie LISP in JSON”. Ziel ist es, eine einfache UX zur Erstellung und Manipulation von ZObjekten über eine Wiki-Schnittstelle zur Verfügung zu stellen, und so eine Kodierumgebung zu schaffen, die eine große Anzahl von Mitwirkenden erreichen und zu einem Wikimedia-Projekt mit einer aktiven Community werden kann.

Jedes ZObjekt muss einen Schlüssel vom Z1K1/Typ haben mit einem Wert, der zu einem Z4/Typ auswertet.

Wir verwenden die Notation ZID/Bezeichnung, um ZIDs mehr oder weniger lesbar zu bezeichnen, wobei ‘ZID’ eine ZObjekt-ID oder ein Schlüssel zu einem solchen Objekt und ‘Bezeichnung’ die (englischsprachige) Bezeichnung ist, die dieser sprachneutralen ID oder diesem Schlüssel zugeordnet ist.

Die Darstellung eines ZObjekts, das nur als Datensatz von Schlüssel/Wert-Paaren dargestellt wird und nur in terminalen Knoten endet, wird als Normalform bezeichnet. Die Normalform eines ZObjekts wird üblicherweise für die Auswertung genutzt.

Kanonische Form

Um ZObjekte lesbarer und kompakter zu machen, speichern und übertragen wir sie normalerweise in die sogenannte kanonische Form.

Kanonische Formen verwenden drei syntaktische Transformationen: für Z9/Referenzen, Z6/Zeichenketten und Z881/Listen.

Kanonische Referenzen

Eine Referenz bezieht sich über ihre ZID auf ein ZObjekt. Eine ZID beginnt mit dem Buchstaben Z, gefolgt von einer natürlichen Zahl. Die normale Form einer Referenz sieht wie folgt aus (hier und im gesamten Dokument zeigen wir ZObjekte immer zweimal: auf der linken Seite in der bezeichneten Version, d. h. wo alle ZIDs und Schlüssel-IDs durch eine englische Bezeichnung ersetzt sind, und auf der rechte Seite mit nicht ersetzten ZIDs und Schlüssel-IDs).

{
  "type": "reference",
  "reference id": "natural number"
}
{
  "Z1K1": "Z9",
  "Z9K1": "Z10"
}

Die kanonische Form ersetzt dieses Objekt durch eine einzelne Zeichenkette mit der ZID. Die kanonische Form der obigen Referenz ist somit die folgende:

"natural number"
"Z10"

Beachte, dass die Normalform und die kanonische Form dieselbe Bedeutung haben.

Kanonische Zeichenketten

Eine Zeichenkette ist eine Folge von Unicode-Codepunkten, die normalerweise ein Wort oder einen Text darstellen. Sie kann Leerzeichen und jedes andere Zeichen außer Steuerzeichen enthalten.

Die Normalform einer Zeichenkette sieht wie folgt aus. Beachte, dass der Wert des zweiten Schlüssels tatsächlich die Zeichenkette ist und nicht die bezeichnete Version einer ZID, wie sie auf der rechten Seite zu sehen ist.

{
  "type": "string",
  "string value": "Wikifunctions"
}
{
  "Z1K1": "Z6",
  "Z6K1": "Wikifunctions"
}

Zeichenketten können normalerweise nur anhand ihrer Zeichenkette und nicht anhand des gesamten Objekts kanonisiert werden. Die obige Zeichenkette kann wie folgt kanonisiert werden.

"Wikifunctions"
"Wikifunctions"

Beachte, dass Zeichenketten, die mit einem lateinischen Großbuchstaben beginnen und denen eine natürliche Zahl folgt, mit Auslassungszeichen versehen werden müssen, da sie sonst mit der normalen Darstellung einer Referenz kollidieren würden. Beispielsweise hätte die Zeichenkette "Z1" die folgende Darstellung, sowohl in der Normalform als auch in der kanonischen Form. Ansonsten wäre es nicht eindeutig, ob "Z1" sich auf die Zeichenkette Z1 beziehen würde oder eine Referenz auf das Objekt mit der ZID Z1 wäre.

{
  "type": "string",
  "string value": "Z1"
}
{
  "Z1K1": "Z6",
  "Z6K1": "Z1"
}

Kanonische Listen

Listen werden durch sogenannte Benjamin-Arrays dargestellt. Ihre Beschreibung findet sich weiter unten im Abschnitt Z881/Typisierte Listen.

Darstellung in JSON

Ein ZObjekt kann in JSON mit seinen Schlüsseln als abstrakten ZID-Schlüsseln ("Z1K1" usw.) und der JSON-Darstellung des Wertes dargestellt werden.

Eine alternative, besser lesbare Darstellung kann gegeben werden, indem die abstrakten Schlüssel und ZIDs durch ihre Bezeichnung in einer bestimmten Sprache ersetzt werden, die "bezeichnete" Darstellung. Die bezeichnete Version ist oft mehrdeutig und kann nicht immer in die maschinenlesbare Darstellung übersetzt werden.

Die folgende Tabelle zeigt ein Beispiel für ein ZObjekt, das die positive Ganzzahl 2 darstellt. Links sehen wir das englisch und in der Mitte das deutsch bezeichnete ZObjekt, und rechts sehen wir das ZObjekt mit Nutzung von ZIDs.

{
  "type": "natural number",
  "base 10 representation": "2"
}
{
  "Typ": "natürliche Zahl",
  "Dezimaldarstellung": "2"
}
{
  "Z1K1": "Z10",
  "Z10K1": "2"
}

Wie man sieht, müssen die Bezeichnungen nicht auf Englisch sein, sie können vielmehr in jeder der mehr als 300 Sprachen sein, die Wikifunctions unterstützt.

Normalform

Für die Verarbeitung von ZObjekten durch die Auswertefunktion werden alle ZObjekte in eine normalisierte Version ihrer selbst umgewandelt. Die normalisierte Version ähnelt der deserialisierten Version, aber wir verlassen uns nicht auf irgendeine Implizität, ob ein String-Wert als Z6/String oder als Z9/Referenz zu interpretieren ist, sondern sie werden alle als explizite ZObjekte ausgedrückt.

Das heißt, die normalisierte Darstellung eines ZObjekts ist ein Baum, bei dem alle Blätter entweder vom Typ Z6/String oder Z9/Referenz sind.

Das bedeutet auch, dass alle Listen als ZObjekte und nicht als Array dargestellt werden.

Die folgende Normalform stellt das obige ZObjekt dar, das den Wert der natürlichen Zahl 2 hat.

{
  "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"
   }
}

Normalisierte Ansichten werden nur als Eingaben für die Auswerte-Engine verwendet. Sie stellen sicher, dass die Eingaben für die Auswertung immer einheitlich und einfach zu verarbeiten sind und nur eine minimale Anzahl von Sonderfällen erfordern.

Persistent und transient

Jedes ZObjekt der obersten Ebene, das in einer Wikifunctions-Wikiseite gespeichert ist, ist ein Z2/Persistentes Objekt. ZObjekte, die nicht auf einer eigenen Wikiseite gespeichert sind, werden als transiente ZObjekte bezeichnet.

Jedes persistente ZObjekt muss eine Z2K1/id haben, eine ZID, die dem Namen der Wikiseite entspricht, auf der es gespeichert ist. Nehmen wir an, dass es ein ZObjekt für die natürliche Zahl 2 gibt, die wir zuvor gesehen haben, und dass es auf der Seite Z702 gespeichert ist. So könnte es aussehen (man beachte, dass die dreistelligen ZIDs, die mit Z7XX beginnen, nur Platzhalter sind, bis wir die richtigen ZIDs für sie in Wikifunctions haben).

{
  "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"
      }
    ]
  }
}

Alle auf Wikifunctions gespeicherten JSON-Objekte werden in der Unicode-Normalisierungsform C gespeichert. Alle Werte müssen vor der Auswertung außerdem Unicode-normalisiert in die Unicode-Normalform C sein.

Das Z2/Persistentes Objekt stellt Metadaten für das ZObjekt zur Verfügung, das in den Z2K2/Wert eingebettet ist.

Das Z2K3/Bezeichnung ist ein ZObjekt vom Typ Z12/multilingualer Text, das einen Z3/Schlüssel und Z12K1/Texte hat, die auf eine Z10/Liste von Z11/monolingualer-Text-ZObjekten zeigen (man denke daran, dass eine Z10/Liste in der JSON-Darstellung als Array dargestellt wird). Die Bezeichnung ermöglicht die Benennung.

Es gibt weitere Z3/Schlüssel auf Z2/Persistentes Objekt, die Metadaten zur Dokumentation, zur Darstellung im Wiki und zur besseren Auffindbarkeit bereitstellen. Sie sind alle auf Z2/Persistentes Objekt definiert.

Z9/Referenzen

Eine Z9/Referenz ist eine Referenz auf den Z2K2/Wert des ZObjekts mit der angegebenen ID und bedeutet, dass dieser Z2K2/Wert die gleiche Bedeutung wie die Referenz hat. Um ein Beispiel zu nennen, sei folgende Referenz gegeben:

"two"
"Z702"

Das Element ist eine Z9/Referenz-Kurzform, die in der Langform so aussehen würde (wie im Abschnitt über Deserialisierung erläutert):

{
  "type": "reference",
  "reference id": "two"
}
{
  "Z1K1": "Z9",
  "Z9K1": "Z702"
}

Und da es sich um eine Referenz handelt, ist diese durch den Z2K2/Wert des Z2/Persistentes Objekt mit der ZID Z702 (wie oben angegeben) zu ersetzen, d. h. es würde wie folgt aussehen:

{
  "type": "natural number",
  "base 10 representation": "2"
}
{
  "Z1K1": "Z10",
  "Z10K1": "2"
}

Alle drei JSON-Darstellungen in diesem Abschnitt haben für Wikifunctions dieselbe Bedeutung. Sie beziehen sich alle auf die natürliche Zahl 2.

Man beachte, dass wenn eine Z8/Funktion einen Argumenttyp Z2/Persistentes Objekt hat, dann wird anstelle des Z2K2/Wertes das Z2/Persistentes Objekt selbst eingesetzt.

Z4/Typen

Typen sind ZObjekte vom Typ Z4/Typ. ZObjekte eines Typs werden als Instanzen dieses Typs bezeichnet. So war Z702/two, die wir oben gesehen haben, eine Instanz des Typs Z10/positive integer.

Ein Typ sagt uns, wie ein Wert zu interpretieren ist. Ein Typ bietet uns die Möglichkeit, die Gültigkeit eines ZObjekts dieses Typs zu überprüfen. Ein Typ deklariert normalerweise die für seine Instanzen verfügbaren Schlüssel und eine Funktion, die zur Validierung der Instanzen verwendet wird.

Hier ist der Typ für natürliche Zahlen (vereinfacht, da die Kurzbeschreibung und Aliasse und alle außer die ersten drei Schlüssel auf Z4/Typ übersprungen werden).

{
  "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"
      }
    ]
  }
}

Um den Kern des Typs besser sichtbar zu machen, betrachten wir nur den Z4/Typ und entfernen die Bezeichnung:

{
  "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"
}

Der Typ Z10/natürliche Zahl definiert in Z4K2/Schlüssel die neue Z3/Schlüssel-Z10K1/Dezimal-Darstellung, die wir oben in der Instanz für die Zahl 2 verwendet hatten.

Z4K3/Validierer zeigt auf eine Z8/Funktion, die eine Instanz als Argument nimmt und entweder einen Fehler oder den Wert selbst zurückgibt, wenn es keinen Fehler gibt. Wenn keine Fehler zurückgegeben werden, hat die Instanz die Validierung bestanden. Im gegebenen Fall würde die Z8/Funktion die folgenden Prüfungen durchführen:

  • Es gibt neben dem Z1K1/Typ auf der Instanz nur einen einzigen Schlüssel, die Z10K1/Dezimal-Darstellung.
  • Der Wert der Dezimaldarstellung hat den Typ Z6/String.
  • Die Dezimaldarstellung enthält nur Ziffern.
  • Die Dezimaldarstellung beginnt nicht mit einer 0, es sei denn, sie hat eine Länge von 1.

Man beachte, dass alle diese Überprüfungen durch Z8/Funktionen durchgeführt werden, die von Mitwirkenden bereitgestellt werden, und dass alle Typen von Mitwirkenden definiert und geändert werden können. Es ist nichts in Stein gemeißelt bezüglich des Zahlentyps, den wir hier verwenden.

Eine Instanz kann Schlüssel verwenden, die nicht für den Typ definiert sind. Es liegt an der Validierungsfunktion, dies zuzulassen oder nicht. Zum Beispiel verwenden Instanzen von Z7/Funktionsaufruf oft Schlüsssel, die nicht beim Z7/Funktionsaufruf definiert sind, wie im Abschnitt über Z7/Funktionsaufrufe zu sehen ist. Bei den meisten Validierungsfunktionen wird jedoch erwartet, dass alle Schlüssel definiert sind.

Aber ein paar Dinge sind definitiv festgelegt, wie z. B. das Verhalten von Z7/Funktionsaufruf. Mehr dazu später.

Z3/Schlüssel

Bei den meisten Validierungsfunktionen wird jedoch erwartet, dass alle Schlüssel definiert sind. Wenn ihnen eine ZID vorangestellt ist, werden sie als Globale Schlüssel und andernfalls als Lokale Schlüssel bezeichnet. Zum Beispiel sind die folgenden zwei Darstellungen gleichwertig.

{
  "Z1K1": "Z7",
  "Z7K1": "Z781",
  "Z781K1": "Z702",
  "Z781K2": "Z702"
}
{
  "Z1K1": "Z7",
  "Z7K1": "Z10000",
  "K1": "Z702",
  "K2": "Z702"
}

Globale Schlüssel sind Namensargumente, während lokale Schlüssel Positionsargumente sind.

  • Als Faustregel gilt, dass man, wann immer möglich, globale Schlüssel verwenden sollte.
  • Der Hauptanwendungsfall für lokale Schlüssel ist, wenn eine Z8/Funktion oder ein Z4/Typ ad hoc erstellt wird und daher keine globalen Schlüssel haben kann, da die erstellte Z8/Funktion oder der Z4/Typ selbst nicht persistent ist.

Ein globaler Schlüssel ist immer für das ZObjekt definiert, auf das sich der ZID-Teil seiner ID bezieht.

Z8/Funktionen

In der Definition von Z10/natürliche Zahl haben wir einen ersten Hinweis auf eine Z8/Funktion gesehen, Z110/validate positive integer. Hier werden wir eine viel einfachere Funktion verwenden, Z781/add. Z781/add ist eine Z8/Funktion, die zwei Z10/natürliche Zahlen annimmt und eine Z10/natürliche Zahl zurückgibt.

Wir zeigen nur den Wert an.

{
 "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"
}

Um übersichtlich zu bleiben, haben wir die Z17K3/Bezeichnungen aus den Z17/Argument-Deklarationen entfernt, die über Z17K2/Schlüssel-IDs identifiziert werden. Aber genau wie die Z3/Schlüssel auf Z4/Typen haben sie Bezeichnungen in allen unterstützten Sprachen. Die Schlüssel sind global, wenn die Z8/Funktion persistent, und lokal, wenn sie transient ist.

Die Funktion wird durch die (weggelassene) Dokumentation spezifiziert, aber auch durch die Z8K3/Tests und die Z8K1/Typ-Deklarationen zu den Argumenten und zum Z8K2/Wiedergabetyp. Da darüber hinaus eine Funktion mehrere Z8K4/Implementierungen haben kann, bestätigen sich die Implementierungen gegenseitig.

Z8/Funktionen dürfen keine zustandsändernden Nebeneffekte haben.

Z7/Funktionsaufrufe

Das folgende ZObjekt stellt einen Funktionsaufruf dar. In der zweiten Zeile sehen wir eine kompaktere Darstellung des Funktionsaufrufs, die eine Syntax verwendet, die für Funktionsaufrufe vertrauter ist.

{
  "type": "function call",
  "function": "add",
  "left": "two",
  "right": "two"
}
{
  "Z1K1": "Z7",
  "Z7K1": "Z781",
  "Z781K1": "Z702",
  "Z781K2": "Z702"
}
add(two, two) Z781(Z702, Z702)

Wenn man Literale anstelle von persistenten ZObjekten für die Argumente verwendet, würde dies wie folgt aussehen.

  • Beachte, dass wir die Literale mit dem Z10/positive integer als Konstruktor erstellen.
  • Alle Z4/Types können auf diese Weise aufgerufen werden, wobei für jeden ihrer Schlüssel ein Wert bereitgestellt wird.
  • Dies ist kein Z7/Funktionsaufruf, sondern eine Notation für das Objekt des betreffenden Z4/Typs.
{
  "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">)

Wenn dieser Z7/Funktionsaufruf ausgewertet wird, ergibt sich erwartungsgemäß die Nummer vier.

{
  "type": "natural number",
  "base 10 representation": "4"
}
{
  "Z1K1": "Z10",
  "Z10K1": "4"
}
natural number<"4"> Z10<"4">

Die Auswertung wird wiederholt auf das Auswerteergebnis angewendet, bis ein Fixpunkt erreicht ist.

Z14/Implementierungen

Jede Z8/Funktion kann eine Anzahl verschiedener Z14/Implementierungen haben. Es gibt drei Haupttypen von Z14/Implementierungen: Built-Ins, Z16/Code oder durch Komposition anderer Z8/Funktionen.

Nehmen wir die Z781/add-Funktion und betrachten wir fünf verschiedene Z14/Implementierungen.

Built-In-Implementierungen

Eine Built-In-Implementierung teilt der Laufzeit-Auswertefunktion mit, dass sie ein entsprechendes Auswertungsergebnis zurückgeben soll. Built-Ins sind der Auswertefunktion hart kodiert. Z14K4/builtin bezieht sich auf die hartkodierte Built-In-ID (die die ZID des Z2/Persistent-Objekts sein muss).

{
  "type": "implementation",
  "implements": "add",
  "builtin": "Z791"
}
{
  "Z1K1": "Z14",
  "Z14K1": "Z781",
  "Z14K4": "Z791"
}

Eine Auswertefunktion würde alle ihre eigenen Built-Ins kennen und könnte sie nach Belieben verwenden. Beachte, dass Addition keine Funktion wäre, die ein Bulit-In erhalten würde. Dies dient hier lediglich der Veranschaulichung.

Z16/Code

Eine Implementierung in Z16/Code stellt einen Code-Schnipsel in einer bestimmten Programmiersprache dar.

{
  "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"
  }
}

Die Auswertefunktion würde wissen, wie die gegebenen ZObjekte, die die Argumente repräsentieren, in die unterstützten Programmiersprachen umzuwandeln sind, wie der bereitgestellte Codeschnipsel auszuführen ist und wie das Ergebnis dann wieder in ein ZObjekt umgewandelt werden kann, das das Ergebnis repräsentiert.

Schließlich würde die Übersetzung von ZObjekten in die nativen Werte der unterstützten Programmiersprachen innerhalb von Wikifunctions selbst gehandhabt werden (was ein neues Design-Dokument erfordern wird). Bis dahin unterstützen wir Z16/Code nur für Argumente und Rückgabetypen, die von der Auswertefunktion hartkodiert unterstützt werden.

Z46/Deserialisierer

Ein Z46/Deserialisierer nimmt ein ZObjekt eines bestimmten Typs und wandelt es in einen Wert für eine bestimmte Programmiersprache um.

Die folgenden Z46/Deserialisierer nehmen beispielsweise ein ZObjekt vom Typ Z10/Natürliche Zahl und wandeln es in einen JavaScript-BigInt-Wert um.

{
  "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"
}

Der Z46K4/nativer Typ gibt an, zu welchem ​​Typ der Deserialisierer führen wird. Das ermöglicht es uns, natürliche Implementierungen wie oben für die Addition zu verwenden.

Z64/Serialisierer

Die umgekehrte Operation eines Z46/Deserialisierers ist der Z64/Serialisierer. A Z64/Serialisierer nimmt einen Wert in der angegebenen Programmiersprache und wandelt ihn in ein ZObjekt des angeforderten Typs um.

{
  "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"
}

Komposition

Die portabelste (aber oft auch die langsamste) Z14-Implementierung wird durch Komposition anderer Z8-Funktionen erreicht.

Wir zeigen sowohl das ZObjekt der Implementierung als auch eine einfacher zu lesende Notation, die auf der Funktionsaufrufsyntax basiert.

{
 "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(
  is zero(right),
  left,
  add(
    successor(left),
    predecessor(right)
  )
)
Z802(
  Z782(Z781K2),
  Z781K1,
  Z781(
    Z783(Z781K1),
    Z784(Z781K2)
  )
)

Diese Komposition beruht auf einer Anzahl anderer Z8/Funktionen: Z782/ist Null, Z783/Nachfolger, Z784/Vorgänger, Z801/if, und interessanterweise — es selbst. Es ist für eine Z14/Implementierung vollständig OK, ihre eigene Z8/Funktion rekursiv aufzurufen. Beachte aber, dass die Auswertefunktion die Z14/Implementierung nicht rekursiv aufrufen muss - einer Auswertefunktion steht es frei, bei jedem Rekursionsschritt eine beliebige Implementierung zu wählen.

Das ist alles andere als schnell - aber es erlaubt uns, einen gut verstandenen Formalismus und eine sehr einfache Implementierung davon zu verwenden, um sicherzustellen, dass die anderen Implementierungen von Z781/add korrekt sind - zugegebenermaßen ist dies wahrscheinlich von geringerem Interesse für die Addition, aber wir können uns vorstellen, dass es Z8/Funktionen gibt, die offensichtlich korrektere und viel schlauere und schnellere Implementierungen haben. Wikifunctions kann diese Implementierungen gegeneinander kreuztesten und uns so ein gewisses Gefühl der Sicherheit in Bezug auf ihre Richtigkeit geben.

Beispielauswertung

Im folgenden werten wir die obige Komposition aus. Wir beginnen mit dem folgenden Z7/Funktionsaufruf (wir bleiben aufgrund der Kürze nur bei der funktionalen Syntax).

add(Natural number<"2">, Natural number<"2">)
Z781(Z10<"2">, Z10<"2">)

Wir ersetzen den Funktionsaufruf durch die oben angegebene Komposition und ersetzen die Argumente durch die angegebenen Werte. Daraus ergibt sich der folgende 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">)
  )
)

Wir werten Z782/ist Null(Z10/Natürliche Zahl<"2">) zum Z40/Boolescher Wert von Z42/falsch aus (da 2 nicht Null ist). Das ergibt:

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">)
  )
)

Dadurch können wir den Aufruf von Z802/wenn durch Z802K3/alternativ ersetzen, da die Z802K1/Bedingung falsch ist. Das ergibt:

add(
  successor(Natural number<"2">),
  predecessor(Natural number<"2">)
)
Z781(
  Z783(Z10<"2">),
  Z784(Z10<"2">)
)

Die Funktion Z783/Nachfolger fügt zu einer Zahl einfach eins hinzu, und die Funktion Z784/Vorgänger entfernt eins. Jede dieser Funktionen kann im Code oder auf andere Weise implementiert sein oder auch nicht. Das spielt keine Rolle. Wenn wir diese beiden Funktionsaufrufe ersetzen, kommen wir zu folgendem Aufruf:

add(
  Natural number<"3">,
  Natural number<"1">
)
Z781(
  Z10<"3">,
  Z10<"1">
)

Auch hier ersetzen wir den Aufruf von Z781/hinzufügen durch seine Komposition und ersetzen die Argumente durch die neuen Werte. Das ergibt:

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">)
  )
)

Wir prüfen erneut, ob der gegebene Wert Z782/Null ist (das ist er nicht, er ist Eins). Also ersetzen wir den Aufruf von Z782/ist Null wieder durch Z42/falsch.

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">)
  )
)

Da die Z802K1/Bedingung erneut falsch ist, ersetzen wir den Aufruf von Z802/wenn durch die Z802K3/Alternative.

add(
  successor(Natural number<"3">),
  predecessor(Natural number<"1">)
)
Z781(
  Z783(Z10<"3">),
  Z784(Z10<"1">)
)

Auch hier ersetzen wir die Funktionsaufrufe an Z783/Nachfolger und Z784/Vorgänger durch die jeweiligen Ergebnisse, eine Zahl mehr, eine Zahl weniger.

add(
  Natural number<"4">,
  Natural number<"0">
)
Z781(
  Z10<"4">,
  Z10<"0">
)

Wir sind nun wieder an dem Punkt angelangt, an dem wir den Aufruf von Z781/hinzufügen durch dessen Komposition ersetzen. Das ergibt:

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">)
  )
)

Der Aufruf von Z782/ist Null hat nun das Argument Z10/natürliche Zahl<"0"> erhalten, das tatsächlich Null ist. Der Aufruf von Z782/ist Null führt also zu Z41/wahr. Das ergibt:

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">)
  )
)

Der Funktionsaufruf Z802/wenn hat jetzt eine Z41/wahre Z802K1/Bedingung, was bedeutet, dass wir den gesamten Aufruf durch die Z802K2/Konsequenz und nicht durch die Z802K3/Alternative ersetzen. Das ergibt:

Natural number<"4">
Z10<"4">

Dies ist ein Fixpunkt, d. h. er ändert sich bei der Auswertung nicht und ist somit das Ergebnis unseres Funktionsaufrufs.

2 und 2 ist 4.

Auswertungsreihenfolge

Die Reihenfolge der Auswertung ist dem Auswerter überlassen. Da alle Z8/Funktionen keine Nebenwirkungen haben dürfen, wird dies immer zum gleichen Ergebnis führen. Eine unkluge Auswertestrategie kann aber dazu führen, dass viel mehr gerechnet wird als nötig oder dass der Auswerter gar nicht zu Ende kommt. Z722/add recursive liefert uns ein Beispiel, das in einer Endlosschleife enden könnte, wenn wir eine vollständige Auswertungsreihenfolge versuchen:

Für den Aufruf von Z802/if in Z722/add recursive wäre es unklug, zuerst alle drei Argumente auszuwerten und dann entweder das zweite oder das dritte Argument zurückzugeben. Abhängig vom ersten Argument Z802K1/Bedingung brauchen wir nur entweder Z802K2/konsequent oder Z802K3/alternative zurückzugeben. Es wird nie der Fall auftreten, dass wir sowohl das zweite als auch das dritte Argument auswerten müssen.

In der Tat könnten wir sogar das zweite oder dritte Argument unausgewertet zurückgeben. Denke daran, dass die Auswertefunktion ohnehin jedes Ergebnis erneut auswertet, bis ein Fixpunkt erreicht ist. Z802/if kann also locker implementiert werden, den irrelevanten Zweig weglassen und den relevanten Zweig als unbewertetes ZObjekt zurückgeben.

Eine lockere Auswertungsstrategie wird im Allgemeinen empfohlen, aber wenn der Auswerter z. B. eine Z16/Code-basierte Implementierung verwenden möchte, ist dies möglicherweise nicht machbar. Und dann könnte der Auswerter entscheiden, zuerst die Argumente und dann den äußeren Aufruf auszuwerten. Schließlich gibt es Möglichkeiten, mit verschiedenen Auswertungsstrategien zu experimentieren.

Z20/Tests

Z20/Tests sind ZObjekte, die einen Z20K2/Aufruf aufrufen und dann das Ergebnis mit Hilfe des Z20K3/Validierers prüfen. Z20K3/Validierer ist ein unvollständiger Z7/Funktionsaufruf, der das Ergebnis des Z20K2/Aufrufs als erstes Argument eingefügt erhält. Wenn der Z20K3/Validierer ein Z54/true zurückgibt, dann ist der Z20/Test bestanden, andernfalls durchgefallen.

Tests werden benutzt, um sicherzustellen, dass alle Z14/Implementierungen sich wie gewollt verhalten. Sie sollten ähnlich wie Unit Tests betrachtet werden. Eine Z8/Funktion sollte alle Z20/Tests auflisten, die zur Sicherstellung der Vorgabenkonformität einer Z14/Implementierung bestanden werden müssen. Zusätzlich können die verschiedenen Z14/Implementierungen gegeneinander auf Konsistenz getestet werden.

{
 "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 diesem Fall werten wir zuerst den Z20K2/Aufruf aus, der Z781/hinzufügen(Z702/Zwei, Z702/Zwei) ist, was zu Z10/Natürliche Zahl<"4"> führt. Dies wiederum wird dann im Z20K3/Ergebnisvalidierer verwendet, wo es als erstes Argument eingefügt wird, was zu Z788/Gleichheit natürlicher Zahlen(Z10/Natürliche Zahl<"4">, Z704/Vier) führt. Dieser Aufruf sollte zu Z41/wahr führen und daher sollte Z20/Tester erfolgreich sein.

Generische Typen

Ein generischer Typ wird durch einen Z7/Funktionsaufruf einer Z8/Funktion realisiert, die einige Argumente entgegennimmt und einen Z4/Typ zurückgibt.

Beispielsweise ist vielleicht ein Z882/typisiertes Paar eine Funktion mit zwei Z4/Typen als Argumenten, einem für das erste und einem für das zweite Element, die einen Inline-Z4/Typ zurückgibt. Wenn wir also ein Paar von Z10/natürliche Zahlen erzeugen wollen, würden wir Z882/typed pair(Z10/Natural number, Z10/Natural number) aufrufen und das Ergebnis wäre ein Z4/Typ, den wir für das Z1K1-Feld eines ZObjekts verwenden können.

{
 "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"
}

Das Ergebnis des Z7/Funktionsaufrufs ist ein dynamisch erzeugter Z4/Typ, was sicherstellt, dass die beiden Elemente des Paares den richtigen Z4/Typ haben. Das Ergebnis dieses Z7/Funktionsaufrufs sieht so aus:

{
 "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"
}

Dies erklärt auch das Z4K1/Identitätsfeld auf Z4/Typ: Es beschreibt, wie der Z4/Typ erstellt wurde, und ermöglicht uns den Zugriff auf die Argumente, die für die Typ-Erstellung verwendet wurden. Diese Information deklarativ zu halten ist sehr hilfreich, um einen Funktionsaufruf statisch zu validieren und um Typen zu vergleichen.

Wenn wir ein Z882/typisiertes Paar wollen, das den Z4/Typ eines oder beider seiner Elemente nicht einschränkt, könnte man die Z882/typisiertes-Paar-Funktion mit Z1/ZObjekt als einem oder beiden Argumenten aufrufen.

Z881/Listen

Hier ist eine Liste mit zwei Zeichenketten:

[
 "string",
 "a",
 "b"
]
[
 "Z6",
 "a",
 "b"
]

Wenn wir sie in ZObjekte umwandeln, sieht sie wie folgt aus:

{
 "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"
 }
}

Ein JSON-Array-Literal beginnt immer mit dem Typ, der für die Z881/typisierte Liste genutzt wird. Dies ist nicht das erste Element der Liste, sondern der Typ für die typisierte Liste. Diese Arrays werden Benjamin-Arrays genannt. Wenn wir eine nicht typisierte Liste haben wollen, würden wir Z1/Objekt als Argument nutzen. Eine nicht typisierte leere Liste würde wie folgt aussehen.

[
 "object"
]
[
 "Z1"
]

Z22/Auswertungsergebnis

Ein in Wikifunctions ausgeführter Z7/Funktionsaufruf gibt immer ein Objekt vom Typ Z22/Auswertungsergebnis zurück.

Ein Auswertungsergebnis-Objekt enthält immer den von der Ausführung eines Funktionsaufrufs zurückgegebenen Wert und eine Sammlung von Metadaten, die während der Auswertung erfasst wurden. Hier ist ein Beispiel für eine erfolgreiche Antwort:

{
    "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"
            }
        ]
    }
}

Wenn die Auswertung nicht erfolgreich ist, enthält das Antwortfeld Z24/leer, während das Metadatenfeld einen "Fehler"-Schlüssel mit den Einzelheiten des Fehlers enthält. Dies ist ein Beispiel für ein fehlgeschlagenes Auswertungsergebnis-Objekt:

{
    "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"
                    }
                }
            }
        ]
    }
}

Diese Beispiele sind lediglich komprimierte Versionen der echten Auswertungsergebnis-Objekte und enthalten nur einen Beispielschlüssel im Metadaten-Feld. In echten Beispielen gibt die Metadatensammlung alle von den Backend-Diensten gesammelten Metriken zurück, einschließlich der Dauer des Laufs, der CPU-Auslastung und der Speicherauslastung.

Eine ausführlichere Beschreibung aller möglichen Metadaten, die im Auswertungsergebnis-Objekt zurückgegeben werden, findet du im Leitfaden zu Funktionsaufruf-Metadaten auf Mediawiki.

Z5/Fehler

Ein Z7/Funktionsaufruf kann zu einem Z5/Fehler führen. Dies passiert, wenn der Funktionsaufruf nicht nicht korrekt ausgeführt werden kann und er unbehebbar ist (z.B. Division durch Null oder Fehlschlagen aufgrund von mangelndem Speicher).

Z5 ist ein generischer Typ. Jede Instanz von Z5 verweist auf die ZID eines Fehlertyps (in Z5K1/Fehlertyp), und dieser Fehlertyp bestimmt den Typ von Z5K2/Fehlerwert und die darin vorhandenen Schlüssel. Jeder Fehlertyp ist eine Instanz von Z50/Fehlertyp und die ZIDs Z500–Z599 sind für Fehlertypen reserviert.

Weitere Informationen sind unter Abstrakte Wikipedia/Darstellung von Fehlern verfügbar.

Z99/Zitat

Z99/Zitat wird als Wrapper um ein anderes ZObjekt verwendet, um anzugeben, dass es nicht ausgewertet ("aufgelöst") werden soll. (Dies ähnelt dem Zitieren in Lisp.) Z99/Zitat hat einen einzigen Schlüssel, Z99K1/Zitierung, vom Typ Z1/Objekt.

Zur Veranschaulichung werden einige Teile von Fehlerobjekten (Instanzen von Z5/Fehler) zitiert, wenn sie während der Ausführung eines Funktionsaufrufs erstellt werden. Beispielsweise umfasst ein Fehler vom Typ Z507/Fehler in der Auswertung eine Kopie des gesamten Funktionsaufrufs, dessen Ausführung den Fehler verursacht hat (als Wert von Z507K1/Funktionsaufruf). Da dieser Funktionsaufruf höchstwahrscheinlich in irgendeiner Weise fehlerhaft ist, stellen wir sicher, dass kein weiterer Versuch unternommen wird, ihn auszuwerten, indem wir ihn innerhalb des Fehlerobjekts zitieren. (Daher wird der Typ von Z507K1 als Z99 deklariert und sein Wert wird immer zitiert.)

Wir verwenden die folgenden Richtlinien für die Nutzung von Z99/Zitat:

  1. Zitiere ein ZObjekt, wenn wir glauben, dass es in irgendeiner Weise ungültig sein könnte.
  2. Zitiere Z1K1 jedoch nicht allein. Wenn Zweifel an seinem Wert bestehen, zitiere das gesamte Objekt, das ihn enthält.
  3. Zitiere auflösbare Schlüssel (Schlüssel, deren Werte Instanzen von Z7, Z9 oder Z18 enthalten), die möglicherweise fälschlicherweise als Eingabe für eine Funktion erfasst werden.
  4. Zitieren beim Auflösen eines Werts kann zu einer Katastrophe führen (z. B. unendliche Rekursion).

Hinweis: Da sich unsere Lösungsstrategie weiterentwickelt, ist es möglich, dass (3) und (4) unnötig werden.

Nicht-funktionale Funktionen

Keine Z8/Funktion darf Nebenwirkungen haben. Alle Z8/Funktionen müssen funktional sein. D.h. sie müssen den gleichen Wert zurückgeben, wenn sie mit den gleichen Argumenten aufgerufen werden. Das bedeutet, dass Z8/Funktionen wie "Rückgabe einer Zufallszahl" oder "Rückgabe der aktuellen Uhrzeit" nicht möglich sind.

This might change in the future. Dies wird in einem späteren Dokument behandelt.

Zx/Summentypen

Ein besonders nützlicher generischer Typ ist der Typ Zx/Summe, der eine Liste von Z4/Typen annimmt und einen Z4/Typ zurückgibt, der genau eine Instanz eines beliebigen der angegebenen Typen annehmen kann.

Dadurch werden auch nicht benötigte Parameter in Funktionsaufrufen ermöglicht.

Dies wird in einem späteren Dokument behandelt.

Ein paar Fragen und zu erledigende Aufgaben

  • Brauchen wir irgendwo am Anfang "muss/optional" für Schlüssel? — Nein.
  • Voreinstellungen auf Z3/Schlüssel durch Zx/Summe ersetzen? (Oder zumindest mit Z17/Argumentendeklaration konsistent machen)
  • Könnte auf später aufgeschoben werden, falls wir die Voreinstellung auf Z3 im Moment nicht brauchen.
  • Darauf hinweisen, dass alles Unicode ist und dass es sich bei allen um die von MediaWiki geforderte Normalisierung handelt
  • Das Intro neuschreiben, um mit der Normalform zu beginnen und dann zu kanonisieren

Siehe auch