Share via


類型

「類型值」是「分類」其他值的值。 由類型分類的值稱為「符合」該類型。 M 類型系統由下列類型種類組成:

  • 基本類型,這些類型會分類基本值 (binarydatedatetimedatetimezonedurationlistlogicalnullnumberrecordtexttimetype),也包含一些抽象類型 (functiontableanyanynonnullnone)

  • 記錄類型,其根據欄位名稱和值類型來分類記錄值

  • 清單類型,其使用單一項目基底類型來分類清單

  • 函式類型,其根據函式的參數和傳回值來分類函式值

  • 資料表類型,其根據資料行名稱、資料行類型和索引鍵來分類資料表值

  • 可為 Null 的型別,其分類 Null 值和所有由基底類型分類的值

  • 類型類型,其分類本身是類型的值

「基本類型」集合包含基本值的類型,以及一些「抽象類型」,這是無法唯一分類任何值的類型:functiontableanyanynonnullnone。 所以函式值都符合抽象類型 function,所有資料表值都符合抽象類型 table,所有值都符合抽象類型 any,我有非 Null值都符合抽象類型 anynonnull,且沒有任何值符合抽象類型 nonenone 類型的運算式必須引發錯誤或是終止失敗,因為無法產生任何符合 none 類型的值。 請注意,基本類型 functiontable 為抽象類型,因為沒有任何函式或資料表分別直接符合這些類型。 基本類型 recordlist 並非抽象類型,因為其分別代表沒有已定義欄位的開放式記錄和 any 類型清單。

所有並非基本類型封閉集合成員的類型及其可為 Null的對等項目都統稱為「自訂類型」。 自訂類型可以使用 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:
下列其中一個
      any anynonnull binary date datetime datetimezone duration function list logical
      none null number record table text time type

primitive-type 名稱是只有在「類型」內容中才會識別的「內容關鍵字」。 在「類型」內容中使用括弧會將文法移回規則運算式內容,並要求使用 type 關鍵字來移回類型內容。 例如,若要在「類型」內容中呼叫函式,則可使用括弧:

type nullable ( Type.ForList({type number}) )   
// type nullable {number}

括弧也可以用來存取名稱與 primitive-type 名稱衝突的變數:

let  record = type [ A = any ]  in  type {(record)} 
// type {[ A = any ]}

下列範例會定義分類數字清單的類型:

type { number }

同樣地,下列範例會定義自訂類型,其分類包含名為 XY 強制欄位,且其值是數字的記錄:

type [ X = number, Y = number ]

您可使用標準程式庫函式 Value.Type 取得已歸屬值的類型,如下列範例中所示:

Value.Type( 2 )                 // type number 
Value.Type( {2} )               // type list 
Value.Type( [ X = 1, Y = 2 ] )  // type record

is 運算子會用來判斷值的類型是否與指定類型相容,如下列範例中所示:

1 is number          // true 
1 is text            // false 
{2} is list          // true

as 運算子會檢查值是否與指定類型相容,且在不相容時引發錯誤。 否則,其會傳回原始值。

Value.Type( 1 as number )   // type number 
{2} as text                 // error, type mismatch

請注意,isas 運算子只接受可為 Null 的基本類型作為其右運算元。 M 沒有為自訂類型提供檢查值是否符合的方式。

只有在所有與 X 相符的值也都與 Y 相符時,類型 X 才與類型 Y「相容」。 所以類型都與 any 類型相容,且沒有任何類型 (但 none 本身除外) 與 none 類型相容。 下圖顯示相容性關聯。 (類型相容性是自反且可轉移的。其會構成辨識格,且上界值為 any 類型,下界值則為 none 類型。)抽象類型的名稱為「斜體」

類型相容性

其中已為類型值定義下列運算子:

運算子 結果
x = y 等於
x <> y 不等於
x ?? y Coalesce

類型值的原生類型是內建類型 type

基本類型

M 語言中類型構成根部為 any 類型的斷續階層,該類型同時也是分類所有值的類型。 任何 M 值都符合一個 any 的基本子類型。 衍生自 any 類型的基本類型封閉集合如下:

  • type null,其分類 Null 值。
  • type logical,其分類 true 和 false 值。
  • type number,其分類數字值。
  • type time,其分類時間值。
  • type date,其分類日期值。
  • type datetime,其分類日期時間值。
  • type datetimezone,其分類 datetimezone 值。
  • type duration,其分類持續時間值。
  • type text,其分類文字值。
  • type binary,其分類二進位值。
  • type type,其分類類型值。
  • type list,其分類清單值。
  • type record,其分類記錄值。
  • type table,其分類資料表值。
  • type function,其分類函式值。
  • type anynonnull,其分類所有除 Null 之外的值。
  • type none,其不分類任何值。

任何類型

any 類型為抽象類型,會分類 M 中的所有值,且 M 中的所有類型皆和 any 相容。 any 類型的變數可以繫結至所有可能值。 由於 any 為抽象類型,因此無法歸屬至值—意即沒有任何值的直接類型是 any

清單類型

任何屬於清單的值都符合原生類型 list,此類型不會針對清單值內的項目施加任何限制。

list-type:
      {item-type}
item-type:
      type

評估 list-type 的結果是一個「清單類型值」,其基本類型為 list

下列範例示範宣告同質清單類型的語法:

type { number }        // list of numbers type 
     { record }        // list of records type
     {{ text }}        // list of lists of text values

如果值是清單且該清單值中每個項目都符合清單類型的項目類型,則該值便符合清單類型。

清單類型的項目類型會指出界限:符合清單的所有項目都符合項目類型。

記錄類型

任何屬於記錄的值都符合原生類型 record,此類型不會針對記錄值內的欄位名稱或值施加任何限制。 record-type-value 會用來限制有效名稱集合,以及允許與這些名稱建立關聯的值類型。

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:
      type
open-record-marker:

      ...

評估 record-type 的結果是一個類型值,其基本類型為 record

下列範例示範宣告記錄類型的語法:

type [ X = number, Y = number] 
type [ Name = text, Age = number ]
type [ Title = text, optional Description = text ] 
type [ Name = text, ... ]

記錄類型根據預設是「封閉」的,這表示任何不存在 fieldspecification-list 中的其他欄位都不允許出現在符合值內。 在記錄類型中包含 openrecord-marker 會宣告類型為「開放」,其允許不存在於欄位規格清單中的欄位。 下列兩個運算式是對等的:

type record   // primitive type classifying all records 
type [ ... ]  // custom type classifying all records

如果值是記錄且記錄類型中的每個欄位規格都獲得滿足,則值便符合記錄類型。 若下列任何一項為 true,欄位規格便會獲得滿足:

  • 記錄中存在與規格識別碼相符的欄位名稱,且相關聯值符合規格的類型

  • 規格已標記為選擇性,且在記錄中沒有找到任何對應的欄位名稱

只有在記錄類型為開放時,符合值才可包含並未列在欄位規格清單中的欄位名稱。

函式類型

任何函式類型都符合基本類型 function,此類型不會針對函式的型式參數或函式的傳回值施加任何限制。 自訂 function-type 值會用來針對符合函式值的簽章施加類型限制。

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

評估 function-type 的結果是一個類型值,其基本類型為 function

下列範例示範宣告函式類型的語法:

type function (x as text) as number 
type function (y as number, optional z as text) as any

若函式值其傳回類型與函式類型的傳回類型相容,且函式類型每個參數規格都與函式對應位置的型式參數相容,則函式值便符合函式類型。 若指定的 parameter-type 類型與型式參數類型相容,則參數規格便與型式參數相容,且若型式參數是選擇性的,則參數規格也會是選擇性的。

為了判斷函式類型是否符合,會忽略型式參數的名稱。

將參數指定為選擇性,以隱含方式使其類型可為 Null。 下列會建立相同的函式類型:

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

資料表類型

table-type 值會用來定義資料表值的結構。

table-type:
      tablerow-type
row-type:

      [field-specification-listopt]

評估 table-type 的結果是一個類型值,其基本類型為 table

資料表的「資料列類型」會指定資料行名稱,並將資料表的資料行類型指定為封閉記錄類型。 所以,所有與 table 類型符合的資料表值,其資料列類型都會是 record 類型 (空白的開放式記錄類型)。 因此,table 類型是抽象類型,因為沒有任何資料表值的資料列類型是 table (但所有資料表值都擁有與 table 資料列類型相容的資料列類型)。 下列範例顯示資料表類型的建構:

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

table-type 值也包含資料表值「索引鍵」的定義。 索引鍵是一組資料行名稱。 最多只能指定一個索引鍵作為資料表的「主索引鍵」。 (在 M 中,資料表索引鍵沒有任何語意意義。但是,外部資源 (例如資料庫或 OData 摘要) 在資料表上定義索引鍵非常常見。Power Query 會使用索引鍵資訊來改善進階功能的效能,例如跨來源聯結作業。)

標準程式庫函式 Type.TableKeysType.AddTableKeyType.ReplaceTableKeys 分別可以用來取得資料表類型的索引鍵、將索引鍵新增到資料表類型,以及取代資料表類型的所有索引鍵。

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

可為 Null 的類型

針對任何 type T,可為 Null 的變體可透過使用 nullable-type 來衍生:

nullable-type:
      nullabletype

其結果是抽象類型,其允許 T 類型的值,或 null 值。

42 is nullable number             // true null is
nullable number                   // true

type nullableT 的歸屬會縮減至 type nulltypeT 的歸屬。(請回想一下可為 Null 的型別為抽象類型,因此沒有任何值直接屬於抽象類型。)

Value.Type(42 as nullable number)       // type number
Value.Type(null as nullable number)     // type null

標準程式庫函式 Type.IsNullableType.NonNullable 可以用來測試類型的可為 Null 性,以及從該類型移除可為 Null 性。

下列項目適用 (針對任何 type T):

  • type Ttype nullable T 相容
  • Type.NonNullable(type T)type T 相容

下列是成對的相等項目 (針對任何 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

值的歸屬類型

值的「歸屬類型」是值「宣告」符合的類型。

您可使用程式庫函式 Value.ReplaceType 來將值歸屬至類型。 此函式會傳回已歸屬類型的新值,如果新類型與值不相容,則會引發錯誤。

當值歸屬於某一類型時,只會進行有限的符合性檢查:

  • 所歸屬的類型必須是非抽象、不可為 Null 並與值的內建 (原生) primitive-type 相容。
  • 歸屬可定義結構的自訂類型時,其必須符合值的結構。
    • 對於記錄:類型必須為封閉,必須定義與值相同的欄位數目,而且不得包含任何選擇性欄位。 (此類型的欄位名稱和欄位類型會取代目前與記錄相關聯的欄位名稱和欄位類型。不過,不會針對新的欄位類型檢查現有的欄位值。)
    • 對於資料表:此類型必須定義與值相同的資料行數目。 (此類型的資料行名稱和資料行類型會取代目前與資料表相關聯的欄位名稱和欄位類型。不過,不會針對新的資料行類型檢查現有的資料行值。)
    • 針對函式:此類型必須定義與值相同的必要參數數目,以及相同的選擇性參數數目。 (此類型的參數和傳回判斷提示及其參數名稱,會取代與函式值目前類型相關聯的前述項目。不過,新的判斷提示不會影響函式的實際行為。)
    • 針對清單:此值必須是清單。 (不過,不會針對新的項目類型檢查現有的清單項目。)

程式庫函式可根據輸入值的歸屬類型來選擇計算,並將複雜類型歸屬至結果。

您可使用程式庫函式 Value.Type 來取得值的歸屬類型。 例如:

Value.Type( Value.ReplaceType( {1}, type {number} ) 
// type {number}

類型等價和相容性

類型等價未定義於 M。M 實作可選擇性地選擇使用自己的規則來執行類型值之間的相等比較。 比較兩個類型值是否相等應該評估為 true,而如果實作將兩個類型值視為相同,則會評估為 false。 不論是哪一種情況,如果重複比較相同的兩個值,則傳回的回應必須一致。 請注意,在指定的實作中,比較某些相同的類型值 (例如 (type text) = (type text)) 可能會傳回 true,而比較其他類型值 (例如 (type [a = text]) = (type [a = text])) 則可能不會傳回該值。

指定類型和可為 Null 基本類型間的相容性可以透過程式庫函式 Type.Is 判斷,該函式接受任意類型值作為其第一個引數,並接受可為 Null 的基本類型值作為其第二個引數:

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

M 中並未提供判斷指定類型與自訂類型是否相容的支援。

標準程式庫包含函式的集合,可從自訂類型擷取定義特性,因此仍然可以將特定相容性測試作為 M 運算式實作。 以下是一些範例;請參閱 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