Tipi
Un valore tipo è un valore che classifica altri valori. Si dice che un valore classificato in base a un tipo sia conforme a quel tipo. Il sistema di tipi del linguaggio M è costituito dai tipi seguenti:
Tipi primitivi, che classificano i valori primitivi (
binary
,date
,datetime
,datetimezone
,duration
,list
,logical
,null
,number
,record
,text
,time
,type
) e includono anche alcuni tipi astratti (function
,table
,any
,anynonnull
enone
)Tipi record, che classificano i valori record in base ai nomi dei campi e ai tipi di valore
Tipi elenco, che classificano gli elenchi usando un tipo di base a elemento singolo
Tipi funzione, che classificano i valori funzione in base ai rispettivi tipi di parametri e valori restituiti
Tipi tabella, che classificano i valori tabella in base ai nomi delle colonne, ai tipi di colonna e alle chiavi
Tipi nullable, che classificano il valore Null, oltre a tutti i valori classificati a partire da un tipo di base
Tipi "tipo", che classificano i valori costituiti da tipi
Il set di tipi primitivi include i tipi di valori primitivi e alcuni tipi astratti, tipi che non classificano in modo univoco alcun valore: function
, table
, any
, anynonnull
e none
. Tutti i valori funzione sono conformi al tipo astratto function
, tutti i valori tabella al tipo astratto table
, tutti i valori al tipo astratto any
, tutti i valori non null al tipo astratto anynonnull
e nessun valore al tipo astratto none
. Un'espressione di tipo none
deve generare un errore o non poter essere terminata perché non è stato possibile produrre alcun valore conforme al tipo none
. I tipi primitivi function
e table
sono astratti perché, rispettivamente, nessuna funzione o tabella appartiene direttamente a questi tipi. I tipi primitivi record
e list
non sono astratti perché rappresentano, rispettivamente, un record aperto senza campi definiti e un elenco di tipo any.
Tutti i tipi che non appartengono al set chiuso di tipi primitivi e le loro controparti non nullable sono definiti collettivamente come tipi personalizzati. I tipi personalizzati possono essere scritti usando un'espressione type-expression
:
type-expression:
primary-expression
type
primary-type
type:
primary-expression
primary-type
primary-type:
primitive-type
record-type
list-type
function-type
table-type
nullable-type
primitive-type: uno tra
any anynonnull binary date datetime datetimezone duration function list logical
none null number record table text time type
I nomi primitive-type sono parole chiave contestuali riconosciute solo in un contesto type. L'uso di parentesi in un contesto type riconduce la grammatica a un contesto di espressioni regolari ed è quindi necessario usare la parola chiave type per tornare a un contesto type. Per richiamare una funzione in un contesto type, è possibile, ad esempio, usare le parentesi:
type nullable ( Type.ForList({type number}) )
// type nullable {number}
Le parentesi possono essere usate anche per accedere a una variabile il cui nome entra in conflitto con un nome primitive-type:
let record = type [ A = any ] in type {(record)}
// type {[ A = any ]}
Nell'esempio seguente viene definito un tipo che classifica un elenco di numeri:
type { number }
Analogamente, nell'esempio seguente viene definito un tipo personalizzato che classifica i record con campi obbligatori denominati X
e Y
, i cui valori sono numeri:
type [ X = number, Y = number ]
Il tipo ascritto di un valore viene ottenuto usando la funzione di libreria standard Value.Type, come illustrato negli esempi seguenti:
Value.Type( 2 ) // type number
Value.Type( {2} ) // type list
Value.Type( [ X = 1, Y = 2 ] ) // type record
L'operatore is
consente di determinare se il tipo di un valore è compatibile con un tipo specificato, come illustrato negli esempi seguenti:
1 is number // true
1 is text // false
{2} is list // true
L'operatore as
verifica che il valore sia compatibile con il tipo specificato e, se non è compatibile, genera un errore. In caso contrario, restituisce il valore originale.
Value.Type( 1 as number ) // type number
{2} as text // error, type mismatch
Tenere presente che gli operatori is
e as
accettano solo tipi primitivi nullable come operando destro. M non offre alcun modo per verificare la conformità dei valori a tipi personalizzati.
Un tipo X
è compatibile con un tipo Y
se e solo se tutti i valori conformi a X
sono conformi anche a Y
. Tutti i tipi sono compatibili con il tipo any
e nessun tipo (ad eccezione di none
) è compatibile con il tipo none
. Il grafico seguente illustra la relazione di compatibilità (la compatibilità dei tipi è riflessiva e transitiva; forma un reticolo con tipo any
come valore superiore e tipo none
come valore inferiore). I nomi dei tipi astratti vengono impostati in corsivo.
Per i valori tipo vengono definiti gli operatori seguenti:
Operatore | Risultato |
---|---|
x = y |
Equal |
x <> y |
Not equal |
x ?? y |
Coalesce |
Il tipo nativo dei valori tipo è il tipo intrinseco type
.
Nel linguaggio M, i tipi formano una gerarchia non contigua con radice nel tipo any
, ovvero il tipo che classifica tutti i valori. Qualsiasi valore M è conforme esattamente a un sottotipo primitivo di any
. Di seguito è illustrato il set chiuso di tipi primitivi che derivano dal tipo any
:
-
type null
, che classifica il valore Null. -
type logical
, che classifica i valori true e false. -
type number
, che classifica i valori numero. -
type time
, che classifica i valori ora. -
type date
, che classifica i valori data. -
type datetime
, che classifica i valori datetime. -
type datetimezone
, che classifica i valori datetimezone. -
type duration
, che classifica i valori durata. -
type text
, che classifica i valori testo. -
type binary
, che classifica i valori binari. -
type type
, che classifica i valori tipo. -
type list
, che classifica i valori elenco. -
type record
, che classifica i valori record. -
type table
, che classifica i valori tabella. -
type function
, che classifica i valori funzione. -
type anynonnull
, che classifica tutti i valori ad esclusione di Null. -
type none
, che classifica nessun valore.
Il tipo any
è astratto, classifica tutti i valori in M e tutti i tipi disponibili in M sono compatibili con any
. Le variabili di tipo any
possono essere associate a tutti i valori possibili. Poiché any
è astratto, non può essere attribuito a valori, vale a dire che nessun valore è direttamente di tipo any
.
Qualsiasi valore che rappresenta un elenco è conforme al tipo intrinseco list
, che non prevede alcuna restrizione per gli elementi all'interno di un valore elenco.
list-type:
{
item-type}
item-type:
type
Il risultato della valutazione di un list-type è un valore di tipo list il cui tipo base è list
.
Negli esempi seguenti viene illustrata la sintassi necessaria per la dichiarazione di tipi elenco omogenei:
type { number } // list of numbers type
{ record } // list of records type
{{ text }} // list of lists of text values
Un valore è conforme a un tipo elenco se costituisce un elenco in cui ogni elemento è conforme al tipo elemento del tipo elenco.
Il tipo elemento di un tipo elenco denota un'associazione: tutti gli elementi di un elenco conforme sono necessariamente conformi al tipo elemento.
Qualsiasi valore che rappresenta un record è conforme al tipo intrinseco type, che non prevede alcuna restrizione per i valori o i nomi di campo all'interno di un valore record. Un valore record-type consente di limitare il set di nomi validi e i tipi di valori che possono essere associati a questi nomi.
record-type:
[
open-record-marker]
[
field-specification-listopt]
[
field-specification-list , open-record-marker]
field-specification-list:
field-specification
field-specification,
field-specification-list
field-specification:
optional
opt field-name field-type-specificationopt
field-type-specification:
=
field-type
field-type:
Tipo
open-record-marker:
...
Il risultato della valutazione di un record-type è un valore di tipo il cui tipo base è record
.
Nell'esempio seguente viene illustrata la sintassi necessaria per la dichiarazione di tipi record:
type [ X = number, Y = number]
type [ Name = text, Age = number ]
type [ Title = text, optional Description = text ]
type [ Name = text, ... ]
I tipi record sono chiusi per impostazione predefinita, ovvero non è possibile presentare come valori conformi eventuali campi aggiuntivi non presenti in fieldspecification-list. L'inserimento del openrecord-marker nel tipo record dichiara che il tipo è aperto, ovvero sono consentiti campi non presenti nell'elenco delle specifiche dei campi. Le due espressioni seguenti sono equivalenti:
type record // primitive type classifying all records
type [ ... ] // custom type classifying all records
Un valore è conforme a un tipo record se il valore è un record e viene soddisfatta ogni specifica di campo nel tipo record. Una specifica di campo viene soddisfatta se si verifica una delle condizioni seguenti:
Nel record è presente un nome di campo corrispondente all'identificatore della specifica e il valore associato è conforme al tipo di specifica
La specifica è contrassegnata come facoltativa e nel record non viene trovato alcun nome di campo corrispondente
Un valore conforme può contenere nomi di campo non elencati nell'elenco di specifiche dei campi se e solo se il tipo record è aperto.
Qualsiasi valore funzione è conforme al tipo primitivo function
, che non applica alcuna restrizione ai tipi dei parametri formali della funzione o al valore restituito dalla funzione. Un valore function-type consente di inserire restrizioni di tipo nelle firme dei valori funzione conformi.
function-type:
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-list
required-parameter-specification:
parameter-specification
optional-parameter-specification-list:
optional-parameter-specification
optional-parameter-specification,
optional-parameter-specification-list
optional-parameter-specification:
optional
parameter-specification
parameter-specification:
parameter-name parameter-type
function-return-type:
assertion
assertion:
as
nullable-primitive-type
Il risultato della valutazione di un function-type è un valore di tipo il cui tipo base è function
.
Nell'esempio seguente viene illustrata la sintassi necessaria per la dichiarazione di tipi funzione:
type function (x as text) as number
type function (y as number, optional z as text) as any
Un valore funzione è conforme a un tipo funzione se il tipo restituito del valore funzione è compatibile con il tipo restituito del tipo funzione e ogni specifica di parametro del tipo funzione è compatibile con il parametro formale della funzione corrispondente a livello di posizione. Una specifica di parametro è compatibile con un parametro formale se il tipo parameter-type specificato è compatibile con il tipo del parametro formale ed è facoltativa se il parametro formale è facoltativo.
I nomi dei parametri formali vengono ignorati quando si tratta di determinare la conformità del tipo funzione.
Specificare un parametro come facoltativo, lo rende di tipo nullable. I seguenti creano tipi di funzione identici:
type function (optional x as text) as any
type function (optional x as nullable text) as any
Un valore table-type consente di definire la struttura di un valore tabella.
table-type:
table
row-type
row-type:
[
field-specification-listopt]
Il risultato della valutazione di un table-type è un valore di tipo il cui tipo base è table
.
Il tipo row di una tabella specifica i nomi e i tipi di colonna della tabella come tipo record chiuso. Tutti i valori della tabella sono conformi al tipo table
solo se la riga è di tipo record
(tipo record aperto vuoto). Il tipo tabella è quindi astratto perché nessun valore di tabella può avere una riga di tipo table
(ma tutti i valori di tabella hanno un tipo di riga compatibile con il tipo table
). Nell'esempio seguente viene illustrata la creazione di un tipo tabella:
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
Un valore tipo-tabella contiene anche la definizione delle chiavi di un valore tabella. Una chiave è un set di nomi di colonna. Non è possibile designare più di una chiave come chiave primaria della tabella. Nel linguaggio M, le chiavi di tabella non hanno alcun significato semantico. È tuttavia comune che in origini dati esterne, quali database o feed OData, siano state definite chiavi sulle tabelle. Power Query usa le informazioni delle chiavi per migliorare le prestazioni delle funzionalità avanzate, ad esempio le operazioni di join tra più origini.
Le funzioni Type.TableKeys
, Type.AddTableKey
e Type.ReplaceTableKeys
della libreria standard consentono, rispettivamente, di ottenere le chiavi di un tipo tabella, di aggiungere una chiave a un tipo tabella e di sostituire tutte le chiavi di un tipo tabella.
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
Per qualsiasi type T
è possibile ricavare una variante nullable usando nullable-type:
nullable-type:
nullable
type
Il risultato è un tipo astratto che consente valori di tipo T o il valore null
.
42 is nullable number // true null is
nullable number // true
L'attribuzione di type nullable
T si riduce all'attribuzione di type null
o type
T. Tenere presente che i tipi nullable sono astratti e nessun valore può essere direttamente di tipo astratto.
Value.Type(42 as nullable number) // type number
Value.Type(null as nullable number) // type null
Le funzioni della libreria standard Type.IsNullable
e Type.NonNullable
consentono di testare la capacità di un tipo di supportare valori Null e di rimuovere questo tipo di supporto.
Sono valide le considerazioni seguenti (per qualsiasi type T
):
-
type T
è compatibile contype nullable T
-
Type.NonNullable(type T)
è compatibile contype T
Di seguito sono riportati gli equivalenti pairwise (per qualsiasi 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
Il tipo attribuito di un valore è il tipo a cui un valore viene dichiarato conforme.
È possibile attribuire un tipo a un valore usando la funzione di libreria Value.ReplaceType
. Questa funzione restituisce un nuovo valore con il tipo attribuito o genera un errore se il nuovo tipo non è compatibile con il valore.
Quando a un valore viene attribuito un tipo, viene eseguito solo un controllo di conformità limitato:
- Il tipo attribuito deve essere non astratto, non nullable e compatibile con il tipo primitivo intrinseco (nativo) del valore.
- Quando viene attribuito un tipo personalizzato che definisce la struttura, deve corrispondere alla struttura del valore.
- Per i record: il tipo deve essere chiuso, deve definire lo stesso numero di campi del valore e non deve contenere campi facoltativi. I nomi dei campi e i tipi di campo del tipo sostituiranno quelli attualmente associati al record. Tuttavia, i valori dei campi esistenti non verranno confrontati con i nuovi tipi di campo.
- Per le tabelle: il tipo deve definire lo stesso numero di colonne del valore. I nomi delle colonne e i tipi di colonna del tipo sostituiranno quelli attualmente associati alla tabella. Tuttavia, i valori di colonna esistenti non verranno confrontati con i nuovi tipi di colonna.
- Per le funzioni: il tipo deve definire lo stesso numero di parametri obbligatori, nonché lo stesso numero di parametri facoltativi, del valore. Il parametro del tipo e le asserzioni restituite, nonché i relativi nomi di parametro, sostituiranno quelli associati al tipo corrente del valore della funzione. Tuttavia, le nuove asserzioni non avranno alcun effetto sul comportamento effettivo della funzione.
- Per gli elenchi: il valore deve essere un elenco. Tuttavia, gli elementi elenco esistenti non verranno confrontati con i nuovi tipi di elemento.
Le funzioni di libreria possono scegliere di calcolare e attribuire tipi complessi ai risultati in base ai tipi attribuiti ai valori di input.
Il tipo attribuito a un valore può essere ottenuto usando la funzione di libreria Value.Type
. Ad esempio:
Value.Type( Value.ReplaceType( {1}, type {number} )
// type {number}
L'equivalenza del tipo non è definita in M. Un'implementazione di M può facoltativamente scegliere di usare le proprie regole per eseguire confronti di uguaglianza tra valori di tipo. Il confronto di due valori di tipo per l'uguaglianza deve restituire true
se sono considerati identici dall'implementazione e false
in caso contrario. In entrambi i casi, la risposta restituita deve essere coerente se gli stessi due valori vengono confrontati più volte. Si noti che, all'interno di una specifica implementazione, il confronto di alcuni valori di tipo identici (ad esempio (type text) = (type text)
) può restituire true
, ma questo potrebbe non avvenire confrontandone altri (ad esempio (type [a = text]) = (type [a = text])
).
È possibile determinare la compatibilità tra un tipo specifico e un tipo primitivo nullable usando la funzione di libreria Type.Is
, che accetta un valore di tipo arbitrario come primo argomento e un valore di tipo primitivo nullable come secondo argomento:
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
In M non è possibile determinare la compatibilità di un tipo con un tipo personalizzato.
La libreria standard include una raccolta di funzioni che consentono di estrarre le caratteristiche distintive di un tipo personalizzato e, pertanto, è possibile implementare test di compatibilità come espressioni M. Di seguito sono riportati alcuni esempi. Per informazioni dettagliate, consultare la specifica della libreria M.
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