Werte
DXN hat 28 Datentypen in vier Gruppen: Skalare, Sammlungen, Zeitwerte und erweiterte Typen. Jeder hat genau eine Textschreibweise und eine Binärkodierung.
Auf einen Blick
| Typ | Text |
|---|---|
| nil, boolean | nil true false |
| Ganzzahl, Gleitkomma | -17 3.14 5e-3 NaN -Infinity |
| Dezimalzahl, Bruch | 19.99M 22/7 |
| Zeichenkette, Zeichen | "text\n" ?a ?s |
| Symbol, Keyword | Point ns/name :ok name: |
| Liste, Tupel, Array | [1 2] {:ok 200} @array[1 2] |
| Map, geordnete Map | %{a: 1} %{"k" => 1} @ordered %{…} |
| Menge, sortierte Menge | @{1 2} @sorted-set @{3 1 2} |
| struct | %Point{x: 1 y: 2} %Point[1 2] |
| Datum, Zeit, Zeitstempel | ~D[2024-01-01] ~T[12:30:00] ~U[2024-01-01 12:30:00Z] |
| Datum mit Offset, Dauer | @datetime "2024-01-01T12:30:00+02:00" @duration "P1Y2M10D" |
| UUID, URI, Bytes, Regex | @uuid "…" @uri "…" @bytes "SGVsbG8=" ~r/^a+$/i |
| eigenes Tag | @my-app/money 500 |
Zahlen
Vier Arten, jede genau in dem, was sie ist:
- Ganzzahl — ein optionales
-und Ziffern, mit beliebiger Genauigkeit. Es gibt kein+, kein Hex und keine Zifferntrenner. - Gleitkommazahl — ein IEEE-754-Double, mit Nachkommastellen, Exponent oder beidem.
NaN,Infinityund-Infinitysind Literale;NaNhat kein Vorzeichen. - Dezimalzahl — Ziffern mit nachgestelltem
M, exakte Festkommazahl.19.99Mbleibt 19,99, im Text wie binär. - Bruch —
Zähler/Nenner, gespeichert wie geschrieben:22/7und44/14sind verschiedene Werte. Ein Nenner von null ist ein Fehler.
[42 -17 12345678901234567890123 3.14 1e3 NaN -Infinity 19.99M -1/3]
Zeichenketten und Zeichen
Zeichenketten sind UTF-8 in doppelten Anführungszeichen und dürfen über Zeilen reichen. Die Escapes sind \", \\, \n, \t, \r, \0, \a, \b, \f, \v und \x{…} mit beliebig vielen Hex-Ziffern für einen Codepoint. Eine Form mit einfachen Anführungszeichen und Interpolation gibt es nicht.
Ein Zeichen ist ? mit einem Codepoint — ein eigener Wert, keine Zeichenkette aus einem Buchstaben. ?s ist das Leerzeichen, und Escapes funktionieren auch nach ?.
["tab\there, smile \x{1F600}" ?a ?s ?\n ?é]Symbole und Keywords
Ein nackter Bezeichner ist ein Symbol: ein Name, der auf etwas verweist — einen Typ, einen Handler, ein Schema — und nie ausgewertet wird. Ein Keyword ist ein Name als Wert, etwa ein Status oder Modus: :ok, oder :"mit Leerzeichen", wenn es kein einfacher Bezeichner ist. In einer Map ist name: vor einem Wert ein Keyword-Schlüssel.
%{
handler: billing/invoice-created # a symbol
status: :ok # a keyword
label: :"needs review"
}Sammlungen
| Form | Typ | Bedeutung |
|---|---|---|
[…] | Liste | eine geordnete Folge, die wachsen darf |
{…} | Tupel | eine feste Gruppe — ein Paar, ein Ergebnis, ein Datensatz |
@array[…] | array | eine Folge fester Größe mit Indexzugriff |
%{…} | map | Schlüssel auf Werte, ohne festgelegte Reihenfolge |
@ordered %{…} | geordnete Map | eine Map, deren Reihenfolge zu ihrer Identität gehört |
@{…} | Menge | verschiedene Werte, ungeordnet |
@sorted-set @{…} | sortierte Menge | eine stets sortierte Menge |
Ein Map-Eintrag ist entweder name: wert für Keyword-Schlüssel oder schlüssel => wert für Schlüssel beliebigen Typs. Beide Stile lassen sich mischen. Eine einfache Map verspricht keine Reihenfolge, auch wenn ihre Einträge nacheinander stehen; wo die Reihenfolge zählt, sagt @ordered das ausdrücklich.
%{
name: "api"
"Content-Type" => "application/json"
404 => :not-found
{52.52 13.40} => "Berlin"
}@sorted-set @{3 1 2}@sorted-set @{1 2 3}Structs
Ein Struct ist ein benannter Datensatz, geschrieben mit Schlüsseln — Feldnamen im Text, in beliebiger Reihenfolge — oder nach Position, mit den Werten in der Reihenfolge, die das Schema festlegt. Beide Formen sind derselbe Wert.
%Point{y: 2, x: 1}
%Point[1, 2]Ein Struct lässt sich ohne Schema lesen: Ein Leser, der Point nicht kennt, liefert einen undurchsichtigen Wert mit dem Namen und den Feldern, wie sie geschrieben stehen. Mit einem Schema kann er die Felder prüfen, zwischen beiden Formen umwandeln und den Feldern ihre Typen geben.
Datum und Uhrzeit
| Form | Typ | Bedeutung |
|---|---|---|
~D[2024-01-01] | Datum | ein Kalenderdatum |
~T[12:30:00.5] | Zeit | eine Uhrzeit, bis auf Mikrosekunden |
~U[2024-01-01 12:30:00Z] | Zeitstempel | ein Zeitpunkt in UTC — immer mit Z |
@datetime "2024-01-01T12:30:00+02:00" | datetime | ein Zeitpunkt mit einem UTC-Offset ungleich null |
@duration "P1Y2M10DT2H30M" | Dauer | eine Dauer nach ISO 8601 |
UTC-Zeitpunkte und solche mit Offset unterscheiden sich schon in der Syntax: Das Sigil steht für UTC, das Tag für alles andere. Eine Dauer hält Jahre, Monate, Wochen, Tage, Stunden, Minuten und Sekunden als eigene Felder, weil ein Monat oder ein Jahr keine feste Länge hat und sich nicht in eine Sekundenzahl falten lässt.
UUIDs, URIs, Bytes und Regexes
@uuid "…"nimmt die 36-stellige Form mit Bindestrichen; alles andere ist ein Fehler.@uri "…"ist eine URI nach RFC 3986, genau so gespeichert, wie sie geschrieben ist.@bytes "…"sind rohe Binärdaten, im Text als Base64, in.dxnbals reine Bytes.~r/muster/flagsist ein regulärer Ausdruck mit den Flagsi m s u x f r— ohne Groß-/Kleinschreibung, mehrzeilig, dotall, Unicode, erweitert, erste Zeile, nicht gierig.
%{
id: @uuid "9b74c989-1e2a-4b17-9b1f-2a6a4a2b6f10"
home: @uri "https://example.com/path"
avatar: @bytes "SGVsbG8="
handle: ~r/^[a-z0-9_]{3,20}$/i
}Eigene Tags
Jeder Tag-Name, der nicht eingebaut ist, ist ein eigenes Tag: ein Wert, verpackt in einen von der Anwendung festgelegten Namen. Ein Leser mit einem Decoder für den Namen macht daraus seinen eigenen Typ; ein Leser ohne behält ihn als undurchsichtigen Wert mit Tag und gibt ihn weiter. Ein Namensraum im Namen verhindert Kollisionen mit fremden Tags.
@my-app/money {1999 :eur}Eigene Tags passen zu einem einzelnen verpackten Wert. Alles mit echten Feldern ist als Struct besser aufgehoben, das ein Schema prüfen kann.