Tipos

Um valor de tipo é um valor que classifica outros valores. Um valor classificado por um tipo obedece a esse tipo. O sistema de tipos de M é composto pelas seguintes categorias de tipos:

  • Tipos primitivos, que classificam valores primitivos (binary, date, datetime, datetimezone, duration, list, logical, null, number, record, text, time, type) e incluem alguns tipos abstratos (function, table, any, anynonnull e none)

  • Tipos de registro, que classificam valores de registro com base em nomes de campo e em tipos de valor

  • Tipos de lista, que classificam listas usando apenas um tipo de base de item

  • Tipos de função, que classificam valores de função com base nos tipos de seus parâmetros e valores de retorno

  • Tipos de tabela, que classificam valores de tabela com base em nomes de coluna, tipos de coluna e chaves

  • Tipos anuláveis, que classificam o valor nulo além de todos os valores classificados por um tipo base

  • Tipos de tipo, que classificam valores que são tipos

O conjunto de tipos primitivos inclui os tipos de valores primitivos e alguns tipos abstratos, que são tipos que não classificam os valores de maneira exclusiva: function, table, any, anynonnull e none. Todos os valores de função obedecem o tipo abstrato function, todos os valores de tabela ao tipo abstrato table, todos os valores ao tipo abstrato any, todos os valores não nulos ao tipo abstrato anynonnull e nenhum valor ao tipo abstrato none. Uma expressão do tipo none deve gerar um erro ou falhar ao ser encerrada, pois não é possível produzir nenhum valor que obedeça o tipo none. Observe que os tipos primitivos function e table são abstratos, porque nenhuma função ou tabela é diretamente desses tipos, respectivamente. Os tipos primitivos record e list não são abstratos porque representam um registro aberto sem campos definidos e uma lista do tipo any, respectivamente.

Todos os tipos que não são membros do conjunto fechado de tipos primitivos e suas contrapartes anuláveis são chamados coletivamente de tipos personalizados. Tipos personalizados podem ser escritos usando um type-expression:

expressão-de-tipo:
      expressão-primária

      typetipo-primário
tipo:
      primary-expression
      tipo-primário
tipo-primário:
      tipo-primitivo
      tipo-de-registro
      tipo-de-lista
      tipo-de-função
      tipo-de-tabela
      tipo-que-permite-valor-nulo
tipo-primitivo:
um de
      any anynonnull binary date datetime datetimezone duration function list logical
      none null number record table text time type

Os nomes de tipo-primitivo são palavras-chave contextuais reconhecidas somente em um contexto de tipo. O uso de parênteses em um contexto de tipo muda a gramática de volta para o contexto de expressão regular, exigindo o uso da palavra-chave type para voltar para o contexto de tipo. Por exemplo, para invocar uma função em um contexto de tipo, os parênteses podem ser usados:

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

Os parênteses também podem ser usados para acessar uma variável cujo nome colide com um nome de tipo-primitivo:

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

O exemplo a seguir define um tipo que classifica uma lista de números:

type { number }

De maneira semelhante, o exemplo a seguir define um tipo personalizado que classifica os registros com campos obrigatórios chamados X e Y, cujos valores são números:

type [ X = number, Y = number ]

O tipo atribuído de um valor é obtido usando a função de biblioteca padrão Value.Type, conforme mostrado nos seguintes exemplos:

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

O operador is é usado para determinar se o tipo de um valor é compatível com um determinado tipo, conforme mostrado nos exemplos a seguir:

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

O operador as verifica se o valor é compatível com o tipo determinado e gera um erro se não for. Caso contrário, ele retorna o valor original.

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

Observe que os operadores is e as só aceitam tipos primitivos anuláveis como o operando à direita. A linguagem M não fornece um meio de verificar os valores quanto à conformidade com tipos personalizados.

Um tipo X é compatível com um tipo Y se, e somente se, todos os valores que obedecem X também obedecem Y. Todos os tipos são compatíveis com o tipo any e nenhum tipo (exceto pelo próprio none) é compatível com o tipo none. O gráfico a seguir mostra a relação de compatibilidade. (A compatibilidade do tipo é reflexiva e transitiva. Ela forma um malha com o tipo any como o valor superior e o tipo none como o inferior.) Os nomes dos tipos abstratos estão em itálico.

Compatibilidade de tipo

Os seguintes operadores são definidos para os valores de tipo:

Operador Resultado
x = y Igual
x <> y Diferente
x ?? y Coalesce

O tipo nativo de valores de tipo é o tipo intrínseco type.

Tipos primitivos

Os tipos na linguagem M formam uma hierarquia não contígua enraizada no tipo any, que é o tipo que classifica todos os valores. Qualquer valor de M obedece exatamente um subtipo primitivo de any. O conjunto fechado de tipos primitivos derivados do tipo any é o seguinte:

  • type null, que classifica o valor nulo.
  • type logical, que classifica os valores true e false.
  • type number, que classifica valores numéricos.
  • type time, que classifica valores de tempo.
  • type date, que classifica valores de data.
  • type datetime, que classifica valores de datetime.
  • type datetimezone, que classifica valores de datetimezone.
  • type duration, que classifica valores de duração.
  • type text, que classifica valores de texto.
  • type binary, que classifica valores binários.
  • type type, que classifica valores de tipo.
  • type list, que classifica valores de lista.
  • type record, que classifica valores de registro.
  • type table, que classifica valores de tabela.
  • type function, que classifica valores de função.
  • type anynonnull, que classifica todos os valores, exceto por nulo.
  • type none, que classifica nenhum valor.

Tipo any

O tipo any é abstrato, ele classifica todos os valores em M e todos os tipos em M são compatíveis com any. Variáveis do tipo any podem ser associadas a todos os valores possíveis. Como any é abstrato, ele não pode ser atribuído a valores, ou seja, nenhum valor tem diretamente o tipo any.

Tipos de lista

Qualquer valor que seja uma lista obedece o tipo intrínseco list, que não estabelece nenhuma restrição sobre os itens dentro de um valor de lista.

tipo-de-lista:
      {tipo-de-item}
tipo-de-item:
      tipo

O resultado da avaliação de um tipo-de-lista é um valor de tipo de lista cujo tipo base é list.

Os exemplos a seguir ilustram a sintaxe para declarar tipos de lista homogêneos:

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

Um valor obedece um tipo de lista se o valor é uma lista e cada item nesse valor de lista obedece o tipo de item do tipo de lista.

O tipo de item de um tipo de lista indica uma associação: todos os itens de uma lista compatível obedecem o tipo de item.

Tipos de registro

Qualquer valor que seja um registro obedece o tipo intrínseco record, que não estabelece nenhuma restrição sobre os itens dentro de um valor de registro. Um valor-de-tipo-de-registro é usado para restringir o conjunto de nomes válidos, bem como os tipos de valores que têm permissão para ser associados a esses nomes.

tipo-de-registro:
      [marcador-de-registro-aberto]
      [lista-de-especificação-de-camposopc]
      [lista-de-especificações-de-campo, marcador-de-registro-aberto]
lista-de-especificações-de-campo:
      especificação-do-campo
      especificação-de-campo,lista-de-especificações-de-campo
especificação-do-campo:

      optionalopc nome-do-campo especificação-de-tipo-de-campoopc
especificação-de-tipo-de-campo:

      =tipo-de-campo
tipo-de-campo:
      tipo
marcador-de-registro-aberto:

      ...

O resultado da avaliação de um tipo-de-registro é um valor de tipo cujo tipo base é record.

Os exemplos a seguir ilustram a sintaxe para declarar tipos de registro:

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

Os tipos de registro são fechados por padrão, o que significa que campos adicionais que não estão presentes na lista-de-especificações-de-campo não podem estar presentes em valores compatíveis. Incluir o marcador-de-registro-aberto no tipo de registro declara que o tipo é aberto, o que permite campos não presentes na lista de especificações de campo. As duas expressões a seguir são equivalentes:

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

Um valor obedecerá um tipo de registro se o valor for um registro e cada especificação de campo no tipo de registro for satisfeita. Uma especificação de campo será atendida se qualquer uma das seguintes opções for verdadeira:

  • Um nome de campo correspondente ao identificador da especificação existe no registro e o valor associado obedece o tipo da especificação

  • A especificação é marcada como opcional e nenhum nome de campo correspondente é encontrado no registro

Um valor compatível pode conter nomes de campo não listados na lista especificações de campo se, e somente se, o tipo de registro é aberto.

Tipos de função

Qualquer valor de função obedece o tipo primitivo function, que não estabelece nenhuma restrição sobre os tipos dos parâmetros formais da função nem sobre o valor retornado da função. Um valor de tipo-de-função personalizado é usado para estabelecer restrições de tipo sobre assinaturas dos valores de função compatíveis.

tipo-de-função:
      function (lista-de-especificações-de-parâmetroopc)tipo-de-retorno-da-função
lista-de-especificação-de-parâmetros:
      lista-de-especificação-de-parâmetros-exigidos
      lista-de-especificações-de-parâmetro-obrigatória
,lista-de-especificações-de-parâmetro-opcional
      lista-de-especificação-de-parâmetros-opcionais
lista-de-especificação-de-parâmetros-exigidos:
      especificação-de-parâmetros-exigidos
      especificação-de-parâmetro-obrigatória
,lista-de-especificações-de-parâmetro-obrigatória
especificação-de-parâmetros-exigidos:
      especificação-de-parâmetros
lista-de-especificação-de-parâmetros-opcionais:
      especificação-de-parâmetros-opcionais
      especificação-de-parâmetro-opcional
,lista-de-especificações-de-parâmetro-opcional
especificação-de-parâmetros-opcional:

      optionalespecificação-de-parâmetros
especificação-de-parâmetros:
      nome-do-parâmetro tipo-de-parâmetro
tipo-de-retorno-da-função:
      assertion
declaração:

      astipo-primitivo-que-permite-valor-nulo

O resultado da avaliação de um tipo-de-função é um valor de tipo cujo tipo base é function.

Os exemplos a seguir ilustram a sintaxe para declarar tipos de função:

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

Um valor de função obedece um tipo de função se o tipo retornado do valor da função é compatível com o tipo retornado do tipo de função e cada especificação de parâmetro do tipo de função é compatível com o parâmetro formal correspondente em termos de posição da função. Uma especificação de parâmetro é compatível com um parâmetro formal se o tipo-de-parâmetro especificado é compatível com o tipo do parâmetro formal e a especificação de parâmetro é opcional se o parâmetro formal é opcional.

Os nomes de parâmetro formais são ignorados para fins de determinação da conformidade do tipo de função.

Especificar um parâmetro como opcional torna implicitamente seu tipo anulável. O seguinte cria tipos de função idênticos:

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

Tipos de tabela

Um valor de tipo-de-tabela é usado para definir a estrutura de um valor de tabela.

tipo-de-tabela:
      tabletipo-de-linha
tipo-de-linha:

      [field-specification-listopt]

O resultado da avaliação de um tipo-de-tabela é um valor de tipo cujo tipo base é table.

O tipo de linha de uma tabela especifica os nomes de coluna e os tipos de coluna da tabela como um tipo de registro fechado. Para que todos os valores de tabela obedeçam o tipo table, seu tipo de linha é tipo record (o tipo de registro aberto vazio). Portanto, o tipo de tabela é abstrato, uma vez que nenhum valor de tabela pode ter o tipo de linha do tipo table (mas todos os valores de tabela têm um tipo de linha compatível com o tipo de linha do tipo table). O exemplo a seguir mostra a construção de um tipo de tabela:

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

Um valor de tipo de tabela também contém a definição das chaves de um valor de tabela. Uma chave é um conjunto de nomes de coluna. No máximo uma chave pode ser designada como a chave primária da tabela. (Em M, as chaves de tabela não têm nenhum significado semântico. No entanto, é comum que fontes de dados externas, como bancos de dados ou feeds OData, definam chaves em tabelas. O Power Query usa informações importantes para melhorar o desempenho da funcionalidade avançada, como operações de junção entre fontes.)

As funções de biblioteca padrão Type.TableKeys, Type.AddTableKey e Type.ReplaceTableKeys podem ser usadas para obter as chaves de um tipo de tabela, adicionar uma chave a um tipo de tabela e substituir todas as chaves de um tipo de tabela, respectivamente.

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

Tipos anuláveis

Para qualquer type T, uma variante anulável pode ser derivada usando o tipo-anulável:

tipo-anulável:
      nullabletipo

O resultado é um tipo abstrato que permite valores do tipo T ou o valor null.

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

A ascrição de type nullableT reduz para a ascrição de type null ou typeT. (Lembre-se de que os tipos que permitem valor nulo são abstratos e nenhum valor pode ser diretamente do tipo abstrato.)

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

As funções de biblioteca padrão Type.IsNullable e Type.NonNullable podem ser usadas para testar um tipo quanto à nulidade e remover a nulidade de um tipo.

O seguinte é válido (para qualquer type T):

  • type T é compatível com type nullable T
  • Type.NonNullable(type T) é compatível com type T

Os seguintes são equivalentes emparelhados (para qualquer 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 atribuído de um valor

O tipo atribuído de um valor é o tipo a que é declarado que um valor obedece.

É possível atribuir um tipo a um valor usando a função de biblioteca Value.ReplaceType. A função retorna um novo valor com o tipo atribuído ou gera um erro quando o novo tipo é incompatível com o valor.

Quando um valor é atribuído a um tipo, ocorre apenas uma verificação de conformidade limitada:

  • O tipo que está sendo atribuído precisa ser não abstrato, não anulável e compatível com o tipo primitivo intrínseco (nativo) do valor.
  • Quando um tipo personalizado que define a estrutura é atribuído, ele precisa corresponder à estrutura do valor.
    • Para registros: o tipo precisa ser fechado, precisa definir o mesmo número de campos que o valor e não pode conter campos opcionais. (Os nomes de campo do tipo e os tipos de campo substituirão aqueles que estiverem associados ao registro no momento. No entanto, os valores de campo existentes não serão verificados em relação aos novos tipos de campo).
    • Para tabelas: o tipo precisa definir o mesmo número de colunas que o valor. (Os nomes de coluna do tipo e os tipos de coluna substituirão aqueles que estiverem associados à tabela no momento. No entanto, os valores de coluna existentes não serão verificados em relação aos novos tipos de coluna).
    • Para funções: o tipo precisa definir o mesmo número de parâmetros necessários, bem como o mesmo número de parâmetros opcionais, como o valor. (O parâmetro do tipo e as declarações de retorno, bem como seus nomes de parâmetro, substituirão aqueles associados ao tipo do valor da função no momento. No entanto, as novas declarações não terão efeito sobre o comportamento real da função).
    • Para listas: o valor precisa ser uma lista. (No entanto, os itens de lista existentes não serão verificados em relação ao novo tipo de item).

As funções de biblioteca podem optar por computar e atribuir tipos complexos para a resultados com base nos tipos atribuídos dos valores de entrada.

O tipo atribuído de um valor pode ser obtido usando a função de biblioteca Value.Type. Por exemplo:

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

Equivalência e compatibilidade de tipos

A equivalência de tipo não é definida em M. Opcionalmente, uma implementação de M pode optar por usar as próprias regras a fim de executar comparações de igualdade entre valores de tipo. A comparação de igualdade entre dois valores de tipo deve resultar em true quando eles são considerados idênticos pela implementação e em false quando não. Em ambos os casos, a resposta retornada deve ser consistente quando os mesmos dois valores são comparados repetidamente. Observe que, em uma determinada implementação, comparar alguns valores de tipo idênticos (como (type text) = (type text)) pode retornar true, enquanto o mesmo pode não ocorrer ao comparar outros (como (type [a = text]) = (type [a = text])).

A compatibilidade entre um determinado tipo e um tipo primitivo anulável pode ser determinada usando a função de biblioteca Type.Is, que aceita um valor de tipo arbitrário como o primeiro e um valor de tipo primitivo anulável como o segundo argumento:

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

Não há suporte em M para determinar a compatibilidade de um determinado tipo com um tipo personalizado.

A biblioteca padrão inclui uma coleção de funções para extrair as características de definição de um tipo personalizado, de modo que testes de compatibilidade específicos podem ser implementados como expressões em M. Veja abaixo alguns exemplos; consulte a especificação da biblioteca de M para ver os detalhes completos.

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