Condividi tramite


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 e none)

  • 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

      typeprimary-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 attribuito di un valore viene ottenuto usando la funzione della 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.

Compatibilità del tipo

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.

Tipi primitivi

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.

Tipo any

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.

Tipi list

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.

Tipi di record

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:

      optionalopt 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.

Tipi di funzioni

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:

      optionalparameter-specification
parameter-specification:
      parameter-name parameter-type
function-return-type:
      assertion
assertion:

      asnullable-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.

Se si specifica un parametro come facoltativo, il tipo risulta nullable. Di seguito vengono creati tipi di funzione identici:

type function (optional x as text) as any
type function (optional x as nullable text) as any

Tipi di tabella

Un valore table-type consente di definire la struttura di un valore tabella.

table-type:
      tablerow-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

Tipi nullable

Per qualsiasi type T è possibile ricavare una variante nullable usando nullable-type:

nullable-type:
      nullabletype

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 del type nullableT si riduce all'attribuzione del type null o del typeT. 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 con type nullable T
  • Type.NonNullable(type T) è compatibile con type 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

Tipo attribuito di un valore

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}

Compatibilità ed equivalenza del tipo

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