Typen
Ein Typwert ist ein Wert, der andere Werte klassifiziert. Ein Wert, der durch einen Typ klassifiziert wird, wird als konform mit diesem Typ bezeichnet. Das M-Typsystem besteht aus den folgenden Arten von Typen:
Primitiven Typen, die primitive Werte klassifizieren (
binary
,date
,datetime
,datetimezone
,duration
,list
,logical
,null
,number
,record
,text
,time
,type
) und auch einige abstrakte Typen umfassen (function
,table
,any
,anynonnull
undnone
)Datensatztypen, die Datensatzwerte basierend auf Feldnamen und Werttypen klassifizieren
Listentypen, die Listen mithilfe eines einzelnen Elementbasistypen klassifizieren
Funktionstypen, die Funktionswerte basierend auf den Typen ihrer Parameter und Rückgabewerte klassifizieren
Tabellentypen, die Tabellenwerte basierend auf Spaltennamen, Spaltentypen und Schlüsseln klassifizieren
Nullable-Typen, die zusätzlich zu allen von einem Basistyp klassifizierten Werten den Wert NULL klassifizieren
Typentypen, die Werte klassifizieren, bei denen es sich um Typen handelt
Die Gruppe der primitiven Typen umfasst die Typen für primitive Werte und einige abstrakte Typen. Letzteres sind Typen, die nicht eindeutig Werte klassifizieren: function
, table
, any
, anynonnull
und none
. Alle Funktionswerte sind konform mit dem abstrakten Typ function
, alle Tabellenwerte mit dem abstrakten Typ table
, alle Werte mit dem abstrakten Typ any
, alle Werte ungleich NULL mit dem abstrakten Typ anynonnull
und keine Werte mit dem abstrakten Typ none
. Ein Ausdruck vom Typ none
muss einen Fehler auslösen, oder seine Beendigung muss fehlschlagen, da kein Wert erzeugt werden konnte, der mit dem Typ none
konform ist. Beachten Sie, dass die primitiven Typen function
und table
abstrakt sind, da Funktionen bzw. Tabellen nicht direkt von diesem Typ sind. Die primitiven Typen record
und list
sind nicht abstrakt, da sie für einen offenen Datensatz ohne definierte Felder bzw. eine Liste vom Typ „any“ stehen.
Alle Typen, die nicht zur abschließend definierten Gruppe von primitiven Typen gehören, sowie deren Pendants mit NULL-Wert, werden zusammen als benutzerdefinierte Typen bezeichnet. Benutzerdefinierte Typen können mithilfe eines type-expression
(Typausdrucks) geschrieben werden:
type-expression:
primary-expression
type
Primärtyp
Typ:
primary-expression
primary-type
primary-type:
primitive-type
record-type
list-type
function-type
table-type
nullable-type
primitive-type: einer der folgenden Werte
any anynonnull binary date datetime datetimezone duration function list logical
none null number record table text time type
Bei den Namen der primitiven Typen handelt es sich um kontextabhängige Schlüsselwörter, die nur im Typkontext erkannt werden. Durch die Verwendung von Klammern in einem Typkontext wird zurück zur Grammatik des normalen Ausdruckskontexts gewechselt, weshalb das Schlüsselwort „type“ erforderlich ist, um zurück in den Typkontext zu wechseln. Klammern können zum Beispiel verwendet werden, um eine Funktion in einem Typkontext aufzurufen:
type nullable ( Type.ForList({type number}) )
// type nullable {number}
Sie können auch verwendet werden, um auf eine Variable zuzugreifen, deren Name mit dem Namen eines primitiven Typs übereinstimmt:
let record = type [ A = any ] in type {(record)}
// type {[ A = any ]}
Im folgenden Beispiel wird ein Typ definiert, der eine Liste mit Zahlen klassifiziert:
type { number }
Ähnlich wird im folgenden Beispiel ein benutzerdefinierter Typ definiert, der Datensätze mit Pflichtfeldern namens X
und Y
klassifiziert, deren Werte Zahlen sind:
type [ X = number, Y = number ]
Der einem Wert zugeordnete Typ wird mithilfe der Standardbibliotheksfunktion Value.Type abgerufen, wie in den folgenden Beispielen zu sehen:
Value.Type( 2 ) // type number
Value.Type( {2} ) // type list
Value.Type( [ X = 1, Y = 2 ] ) // type record
Der Operator is
wird verwendet, um festzustellen, ob der Typ eines Werts mit einem bestimmten Typ kompatibel ist, wie in den folgenden Beispielen zu sehen:
1 is number // true
1 is text // false
{2} is list // true
Der Operator as
überprüft, ob der Wert mit dem angegebenen Typ kompatibel ist und löst einen Fehler aus, wenn dies nicht der Fall ist. Andernfalls wird der ursprüngliche Wert zurückgegeben.
Value.Type( 1 as number ) // type number
{2} as text // error, type mismatch
Beachten Sie, dass die Operatoren is
und as
nur primitive NULL-Typen als rechten Operanden akzeptieren. M stellt keine Möglichkeiten zum Überprüfen der Konformität von Werten mit benutzerdefinierten Typen bereit.
Ein Typ X
ist mit einem Typ Y
nur dann kompatibel, wenn alle Werte, die mit X
konform sind, auch mit Y
konform sind. Alle Typen sind kompatibel mit dem Typ any
und keine (außer none
selbst) mit dem Typ none
. Der folgende Graph veranschaulicht die Kompatibilitätsbeziehungen. (Die Typkompatibilität ist reflexiv und transitiv. Sie stellt ein Gitter mit dem Typ any
als dem obersten und dem Typ none
als dem untersten Wert dar.) Die Namen von abstrakten Typen sind kursiv.
Die folgenden Operatoren sind für Typwerte definiert:
Operator | Ergebnis |
---|---|
x = y |
Gleich |
x <> y |
Ungleich |
x ?? y |
Coalesce |
Der native Typ von Typwerten ist der intrinsische Typ type
.
Primitive Typen
Typen in der M-Sprache stellen eine getrennte Hierarchie dar, die den Typ any
als Stamm hat, der alle Werte klassifiziert. Jeder M-Wert ist mit genau einem primitiven Untertypen von any
konform. Die abschließend definierte Gruppe von vom Typ any
abgeleiteten primitiven Typen besteht aus den folgenden Typen:
type null
, der den Wert NULL klassifiziert.type logical
, der die Werte „true“ und „false“ klassifizierttype number
, der numerische Werte klassifizierttype time
, der Zeitwerte klassifizierttype date
, der Datumswerte klassifizierttype datetime
, der datetime-Werte klassifizierttype datetimezone
, der datetimezone-Werte klassifizierttype duration
, der Dauerwerte klassifizierttype text
, der Textwerte klassifizierttype binary
, der Binärwerte klassifizierttype type
, der Typwerte klassifizierttype list
, der Listenwerte klassifizierttype record
, der Datensatzwerte klassifizierttype table
, der Tabellenwerte klassifizierttype function
, der Funktionswerte klassifizierttype anynonnull
, der alle Werte außer NULL klassifizierttype none
, der keine Werte klassifiziert
Der Typ „any“
Der Typ any
ist abstrakt und klassifiziert alle Werte in M, und alle Typen in M sind kompatibel mit any
. Variablen vom Typ any
können an alle möglichen Werte gebunden sein. Da any
abstrakt ist, kann dieser Typ nicht Werten zugeordnet werden – das heißt, kein Wert ist direkt vom Typ any
.
Listentypen
Jeder Wert, bei dem es sich um eine Liste handelt, ist konform mit dem intrinsischen Typ list
, bei dem es keine Einschränkungen in Bezug auf die Elemente innerhalb eines Listenwerts gibt.
Listentyp:
{
Elementtyp }
item-type:
Typ
Das Ergebnis der Auswertung eines Listentyps ist ein Listentypwert, dessen Basistyp list
ist.
Die folgenden Beispiele zeigen die Syntax für das Deklarieren von homogenen Listentypen:
type { number } // list of numbers type
{ record } // list of records type
{{ text }} // list of lists of text values
Ein Wert ist konform mit einem Listentyp, wenn der Wert eine Liste ist und jedes Element in diesem Listenwert mit dem Elementtyp des Listentyps konform ist.
Der Elementtyp eines Listentyps stellt eine Begrenzung dar: Alle Elemente einer konformen Liste sind mit dem Elementtyp konform.
Datensatztypen
Jeder Wert, bei dem es sich um einen Datensatz handelt, ist konform mit dem intrinsischen Typ „record“, bei dem es keine Einschränkungen in Bezug auf die Feldnamen oder Werte innerhalb eines Datensatzwerts gibt. Datensatztyp-Werte werden verwendet, um die Gruppe gültiger Namen sowie die Typen von Werten, die diesen Namen zugeordnet sein dürfen, zu begrenzen.
Datensatztyp:
[
Open-Record-Marker ]
[
Field-specification-listopt ]
[
field-specification-list , open-record-marker ]
field-specification-list:
field-specification
Feldspezifikationsfeld-Spezifikationsliste,
field-specification:
optional
opt field-name field-type-specificationopt
field-type-specification:
=
Feldtyp
field-type:
Typ
open-record-marker:
...
Das Ergebnis der Auswertung eines Datensatztyps ist ein Typwert, dessen Basistyp record
ist.
Die folgenden Beispiele zeigen die Syntax für das Deklarieren von Datensatztypen:
type [ X = number, Y = number]
type [ Name = text, Age = number ]
type [ Title = text, optional Description = text ]
type [ Name = text, ... ]
Datensatztypen sind standardmäßig geschlossen. Dies bedeutet, dass in konformen Werten keine zusätzlichen Felder vorkommen dürfen, die nicht in der Feldspezifikationsliste vorhanden sind. Durch das Aufnehmen der Kennzeichnung für offene Datensätze in den Datensatztyp wird der Typ als offen deklariert, wodurch Felder, die nicht in der Feldspezifikationsliste vorhanden sind, zulässig sind. Die folgenden beiden Ausdrücke sind äquivalent:
type record // primitive type classifying all records
type [ ... ] // custom type classifying all records
Ein Wert ist mit einem Datensatztyp konform, wenn der Wert ein Datensatz ist, und jede Feldspezifikation im Datensatztyp erfüllt ist. Eine Feldspezifikation ist erfüllt, wenn einer der folgenden Punkte zutrifft:
Ein Feldname, der mit dem Bezeichner der Spezifikation übereinstimmt, ist im Datensatz vorhanden, und der zugeordnete Wert ist konform mit dem Typ der Spezifikation.
Die Spezifikation ist als optional gekennzeichnet, und im Datensatz wird kein entsprechender Feldname gefunden.
Ein konformer Wert kann nur dann Feldnamen enthalten, die nicht in der Feldspezifikationsliste aufgeführt sind, wenn der Datensatztyp offen ist.
Funktionstypen
Jeder Funktionswert ist mit dem primitiven Typ function
konform, bei dem es keine Einschränkungen in Bezug auf die Typen der formalen Parameter der Funktion oder den Rückgabewert der Funktion gibt. Benutzerdefinierte Funktionstypwerte werden verwendet, um Typeinschränkungen für die Signaturen konformer Funktionswerte festzulegen.
Funktionstyp:
function (
parameter-specification-listopt )
function-return-type
parameter-specification-list:
required-parameter-specification-list
required-parameter-specification-list ,
optional-parameter-specification-list
optional-parameter-specification-list
required-parameter-specification-list:
required-parameter-specification
required-parameter-specification ,
required-parameter-specification-specification-list
required-parameter-specification:
parameter-specification
optional-parameter-specification-list:
optional-parameter-specification
optional-parameter-specification ,
optional-parameter-specification-list
optional-parameter-specification:
optional
Parameterspezifikation
parameter-specification:
parameter-name parameter-type
function-return-type:
assertion
assertion:
as
nullable-primitiver Typ
Das Ergebnis der Auswertung eines Funktionstyps ist ein Typwert, dessen Basistyp function
ist.
Die folgenden Beispiele zeigen die Syntax für das Deklarieren von Funktionstypen:
type function (x as text) as number
type function (y as number, optional z as text) as any
Ein Funktionswert ist konform mit einem Funktionstyp, wenn der Rückgabetyp des Funktionswerts mit dem Rückgabetyp des Funktionstyps kompatibel ist, und jede Parameterspezifikation des Funktionstyps mit dem formalen Parameter an derselben Stelle der Funktion kompatibel ist. Eine Parameterspezifikation ist kompatibel mit einem formalen Parameter, wenn der als Parametertyp angegebene Typ mit dem Typ des formalen Parameters kompatibel ist. Die Parameterspezifikation ist optional, wenn der formale Parameter optional ist.
Die Namen formaler Parameter werden beim Überprüfen der Funktionstypkonformität nicht berücksichtigt.
Wenn Sie einen Parameter als optional implizit angeben, lässt der Typ NULL-Werte zu. Die folgenden erstellen identische Funktionstypen:
type function (optional x as text) as any
type function (optional x as nullable text) as any
Tabellentypen
Tabellentypwerte werden verwendet, um die Struktur von Tabellenwerten zu definieren.
Tabellentyp:
table
Zeilentyp
row-type:
[
Field-specification-listopt ]
Das Ergebnis der Auswertung eines Tabellentyps ist ein Typwert, dessen Basistyp table
ist.
Der Zeilentyp einer Tabelle gibt die Spaltennamen und -typen der Tabelle als geschlossener Datensatztyp an. Damit alle Tabellenwerte mit dem Typ table
konform sind, ist der Zeilentyp vom Typ record
(der leere offene Datensatztyp). Folglich ist der Tabellentyp abstrakt, da kein Tabellenwert über den Zeilentyp des Typs table
verfügen kann (aber alle Tabellenwerte verfügen über einen Zeilentyp, der mit dem Zeilentyp des Typs table
kompatibel ist). Das folgende Beispiel zeigt die Erstellung eines Tabellentyps:
type table [A = text, B = number, C = binary]
// a table type with three columns named A, B, and C
// of column types text, number, and binary, respectively
Ein Tabellentypwert enthält auch die Definition der Schlüssel eines Tabellenwerts. Ein Schlüssel ist eine Gruppe von Spaltennamen. Es kann maximal ein Schlüssel als Primärschlüssel der Tabelle festgelegt werden. (Innerhalb von M haben Tabellenschlüssel keine semantische Bedeutung. Es ist jedoch üblich, dass von externen Datenquellen, z. B. Datenbanken oder OData-Feeds, Schlüssel für Tabellen definiert werden. Power Query verwendet Schlüsselinformationen, um die Leistung erweiterter Funktionen zu verbessern, z. B. quellübergreifender Verknüpfungsvorgänge.)
Die Standardbibliotheksfunktionen Type.TableKeys
, Type.AddTableKey
und Type.ReplaceTableKeys
können verwendet werden, um den Schlüssel eines Tabellentyps abzurufen, einen Schlüssel zu einem Tabellentyp hinzuzufügen bzw. alle Schlüssel eines Tabellentyps zu ersetzen.
Type.AddTableKey(tableType, {"A", "B"}, false)
// add a non-primary key that combines values from columns A and B
Type.ReplaceTableKeys(tableType, {})
// returns type value with all keys removed
Nullable-Typen
Für jeden type T
kann mithilfe von Nullable-Typen eine Nullable-Variante abgeleitet werden:
Nullable-Typ:
nullable
Art
Das Ergebnis ist ein abstrakter Typ, der Werte vom Typ T oder den Wert null
zulässt.
42 is nullable number // true null is
nullable number // true
Die Ascription von type nullable
T reduziert sich auf die Schreibung von type null
oder type
T. (Denken Sie daran, dass nullable Typen abstrakt sind und kein Wert direkt vom abstrakten Typ sein kann.)
Value.Type(42 as nullable number) // type number
Value.Type(null as nullable number) // type null
Die Standardbibliotheksfunktionen Type.IsNullable
und Type.NonNullable
können verwendet werden, um einen Typ auf NULL-Zulässigkeit zu überprüfen und die NULL-Zulässigkeit eines Typs zu entfernen.
Es gilt Folgendes (für jeden type T
):
type T
ist kompatibel mittype nullable T
.Type.NonNullable(type T)
ist kompatibel mittype T
.
Folgendes ist jeweils paarweise äquivalent (für jeden type T
):
type nullable any
any
Type.NonNullable(type any)
type anynonnull
type nullable none
type null
Type.NonNullable(type null)
type none
type nullable nullable T
type nullable T
Type.NonNullable(Type.NonNullable(type T))
Type.NonNullable(type T)
Type.NonNullable(type nullable T)
Type.NonNullable(type T)
type nullable (Type.NonNullable(type T))
type nullable T
Zugeordneter Typ eines Werts
Der einem Wert zugeordnete Typ ist der Typ, mit dem der Wert als konform deklariert ist.
Einem Wert kann mithilfe der Bibliotheksfunktion Value.ReplaceType
ein Typ zugeordnet werden. Diese Funktion gibt entweder einen neuen Wert mit dem zugeordneten Typ zurück oder löst einen Fehler aus, wenn der neue Typ nicht mit dem Wert kompatibel ist.
Wenn einem Wert ein Typ zugeordnet wird, erfolgt nur eine eingeschränkte Konformitätsprüfung:
- Der zugeordnete Typ darf nicht abstrakt sein, keine NULL-Werte zulassen und muss mit dem intrinsischen (nativen) primitiven Typ des Werts kompatibel sein.
- Wenn ein benutzerdefinierter Typ zugeordnet wird, der die Struktur definiert, muss er mit der Struktur des Werts übereinstimmen.
- Für Datensätze: Der Typ muss geschlossen sein, die gleiche Anzahl von Feldern wie der Wert definieren und darf keine optionalen Felder enthalten. (Die Feldnamen und Feldtypen des Typs ersetzen die dem Datensatz derzeit zugeordneten Werte. Vorhandene Feldwerte werden jedoch nicht mit den neuen Feldtypen abgeglichen.)
- Für Tabellen: Der Typ muss die gleiche Anzahl von Spalten wie der Wert definieren. (Die Spaltennamen und Spaltentypen des Typs ersetzen die der Tabelle derzeit zugeordneten Werte. Vorhandene Spaltenwerte werden jedoch nicht mit den neuen Spaltentypen abgeglichen.)
- Für Funktionen: Der Typ muss so viele erforderliche und optionale Parameter wie der Wert definieren. (Die Parameter- und Rückgabeassertionen des Typs sowie seine Parameternamen ersetzen diejenigen, die dem aktuellen Typ des Funktionswerts zugeordnet sind. Die neuen Assertionen haben jedoch keine Auswirkungen auf das tatsächliche Verhalten der Funktion.)
- Für Listen: Der Wert muss eine Liste sein. (Vorhandene Listenelemente werden jedoch nicht mit dem neuen Elementtyp abgeglichen.)
Bibliotheksfunktionen entscheiden sich möglicherweise auf Grundlage der zugeordneten Typen der Eingabewerte dazu, komplexe Typen zu berechnen und diese den Ergebnissen zuzuordnen.
Der einem Wert zugeordnete Typ kann mithilfe der Bibliotheksfunktion Value.Type
abgerufen werden. Beispiel:
Value.Type( Value.ReplaceType( {1}, type {number} )
// type {number}
Typäquivalenz und Kompatibilität
Die Typäquivalenz ist in M nicht definiert. Eine M-Implementierung kann optional ihre eigenen Regeln verwenden, um Übereinstimmungsvergleiche zwischen Typwerten durchzuführen. Der Vergleich von zwei Typwerten auf Gleichheit sollte mit true
ausgewertet werden, wenn sie von der Implementierung als identisch angesehen werden, andernfalls mit false
. In beiden Fällen muss die zurückgegebene Antwort konsistent sein, wenn die gleichen beiden Werte wiederholt verglichen werden. Beachten Sie, dass innerhalb einer bestimmten Implementierung mit dem Vergleich einiger identischer Typwerte (z. B. (type text) = (type text)
) möglicherweise true
zurückgegeben wird, während andere (z. B. (type [a = text]) = (type [a = text])
) nicht verglichen werden.
Die Kompatibilität eines bestimmten Typen mit einem primitiven Nullable-Typ kann mithilfe der Bibliotheksfunktion Type.Is
überprüft werden, die einen beliebigen Typwert als erstes und einen primitiven Nullable-Typwert als zweites Argument akzeptiert:
Type.Is(type text, type nullable text) // true
Type.Is(type nullable text, type text) // false
Type.Is(type number, type text) // false
Type.Is(type [a=any], type record) // true
Type.Is(type [a=any], type list) // false
Das Überprüfen der Kompatibilität eines bestimmten Typs mit einem benutzerdefinierten Typ wird in M nicht unterstützt.
Die Standardbibliothek umfasst allerdings eine Reihe von Funktionen zum Extrahieren der definierenden Merkmale von benutzerdefinierten Typen, sodass bestimmte Kompatibilitätstests als M-Ausdrücke implementiert werden können. Im Folgenden sind einige Beispiele aufgeführt. Detaillierte Informationen hierzu finden Sie in der Bibliotheksspezifikation.
Type.ListItem( type {number} )
// type number
Type.NonNullable( type nullable text )
// type text
Type.RecordFields( type [A=text, B=time] )
// [ A = [Type = type text, Optional = false],
// B = [Type = type time, Optional = false] ]
Type.TableRow( type table [X=number, Y=date] )
// type [X = number, Y = date]
Type.FunctionParameters(
type function (x as number, optional y as text) as number)
// [ x = type number, y = type nullable text ]
Type.FunctionRequiredParameters(
type function (x as number, optional y as text) as number)
// 1
Type.FunctionReturn(
type function (x as number, optional y as text) as number)
// type number