Chapter 5. Expressions

An expression is a sequence of operators and operands. This chapter defines the syntax, order of evaluation of operands and operators, and meaning of expressions.

Operators

Expressions are constructed from operands and operators. The operators of an expression indicate which operations to apply to the operands. Examples of operators include +, -, *, and /. Examples of operands include literals, fields, and expressions.

There are the following kinds of operators:

  • Unary operators take one operand and use prefix notation such as -x.

  • Binary operators take two operands and use infix notation such as x + y.

  • Ternary operator. Only one ternary operator, ?:, exists; it takes three operands and uses infix notation (c? x: y).

  • Query comprehensions.

The order of evaluation of operators in an expression is constrained by the precedence and associativity of the operators. Unless otherwise specified, the order of evaluation of operands is undefined.

A single syntactic operator may have different meanings depending on the type of its operands. That is, an operator may be overloaded. In this case, the meaning and the return type is determined by selecting the most specific super type for both operands for which a meaning and return type are specified in Chapter 3.

Operator Precedence and Associativity

Precedence and associativity determine how operators and operands are grouped together. For example, the expression x + y * z is evaluated as x + (y * z) because the * operator has higher precedence than +. The following table summarizes all operators in order of precedence from highest to lowest.

Category

Operators

Primary

x.y f(x)

Unary

+ - ! ~ # identity unique

Multiplicity (unary postfix)

? + * #

Multiplicative

* / %

Additive

+ -

Shift

<< >>

Relational and type testing

< > <= >= in x:T

Equality

== !=

Logical And (conjunction)

&&

Logical Or (disjunction)

||

Null Coalescing

??

Conditional

?:

Query Comprehension

from join let where select group by accumulate

Where

where

Select

select

Bitwise And, Intersection

&

Bitwise Exclusive Or

^

Bitwise Or, Union

|

When an operand occurs between two operators with the same precedence, the associativity of the operators controls the order in which the operations are performed. All binary operators are left associative, that is, operations are performed left to right. For example, x + y + z is evaluated as (x + y) + z.

Precedence and associativity can be controlled using parentheses. For example, x + y * z first multiplies y by z and then adds the result to x, but (x + y) * z first adds x and y and then multiplies the result by z.

Member Access

A MemberAccessExpression takes an expression that resolves to a scope as the left operand and a symbol as the right operand. Evaluating the expression returns the value bound to the symbol in the scope.

MemberAccessExpression:  PrimaryExpression  . MemberNameMemberName:  Identifier

A MemberAccessExpression consists of a PrimaryExpression, followed by a “.” token, followed by a member selector. Consider the following MemberAccessExpression:

{Name = "Bill", Age = 23}.Age

The member access operator looks up the symbol Age in the scope defined by the instance:

{Name = "Bill", Age = 23}

Symbol Lookup

A symbol lookup is the process whereby the meaning of a name in a context is determined. A symbol lookup may occur as part of evaluating a SimpleName or a MemberAccess in an expression.

M is a lexically scoped language. Scopes introduce symbols and may nest, and an inner scope may introduce a symbol that hides a symbol in an outer scope. Initially a symbol is resolved against the lexically innermost scope. If no matching symbol is found in the innermost scope, lookup proceeds in the containing scope. This process continues until the outermost scope is reached, which is always a module.

The following are examples of scopes:

  • An entity definition

  • A module

  • A field definition

  • The left side of a where expression

  • A query expression

A member lookup of a name N in a type T is processed as follows: The set of all accessible members named N declared in T and the base types of T is constructed. If no members named N exist and are accessible, then the lookup produces no match.

Field declarations override lexical scoping to prevent the type of a declaration binding to the declaration itself. The ascribed type of a field declaration must not be the declaration itself; however, the declaration may be used in a constraint. Consider the following example:

type A;
type B {
    A : A;
}

The lexically enclosing scope for the type ascription of the field declaration A is the entity declaration B. With no exception, the type ascription A would bind to the field declaration in a circular reference that is an error. The exception allows lexical lookup to skip the field declaration in this case.

A declaration may be used within a constraint on the ascribed type, as in the following example:

type Node {
    Label : Text;
    Parent : Node;
}
Nodes : Node* where item.Parent in Nodes;

The right operand of the in clause stipulates that the Parent field of a node must be within the collection being defined, Nodes.

Initializers

Entity types and collections use a common initialization syntax.

An InitializationExpression constructs a new instance of a collection or entity.

InitializationExpression:
  { ElementInitializersopt }
  { ElementInitializers  , }
ElementInitializer:
  LeadingDottedIdentifier TypeAscriptionopt = Expression
  LeadingDottedIdentifer TypeAscriptionopt InitializationExpression
ElementInitializers:
  ElementInitializer
  ElementInitializers  , ElementInitializer
LeadingDottedIdentifier:
  DottedIdentifier
  . DottedIdentifier

Collection Initializer

The following example initializes the extent SmallNumbers to the collection of values 1, 2, 3, 4:

SmallNumbers { 1, 2, 3, 4 }

Enumeration Initializer

The following example initializes three extents: Colors, Makes, and Cars. Although there is no intrinsic enumeration type in the M language, the first two extents are used as enumerations.

Colors { Red = "Red", Blue = "Blue", Yellow = "Yellow" }
Makes { Ford = "Ford", Chevy = "Chevrolet" }
Cars {
    { Make = Makes.Ford, Color = Colors.Blue }
    { Make = Makes.Chevy, Color = Colors.Red }
}

Reference Initializer

M provides labeled collections to construct instances that reference each other. Consider the following type Person and extent People:

type Person {
    Id : Integer32 = AutoNumber();
    Name : Text;
    Age : Integer32;
    Spouse : Person;
} where identity Id;

People : Person* where item.Spouse in People;

The Spouse field references another Person. One way to initialize this structure is to explicitly assign the identity of each instance:

People {
    { Id = 0, Name = "Jack", Age = 23, Spouse = 1 },
    { Id = 1, Name = "Jill", Age = 25, Spouse = 0 },
}

This assumes that the values 0 and 1 are not already used in the Person extent and exposes unnecessary implementation details. M provides label values to initialize references without explicit manipulation of identity values. Consider the following example, which initializes the same preceding structure:

People {
    Jack { Name = "Jack", Age = 23, Spouse = Jill },
    Jill { Name = "Jill", Age = 25, Spouse = Jack },
}

The label Jack introduces an identifier that can be used to reference the instance. This allows the first instance, Jack, to reference the second instance, Jill, as a spouse and vice versa.

Non-Local Initialization

It is frequently useful to initialize a value in another structure. In the following example, computers have zero to many boards. This relationship is implemented as a reference from Board to Computer.

type Computer {
    Id : Integer32 = AutoNumber();
    Processor : Text;
} where identity Id;

type Board {
    Id : Integer32 = AutoNumber();
    Kind : Text;
    Computer : Computer;
} where identity Id;

Boards : Board* where item.Computer in Computers;
Computers : Computer*;

Creating an instance of a computer requires initializing both the computer and the boards. This can be initialized “bottom up” as follows:

Computers {
    MyPC { Processor = "x86"}
}

Boards {
     { Kind = "Graphics", Computer = Computers.MyPC },
     { Kind = "Sound", Computer = Computers.MyPC },
     { Kind = "Network", Computer = Computers.MyPC },
}

Using non-local initialization, this same structure can be initialized “top down” as follows:

Computers {
    MyPC { Processor = "x86",
            .Boards {
                 { Kind = "Graphics", Computer = MyPC },
                 { Kind = "Sound", Computer = MyPC },
                 { Kind = "Network", Computer = MyPC },
             }
    }
}

The dot prefix to the Boards label (.Boards) does not introduce a new label into the current scope. Rather, it looks up the symbol at the extent scope and adds the content to that extent.

Invocation Expression

The Identifier in an InvocationExpression resolves to a computed value declaration of the same name and arity. Evaluating an invocation expression causes each argument to be evaluated. The result of each argument is bound to the formal parameter in the corresponding position. The result of evaluating the body of the computed value declaration is the value of the invocation expression:

InvocationExpression:
  Identifier  InvocationExpressionArguments
  InvocationExpressionArguments:
  ( Argumentsopt )
Arguments:
  Argument
  Arguments  , Argument
Argument:
  Expression

Primary Expressions

The following rules define the grammar for primary expressions:

PrimaryExpression:

 

PrimaryCreationExpression

PrimaryCreationExpression:

 

Literal

 

SimpleName

 

ParenthesizedExpression

 

MemberAccessExpression

 

InvocationExpression

 

InitializationExpression

 

EntityTypeExpression

 

ContextVariable

Literal is defined in Section 2.4.3. EntityTypeExpression is defined in Section 3.6. The remaining non-terminals are defined in this section.

Simple Names

A SimpleName consists of a single identifier.

SimpleName:

 

Identifier

In the expression:

Person.Age

Both Person and Age are SimpleNames.

Parenthesized Expressions

A ParenthesizedExpression consists of an Expression enclosed in parentheses:

ParenthesizedExpression:  ( Expression   )

A ParenthesizedExpression is evaluated by evaluating the Expression within the parentheses.

Context Variable

The following rules define the grammar for context variables:

ContextVariable
  this
  value
  item

The context variable this is defined in Section 3.2. value and item are defined in Section 5.16.1.

Unary Operators

The following rules define the grammar for unary operators:

UnaryExpression:
  PrimaryExpression
  + PrimaryExpression
  - PrimaryExpression
  ! PrimaryExpression
  ~ PrimaryExpression
  PrimaryExpression #
  IdentityExpression
  UniqueExpression
IdentityExpression:
  identity Identifier
  identity ( Identifiers )
UniqueExpression:
  unique Identifier
  unique ( Identifiers )

The identity constraint is discussed in Section 3.6.2.

The type rules for unary operators are defined in Number (Section 3.5.3.1), Logical (Section 3.5.6), Binary (Section 3.5.7), and Collection (Section 3.7.2).

Examples of unary operators follow:

+1
-2
!true
~0x00
{1,2,3}#
identity Id
unique Name

Multiplicity

The following rules define the grammar for multiplicity operators:

MultiplicityExpression:
  UnaryExpression
  UnaryExpression  ?
  UnaryExpression  +
  UnaryExpression  *
  UnaryExpression  # IntegralRange
IntegralRange:
  IntegerLiteral
  IntegerLiteral  ..
  IntegerLiteral  .. IntegerLiteral

The type rules for multiplicity operators are defined in type operators in Section 3.4.

Examples of multiplicity expressions follow:

Integer32?
Text#2..4
{Name : Text; Age : Integer32}*

Arithmetic Operators

The following rules define the grammar for arithmetic operators:

AdditiveExpression:
  MultiplicativeExpression
  AdditiveExpression  + MultiplicativeExpression
  AdditiveExpression  - MultiplicativeExpression
MultiplicativeExpression:
  UnaryExpression
  MultiplicativeExpression  * MultiplicityExpression
  MultiplicativeExpression  / MultiplicityExpression
  MultiplicativeExpression  % MultiplicityExpression

The type rules on arithmetic operators are defined in Number (Section 3.5.3), Text (Section 3.5.4), Date (Section 3.5.9), and Time (Section 3.5.11).

Examples of arithmetic operators follow:

1 + 1
2 * 3
"Hello " + "World"

Shift Operators

The following rules define the grammar for shift operators:

ShiftExpression:
  AdditiveExpression
  ShiftExpression  << AdditiveExpression
  ShiftExpression  >> AdditiveExpression

The type rules on shift operators are defined in Binary, Section 3.5.7.

Relational and Type-Testing Operators

The following rules define the grammar for relational and type testing operators:

RelationalExpression:
  ShiftExpression
  RelationalExpression  < ShiftExpression
  RelationalExpression  > ShiftExpression
  RelationalExpression  <= ShiftExpression
  RelationalExpression  >= ShiftExpression
  RelationalExpression  in ShiftExpression
  RelationalExpression  : ShiftExpression

The type rules on relational and type-testing operators are throughout Chapter 3.

Equality Operators

The following rules define the grammar for equality operators:

EqualityExpression:
  RelationalExpression
  EqualityExpression  == RelationalExpression
  EqualityExpression  != RelationalExpression

The type rules on equality operators are throughout Chapter 3.

Logical Operators

The following rules define the grammar for logical operators.

LogicalAndExpression:
  EqualityExpression
  LogicalAndExpression  && EqualityExpression
LogicalOrExpression:
  LogicalAndExpression
  LogicalOrExpression  || LogicalAndExpression

The type rules on logical operators are defined in Logical, Section 3.5.6.

Conditional Operators

There are two conditional operators: coalesce and conditional.

Coalescing Operator

The ?? operator is called the null coalescing operator:

NullCoalescingExpression:
  LogicalOrExpression
  LogicalOrExpression  ?? NullCoalescingExpression

A null coalescing expression of the form a ?? b requires a to be nullable. If a is not null, the result of a ?? b is a; otherwise, the result is b. The operation evaluates b only if a is null.

b must be of the same type as a without the value null.

Conditional Operator

The ?: operator is called the conditional operator. It is at times also called the ternary operator.

ConditionalExpression:
  NullCoalescingExpression
  NullCoalescingExpression  ? Expression  : Expression

A conditional expression of the form b ? x : y first evaluates the condition b. Then, if b is true, x is evaluated and becomes the result of the operation. Otherwise, y is evaluated and becomes the result of the operation. A conditional expression never evaluates both x and y.

The conditional operator is right-associative, meaning that operations are grouped from right to left. For example, an expression of form a ? b : c ? d : e is evaluated as a ? b : (c ? d : e).

The first operand of the ?: operator must be an expression of a type that can be implicitly converted to Logical; otherwise a compile-time error occurs. The middle and left operands must be of compatible types. The result of the conditional is the least specific type.

Query Expressions

Query expressions provide a language integrated syntax for queries that is similar to relational and hierarchical query languages such as Transact SQL and XQuery.

A query expression begins with a from clause and ends with either a select, group, or accumulate clause. The initial from clause can be followed by zero or more from, let, or where clauses. Each from clause is a scope that introduces one or more iteration identifiers ranging over a sequence or a join of multiple sequences. Each let clause computes a value and introduces an identifier representing that value, and each where clause is a filter that excludes items from the result that do not satisfy the Logical expression. The final select, accumulate, or group clause specifies the shape of the result in terms of the iteration identifiers(s). Finally, an into clause can be used to “splice” queries by treating the results of one query as a generator in a subsequent query:

QueryExpression:
  ConditionalExpression
  QueryFromClause  QueryBody
QueryBody:
  QueryBodyClausesopt QueryConstructor  QueryContinuationopt
QueryBodyClauses:
  QueryBodyClause
  QueryBodyClauses  QueryBodyClause
QueryBodyClause:
  QueryFromClause
  QueryLetClause
  QueryWhereClause
  QueryJoinClause
  QueryJoinIntoClause
QueryConstructor:
  QuerySelectClause
  QueryGroupClause
  QueryAccumulateClause
QueryContinuation:
  intoIdentifier  QueryBody
QueryFromClause:
  from Identifier  in ConditionalExpression
QueryLetClause:
  let Identifier  = ConditionalExpression
QueryJoinClause:
  join Identifier  in Expression  on Expression  equals ConditionalExpression
QueryJoinIntoClause:
  join Identifier  in Expression  on Expression  equals Expression  into Identifier
QueryWhereClause:
  where ConditionalExpression
QuerySelectClause:
  select ConditionalExpression
QueryGroupClause:
  group Expression  by ConditionalExpression
QueryAccumulateClause:
  QueryLetClause  accumulate ConditionalExpression

The accumulate keyword generalizes Sum, Min, Max, et cetera. Its purpose is to repeatedly apply an expression to each element in a collection and accumulate the result. Consider the following fragment:

from c in CollectionExpression
let a = Expression
accumulate Expression

As an example, the following M code sums the elements in the collection Numbers:

from n in Numbers
let i = 0
accumulate i + n

Compact Query Expressions

There are two compact forms for query expressions the binary infix where and select.

Where Operator

The infix where operation filters elements from a collection that match a predicate:

WhereExpression:
  QueryExpression
  QueryExpression  where WhereExpressions
WhereExpressions:
  WhereExpression
  WhereExpressions  , WhereExpression

The WhereExpression introduces the identifier value into the scope of the right side to refer to an element of the collection on the left. The right side may also use any other identifiers that are in lexical scope.

If the QueryExpression returns a collection of collections (e.g. Number*); then the right side also introduces the identifier item into scope to refer to elements of the base domain.

The following example uses value to filter the Numbers collection:

OneToTen : Number where value > 0 && value <= 10;

When used over a collection type, value refers to the collection:

SmallCollection : Number* where value.Count == 2;

SmallCollectionOneToTen : (Number where value > 0 && value <=10)*
    where value.Count < 10;

The keyword item constrains the values of elements of elements. The following declaration is equivalent to the previous:

SmallCollectionOneToTen : Number* where value.Count < 10 &&
    item > 0 &&
    item <= 10;

Formalizing this convention:

QueryExpression where Expression

is a compact syntax for one of the two following expressions:

from value in QueryExpression
where Expression
select value

from value in QueryExpression
where (from item in value select Expression).All
select value

The choice between the first and second expansions is made based on the type of the left operand to the where clause. If it is a collection of collections the second expansion is used; otherwise, if it is a collection, the first expansion is used; otherwise it is a type error.

Select Operator

The select operator applies an expression to every element in a collection and returns the results in a new collection:

SelectExpression:
  WhereExpression
  WhereExpression  select Expression

The SelectExpression introduces the identifier value into the scope of the right side to refer to an element of the collection on the left. The right side may also use any other identifiers that are in lexical scope.

Examples of the select operator follow:

{1, 2, 3} select value * 2
People select value.Name
{{}, {1}, {1,1}} select value#

Binary and Collection Operators

The following rules define the grammar for binary and collection operators:

InclusiveOrExpression:
  ExclusiveOrExpression
  InclusiveOrExpression  | ExclusiveOrExpression
ExclusiveOrExpression:
  AndExpression
  ExclusiveOrExpression  ^ AndExpression
AndExpression:
  SelectExpression
  AndExpression  & SelectExpression

The type rules on binary and collection operators are defined in Binary, Section 3.5.7, and Collection, Section 3.7.2.

Expressions

An expression is a sequence of operands and operators. Applying the operator to the operand yields a value:

  • Expression:

  • InclusiveOrExpression

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.138.97