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
enone
)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
type
tipo primário
type:
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.
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:
[
aberto-marcador de registro]
[
opção de listade especificação de campo]
[
lista de especificações de campo , marcador de registro aberto]
lista-de-especificações-de-campo:
especificação-do-campo
lista de especificações de campo de especificação,
de campo
especificação-do-campo:
optional
opc 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 (
listade especificações de parâmetros opt)
function-return-type
lista-de-especificação-de-parâmetros:
lista-de-especificação-de-parâmetros-exigidos
lista,
de especificações de parâmetros obrigatórios lista de especificações de parâmetros opcionais
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ório,
lista-de-especificações-de-parâmetros-obrigatórios
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
optional-parameter-specification,
lista de especificações de parâmetros opcionais
especificação-de-parâmetros-opcional:
optional
especificação de parâmetro
especificação-de-parâmetros:
nome-do-parâmetro tipo-de-parâmetro
tipo-de-retorno-da-função:
assertion
declaração:
as
tipo primitivo anulável
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:
table
tipo de linha
tipo-de-linha:
[
opção de listade especificação de campo]
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:
nullable
tipo
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 atribuição de type nullable
T se reduz à atribuição de type null
ou type
T. (Lembre-se de que os tipos anuláveis 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 comtype nullable T
Type.NonNullable(type T)
é compatível comtype 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