Типы

Значение типа — это значение, которое классифицирует другие значения. Значение, классифицированное типом, соответствует ему. Система типов M состоит из следующих видов типов.

  • Примитивные типы, которые классифицируют примитивные значения (binary, date, datetime, datetimezone, duration, list, logical, null, number, record, text, time, type), а также включают несколько абстрактных типов (function, table, anyи none).

  • Типы записей, которые классифицируют значения записей на основе имен полей и типов значений.

  • Типы списков, которые классифицируют списки, используя базовый тип отдельного элемента.

  • Типы функций, которые классифицируют значения функций на основе типов их параметров и возвращаемых значений.

  • Типы таблиц, которые классифицируют табличные значения на основе имен столбцов, типов столбцов и ключей.

  • Типы, допускающие значение NULL, которые классифицируют значение NULL в дополнение ко всем значениям, классифицируемым базовым типом.

  • Типы типов, которые классифицируют значения, являющиеся типами.

Набор примитивных типов включает типы примитивных значений, несколько абстрактных типов, типы, которые не могут однозначно классифицировать какие-либо значения: function, table, any и none. Все значения функций соответствуют абстрактному типу function, все табличные значения — абстрактному типу table, все значения — абстрактному типу any и никакие значения — абстрактному типу none. Выражение типа none должно вызвать ошибку или завершиться сбоем, так как невозможно создать значение, соответствующее типу none. Обратите внимание, что примитивные типы function и table являются абстрактными, так как ни одна функция или таблица, соответственно, не относятся непосредственно к этим типам. Примитивные типы record и list не являются абстрактными, так как они представляют открытую запись без определенных полей и список типа "any" соответственно.

Все типы, не являющиеся членами закрытого набора примитивных типов, в совокупности называются пользовательскими типами. Пользовательские типы можно написать с помощью type-expression:

выражение-типа:
      основное-выражение

      typeосновной-тип
тип:
      выражение-в-круглых-скобках
      основной-тип
основной-тип:
      тип-примитива
      тип-записи
      тип-списка
      тип-функции
      тип-таблицы
      тип-допускающий-значение-NULL
тип-примитива:
один-из
      any binary date datetime datetimezone duration function list logical
      none null number record table text time type

Имена примитивных-типов являются контекстными ключевыми словами, распознаваемыми только в контексте типа. Использование круглых скобок в контексте типа переводит грамматику обратно в контекст регулярного выражения, что требует использования ключевого слова "type" для перемещения обратно в контекст типа. Например, чтобы вызвать функцию в контексте типа, можно использовать круглые скобки:

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

Круглые скобки также можно использовать для доступа к переменной, имя которой конфликтует с именем примитивного типа:

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

В следующем примере определяется тип, классифицирующий список чисел:

type { number }

Аналогичным образом в следующем примере определяется пользовательский тип, классифицирующий записи с обязательными полями X и Y, значения которых являются числами:

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

Обратите внимание, что операторы is и as принимают в качестве правого операнда только примитивные типы. Язык M не предоставляет средства для проверки значений на соответствие пользовательским типам.

Тип Xсовместим с типом Y только в том случае, если все значения, соответствующие X, также соответствуют Y. Все типы совместимы с типом any, и никакие типы (кроме самого none) не совместимы с типом none. На следующем графе показана связь совместимости. (Совместимость типов является рефлексивной и транзитивной. Она формирует решетку с типом any в качестве верхнего значения и типом none в качестве нижнего значения.) Имена абстрактных типов выделены курсивом.

Type compatibility

Для значений типов определены следующие операторы.

Оператор Результат
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, который классифицирует значения datetime.
  • type datetimezone, который классифицирует значения datetimezone.
  • type duration, который классифицирует значения длительности.
  • type text, который классифицирует текстовые значения.
  • type binary, который классифицирует двоичные значения.
  • type type, который классифицирует значения типов.
  • type list, который классифицирует значения списков.
  • type record, который классифицирует значения записей.
  • type table, который классифицирует табличные значения.
  • type function, который классифицирует значения функций.
  • type anynonnull, который классифицирует все значения, кроме NULL. Внутренний тип none не классифицирует никакие значения.

Тип any

Тип any является абстрактным, классифицирует все значения в M, и все типы в M совместимы с any. Переменные типа any могут быть привязаны ко всем возможным значениям. Так как any является абстрактным, он не может быть приписан значениям. То есть, никакое значение не может напрямую иметь тип any.

Типы списков

Любое значение, которое является списком, соответствует встроенному типу list, который не налагает никаких ограничений на элементы в значении списка.

тип-списка:
      {тип-элемента}
тип-элемента:
      тип

Результатом вычисления типа-списка является значение типа списка с базовым типом list.

В следующих примерах показан синтаксис объявления однородных типов списков:

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

Значение соответствует типу списка, если оно является списком, а каждый элемент в этом значении списка соответствует типу элемента типа списка.

Тип элемента типа списка указывает привязку: все элементы соответствующего списка соответствуют типу элемента.

Типы записей

Любое значение, которое является записью, соответствует встроенному типу "record", который не налагает никаких ограничений на имена полей или значения в значении записи. Значение типа-записи используется для ограничения набора допустимых имен, а также типов значений, которые могут быть связаны с этими именами.

тип-записи:
      [маркер-открытой-записи]
      [список-спецификаций-полянеобязательно]
      [список-спецификаций-поля, маркер-открытой-записи]
список-спецификаций-поля:
      спецификация-поля
      спецификация-поля,список-спецификаций-поля
спецификация-поля:

      optionalнеобязательно имя-поля спецификация-типа-полянеобязательно
спецификация-типа-поля:

      =тип-поля
тип-поля:
      тип
маркер-открытой-записи:

      ...

Результатом вычисления типа-записи является значение типа с базовым типом record.

В следующих примерах показан синтаксис объявления типов записей:

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

Типы записей закрыты по умолчанию, что означает, что дополнительные поля, отсутствующие в списке-спецификаций-поля, не могут присутствовать в соответствующих значениях. Включение маркера-открытой-записи в тип записи объявляет тип как открытый, что разрешает поля, отсутствующие в списке спецификаций поля. Следующие два выражения эквивалентны:

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

Значение соответствует типу записи, если оно является записью и все спецификации полей в типе записи удовлетворены. Спецификация поля считается удовлетворенной, если выполняется одно из следующих условий.

  • В записи существует имя поля, совпадающее с идентификатором спецификации, а связанное значение соответствует типу спецификации.

  • Спецификация помечена как необязательная, и в записи не найдено соответствующее имя поля.

Соответствующее значение может содержать имена полей, не указанные в списке спецификаций поля, и только в том случае, если тип записи является открытым.

Типы функций

Любое значение функции соответствует примитивному типу function, который не налагает никаких ограничений на типы формальных параметров функции или возвращаемое значение функции. Пользовательское значение типа функции используется для наложения ограничений на типы в сигнатурах соответствующих значений функций.

тип-функций:
      function (список-спецификаций-параметровнеобязательно)тип-возвращаемого-значения-функции
список-спецификаций-параметров:
      список-спецификаций-обязательных-параметров
      список-спецификаций-обязательных-параметров
,список-спецификаций-необязательных-параметров
      список-спецификаций-необязательных-параметров
список-спецификаций-обязательных-параметров:
      спецификация-обязательных-параметров
      спецификация-обязательных-параметров
,список-спецификаций-обязательных-параметров
спецификация-обязательных-параметров:
      спецификация-параметров
список-спецификаций-необязательных-параметров:
      спецификация-необязательных-параметров
      спецификация-необязательных-параметров
,список-спецификаций-необязательных-параметров
спецификация-необязательных-параметров:

      optionalспецификация-параметров
спецификация-параметров:
      имя-параметра тип-параметра
тип-возвращаемого-значения-функции:
      assertion
assertion:

      asтип-примитива-допускающий-значение-NULL

Результатом вычисления типа-функции является значение типа с базовым типом function.

В следующих примерах показан синтаксис объявления типов функций:

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

Значение функции соответствует типу функции, если тип возвращаемого значения для значения функции совместим с типом возвращаемого значения для типа функции, а каждая спецификация параметра типа функции совместима с формальным параметром функции, стоящим на аналогичной позиции. Спецификация параметра совместима с формальным параметром, если указанный тип-параметра совместим с типом формального параметра, а спецификация параметра является необязательной, если формальный параметр является необязательным.

Имена формальных параметров игнорируются в целях определения соответствия типов функций.

Типы таблиц

Значение-типа-таблицы используется для определения структуры табличного значения.

тип-таблицы:
      tableтип-строки
тип-строки:

      [список-спецификаций-поля]

Результатом вычисления типа-таблицы является значение типа с базовым типом table.

Тип строки таблицы указывает имена столбцов и типы столбцов таблицы в виде закрытого типа записи. Чтобы все табличные значения соответствовали типу table, тип строки имеет тип record (пустой открытый тип записи). Таким образом, тип таблицы является абстрактным, так как никакое табличное значение не может иметь тип строк типа 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

Значение типа таблицы также содержит определение ключей табличного значения. Ключ — это набор имен столбцов. В качестве первичного ключа можно назначить не более одного ключа. (В языке M ключи таблиц не имеют семантического значения. Однако обычно для внешних источников данных, таких как базы данных или веб-каналы OData, определяются ключи в таблицах. Power Query использует сведения о ключе для повышения производительности расширенных функций, таких как операции объединения между источниками.)

Функции стандартной библиотеки Type.TableKeys, Type.AddTableKey и Type.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, с помощью типа, допускающего значение NULL:

тип-допускающий-значение-NULL:
      nullableтип

Результатом является абстрактный тип, допускающий значения типа T или значение null.

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

Приписывание type nullableT сокращается до приписывания type null или typeT (Помните, что типы, допускающие значение NULL, являются абстрактными и никакое значение не может относиться непосредственно к абстрактному типу.)

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

Функции стандартной библиотеки Type.IsNullable и Type.NonNullable можно использовать для проверки типа на допустимость значений NULL и для удаления допустимости значений NULL из типа.

Происходит следующее (для любого type T):

  • type T совместим с type 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

Приписываемый тип значения

Приписываемый тип значения — это тип, которому, согласно объявлению, соответствует значение. Если значение приписывается типу, то выполняется только ограниченная проверка соответствия. Язык M не выполняет проверку соответствия за пределами примитивного типа, допускающего значение NULL. Авторы программы на языке M, которые хотят приписать определениям типов значения, являющиеся более сложными чем примитивный тип, допускающий значение NULL, должны обеспечить соответствие таких значений этим типам.

Значение можно приписать типу с помощью функции библиотеки Value.ReplaceType. Эта функция возвращает либо новое значение приписываемого типа, либо ошибку, если новый тип несовместим с собственным примитивным типом значения. В частности, эта функция возвращает ошибку при попытке приписать абстрактный тип, такой как any.

Функции библиотеки могут вычислять и приписывать сложные типы результатам на основе приписываемых типов входных значений.

Приписываемый тип значения можно получить с помощью функции библиотеки Value.Type. Например:

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

Эквивалентность типов и совместимость

Эквивалентность типов в языке M не определена. Значения двух любых типов, сравниваемые на предмет равенства, могут возвращать или не возвращать true. Однако связь между этими двумя типами (независимо от того, получено ли значение true или false) всегда будет одинаковой.

Совместимость между заданным типом и примитивным типом, допускающим значение 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