相关文章推荐
玩篮球的跑步机  ·  Spark ...·  1 年前    · 

类型值是一个对其他值进行分类的值 。 认为按类型分类的值符合该类型。 M 类型系统由以下类型组成:

  • 基元类型可以对基元值( binary date datetime datetimezone duration list logical null number record text time type )进行分类,并且基元类型还包括许多抽象类型( function table any anynonnull none

  • 记录类型(根据字段名称和值类型对记录值进行分类)

  • 列表类型(使用单一项基类型对列表进行分类)

  • 函数类型(根据其参数和返回值类型对函数值进行分类)

  • 表类型(根据列名、列类型和键对表值进行分类)

  • 可为 null 的类型(不仅可以按基类型对所有值进行分类,还可以对 null 值进行分类)

  • 类型类型(对属于类型的值进行分类)

    一组基元类型包含基元值的类型和许多抽象类型,这些类型不对任何值进行唯一分类: function table any anynonnull none 。 所有函数值都符合抽象类型 function ,所有表值都符合抽象类型 table ,所有值都符合抽象类型 any ,所有非 null 值都符合抽象类型 anynonnull ,没有值符合抽象类型 none 。 类型为 none 的表达式必须引发错误或无法终止,因为无法生成符合类型 none 的值。 请注意,基元类型 function table 是抽象的,因为任何函数或表都不直接属于这些类型。 基元类型 record list 是非抽象的,因为它们分别表示一个没有定义字段的 open 记录和一个 any 类型的列表。

    所有不属于封闭基元类型集成员的类型及其可为空对应对象的类型统称为自定义类型。 可以使用 type-expression 编写自定义类型:

    type-expression:
    primary-expression

    type primary-type
    primary-expression
    primary-type
    primary-type:
    primitive-type
    record-type
    list-type
    function-type
    table-type
    nullable-type
    primitive-type:
    one of
    any anynonnull binary date datetime datetimezone duration function list logical
    none null number record table text time type

    基元类型名称是上下文关键字,仅在类型上下文中识别 。 在类型上下文中使用括号将语法移回正则表达式上下文,需要使用关键字类型才能返回到类型上下文。 例如,要在类型上下文中调用函数,可以使用括号:

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

    括号还可用于访问名称与基元类型名称相冲突的变量:

    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 运算符仅接受可为空的基元类型作为其正确的操作数。 M 不提供用于检查值是否符合自定义类型的方法。

    当且仅当符合 X 的所有值也符合 Y 时,类型 X 与类型 Y兼容。 所有类型都与类型 any 兼容,没有类型(除了 none 本身)与类型 none 兼容。 下图显示了兼容性关系。 (类型总是与其自身兼容,且类型兼容性可传递。它形成了一个点阵,类型 any 在最上,类型 none 在最下。)抽象类型的名称设置为斜体。

    为类型值定义了以下运算符:

    类型值的本机类型是内部类型 type

    M 语言中的类型形成一个源于类型 any 的不相交的层次结构,该类型对所有值进行分类。 任何 M 值都符合 any 的恰好一种基元子类型。 从类型 any 派生的基元类型闭集如下:

  • type null 可以对 null 值进行分类。
  • type logical 可以将值 true 和 false 进行分类。
  • type number 可以对数字值进行分类。
  • type time 可以对时间值进行分类。
  • type date 可以对日期值进行分类。
  • type datetime 可以对日期/时间值进行分类。
  • type 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:

    计算 list-type 的结果是一个基类型为 list列表类型值

    以下示例演示了用于声明同类列表类型的语法:

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

    如果值是一个列表,并且该列表值中的每一项都符合列表类型的项类型,则该值符合列表类型。

    列表类型的项类型暗示着一种绑定:符合的列表中所有项都符合项类型。

    任何记录值都符合内部类型 record,该类型不会对记录值中的字段名称或值施加任何限制。 使用 record-type 值限制有效名称集,以及允许与这些名称相关联的值类型。

    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:
    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, ... ]
    

    记录类型默认为 closed,这意味着符合值中不可以存在 fieldspecification-list 中没有的字段。 在记录类型中包含 openrecord-marker,可将该类型声明为 open,允许其包含字段规范列表中没有的字段。 以下两个表达式等效:

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

    如果值是一条记录,且满足记录类型中的每项字段规范,则该值符合记录类型。 若以下任何一项属实,则满足字段规范:

  • 记录中存在与规范的标识符匹配的字段名称,并且关联的值符合规范的类型

  • 该规范标记为可选,并且在记录中没有相应的字段名称

    当且仅当记录类型为 open 时,符合值可以包含字段规范列表中未列出的字段名。

    任何函数值都符合基元类型 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 类型与形式参数的类型兼容,并且如果形式参数是可选的,则参数规范与形式参数兼容。

    为了确定函数类型符合性,会忽略形式参数名称。

    使用 table-type 值来定义表值的结构。

    table-type:
          tablerow-type
    row-type:

          [field-specification-list]

    计算 table-type 的结果是一个基类型为 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.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,可以使用 nullable-type 来派生可为 null 的变量:

    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

        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 的,并要与值的内部(本机)基元类型兼容。
  • 如果值归属于定义结构的自定义类型,则该类型必须与值的结构匹配。
  • 对于记录:该类型必须是限定好的,且必须定义与值相同的字段数,不得包含任何可选字段。 (该类型的字段名称和字段类型将替换当前与记录关联的字段名称和字段类型。但系统不会根据新字段类型检查现有字段值。)
  • 对于表:该类型必须定义与值相同的列数。 (该类型的列名和列类型将替换当前与表关联的列名和列类型。但系统不会根据新列类型检查现有列值。)
  • 对于函数:该类型必须定义与值数量相同的必需参数以及可选参数。 (该类型的参数和返回断言及其参数名称将替换与函数值的当前类型关联的相应内容。但新的断言不会影响函数的当前行为。)
  • 对于列表:该值必须是列表。 (但系统不会根据新项类型检查现有列表项。)
  • 库函数可以根据输入值的归属类型来选择计算复杂类型并将其归属于结果。

    可以使用库函数 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]))可能不会返回该值。

    可以使用库函数 Type.Is 来确定指定类型和可为 null 的基元类型之间的兼容性,该函数接受任意类型值作为其第一个参数,接受可为 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) 
    Type.FunctionReturn(
            type function (x as number, optional y as text) as number) 
      // type number 
    
  •