The Microsoft code name "M" Modeling Language Specification - Expressions

November 2009

[This content is no longer valid. For the latest information on "M", "Quadrant", SQL Server Modeling Services, and the Repository, see the Model Citizen blog.]

[This documentation targets the Microsoft SQL Server Modeling CTP (November 2009) and is subject to change in future releases. Blank topics are included as placeholders.]

Sections:
1: Introduction to "M"
2: Lexical Structure
3: Text Pattern Expressions
4: Productions
5: Rules
6: Languages
7: Types
8: Computed and Stored Values
9: Expressions
10: Module
11: Attributes
12: Catalog
13: Standard Library
14: Glossary

9 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.

9.1 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

Precedence determines how operators are grouped with operands. 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 §2.

9.1.1 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

+  -

Relational and type testing

<  >  <=  >=  in !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.

9.2 Member access

A MemberAccessExpression takes an expression which 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. 

syntax MemberAccessExpression

    = PrimaryExpression "." MemberName; 

syntax MemberName

    = 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}

9.2.1 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 which 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 hand 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 which 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 value.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.

9.3 Initializers

This needs to be cleaned up to make entities, collections, and lists primitive. 

Entity types and collections use a common initialization syntax. 

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

syntax InitializationExpression

    = "{" Elements? "}"

    | "[" Elements? "]"

    | Kind? "{" Properties "}";

syntax Elements

    = Expression ","?

    | Elements "," Expression;

syntax Properties

    = Property ","?

    | Properties "," Property;

syntax Kind

       = Identifier;

9.3.1 Collection Initializer

The following are three examples of unordered collections:

{ 1, 2, 3, 4 }

{ 1, 1, 1, 1, }

{ [], "Hello", 24 }

9.3.2 List Initializer

The following are three examples of lists:

[ "Red", "Blue", "Green"]

[]

[{}, 3, [], ]

9.3.3 Record Initializer

The following are three examples of records:

{ Name => "John", Age => 24 }

{ Title => "Specifying Systems", Author => "Leslie Lamport", }

{ Colors => {"Red", "Blue", "Green"} }

9.3.4 Kind Pattern

The kind pattern allows records to have a discriminator. It is a syntactic transformation that transforms the following pattern Identifier { . . . } into { Kind => "Identifier", . . .}.

Consider the following examples:

Person { Name => "John", Age => 24 }

Cat { Name => "Fluffy", Age => 2 }

Dog { Name => "Rover", Age => 8 }

These are equivalent to:

{ Kind => "Person", Name => "John", Age => 24 }

{ Kind => "Cat", Name => "Fluffy", Age => 2 }

{ Kind => "Dog", Name => "Rover", Age => 8 }

9.3.5 Non Local Initialization

This section needs to be updated to new structures design and we should look at whether the feature is needed any longer. 

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 value.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 {

    Graphics { Computer => Computers.MyPC },

    Sound { Computer => Computers.MyPC },

    Network { Computer => Computers.MyPC },

}   

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

Computers {

    MyPC { Processor => "x86",

           .Boards {

               Graphics { Computer => MyPC },

               Sound { Computer => MyPC },

               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.

9.4 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. 

syntax InvocationExpression

    = Identifier "(" Arguments? ")"; 

syntax Arguments

    = Argument 

    | Arguments "," Argument; 

syntax Argument

    = Expression; 

9.5 Primary expressions

The following rules define the grammar for primary expressions.

syntax PrimaryExpression

    = Literal 

    | SimpleName 

    | ParenthesizedExpression 

    | MemberAccessExpression 

    | InvocationExpression 

    | InitializationExpression 

    | EntityTypeExpression

    | ContextVariable

    | AboutExpression; 

Literal is defined in §2.5. EntityTypeExpression is defined in §7.5. The remaining non terminals are defined in this section, §8.3.

9.5.1 Simple names

A SimpleName consists of a single identifier.

syntax SimpleName

    = Identifier;

In the expression:

Person.Age

Both Person and Age are SimpleNames.

9.5.2 Parenthesized expressions

A ParenthesizedExpression consists of an Expression enclosed in parentheses.

syntax ParenthesizedExpression

    = "("  Expression ")";

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

9.5.3 Context variable

The following rules define the grammar for context variables.

syntax ContextVariable

    = "value";

Value is defined in §9.14.1.

9.5.4 About operator

The about is used to obtain the entry in Language.Catalog (§12) for a symbol. About is a special form, not a computed value. The symbol must be visible in the current scope and is resolved using normal symbol resolution rules. On evaluation, rather than returning the value the symbol represents, about returns a reference to the metadata for the symbol itself.

syntax AboutOperator

    = "about" "(" DottedIdentifer ")";

Examples:

module Test {

    import Language.Catalog;

    type Person {

        Name : Text;

        Age : Integer;

    }

    People : {Person*};

    Children() { People where Age < 18 }

    Documentation : {{

        About : Language.Catalog.Declaration,

        Description : Text

    }*};

    Documentation {

        {

            About => about(Person).Declaration,

            Description => "Describes people"

        },

        {

            About => about(People).Declaration,

            Description => "Contains people"

        },

        {

            About => about(Children).Declaration,

            Description => "Extracts young people"

        },

    }

}

The following table defines the classes of symbols that are resolved with about and the resulting entry in Language.Catalog.

Symbol Language.Catalog

Computed Value

ComputedValueGroup

Extent

Extents

Module

Modules

Type

Types

9.6 Unary operators

The following rules define the grammar for unary operators.

syntax UnaryExpression

    = PrimaryExpression 

    | "+" PrimaryExpression 

    | "-" PrimaryExpression 

    | "!" PrimaryExpression 

    | PrimaryExpression "#"

    | IdentityExpression 

    | UniqueExpression;

syntax IdentityExpression

    = identity Identifier 

    | identity "(" Identifiers ")"; 

syntax UniqueExpression

    = unique Identifier 

    | unique "(" Identifiers ")";

The identity constraint is discussed in §7.5.2.

The type rules for unary operators are defined in Number §7.4.3.1, Logical §7.4.5, and Collection §7.6.2.

Examples of unary operators follow:

+1

-2

!true

!0x00

{1,2,3}#

identity Id

unique Name

9.7 Multiplicity

The following rules define the grammar for multiplicity operators.

syntax MultiplicityExpression

    = UnaryExpression 

    | UnaryExpression  "?" 

    | CollectionTypeExpression

    | ListTypeExpression;

The type rules for multiplicity operators are defined in type operators §7.3.

Examples of multiplicity expressions follow:

Integer32?

[Text#2..4]

{{Name : Text; Age : Integer32}*}

9.8 Arithmetic operators

The following rules define the grammar for arithmetic operators.

syntax AdditiveExpression

    = MultiplicativeExpression 

    | AdditiveExpression "+" MultiplicativeExpression 

    | AdditiveExpression "-" MultiplicativeExpression; 

syntax MultiplicativeExpression

    = UnaryExpression 

    | MultiplicativeExpression "*" MultiplicityExpression 

    | MultiplicativeExpression "/" MultiplicityExpression 

    | MultiplicativeExpression "%" MultiplicityExpression ;

The type rules on arithmetic operators are defined in Number §7.4.3, Text §7.4.4, Date §7.4.7, Time §7.4.8.

Examples of arithmetic operators follow:

1 + 1

2 * 3

"Hello " + "World"

9.9 Relational and type-testing operators

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

syntax RelationalExpression

    = AdditiveExpression 

    | RelationalExpression  "<"   ShiftExpression 

    | RelationalExpression  ">"   ShiftExpression 

    | RelationalExpression  "<="  ShiftExpression 

    | RelationalExpression  ">="  ShiftExpression 

    | RelationalExpression  "in"  ShiftExpression 

    | RelationalExpression  ":"   ShiftExpression; 

The type rules on relational and type-testing operators are throughout §2.

9.10 Equality operators

The following rules define the grammar for equality operators.

syntax EqualityExpression

    = RelationalExpression 

    | EqualityExpression "==" RelationalExpression 

    | EqualityExpression "!=" RelationalExpression; 

The type rules on equality operators are throughout §2.

9.11 Logical operators

The following rules define the grammar for logical operators.

syntax LogicalAndExpression

    = EqualityExpression 

    | LogicalAndExpression "&&" EqualityExpression; 

syntax LogicalOrExpression:

    = LogicalAndExpression 

    | LogicalOrExpression "||" LogicalAndExpression; 

The type rules on logical operators are defined in Logical §7.4.5.

9.12 Conditional operators

There are two conditional operators coalesce and conditional.

9.12.1 Coalescing operator

The ?? operator is called the null coalescing operator.

syntax 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.

9.12.2 Conditional operator

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

syntax 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 the 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.

9.13 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, let and join clause introduces a scope that adds one iteration identifier that is in scope for the remainder of the query expression.

syntax QueryExpression

    = ConditionalExpression

    | QueryFromClause QueryBody; 

syntax QueryBody

    = QueryBodyClauses? QueryConstructor;

syntax QueryBodyClauses

    = QueryBodyClause 

    | QueryBodyClauses  QueryBodyClause; 

syntax QueryBodyClause

    = QueryFromClause 

    | QueryLetClause 

    | QueryWhereClause 

    | QueryJoinClause;

syntax QueryConstructor

    = QuerySelectClause 

    | QueryGroupClause 

    | QueryAccumulateClause; 

syntax QueryFromClause

    = "from"  Identifier  "in"  ConditionalExpression; 

syntax QueryLetClause

    = "let"  Identifier  "="  ConditionalExpression; 

syntax QueryJoinClause

    = "join"  Identifier  "in"  Expression  "on"  Expression  "equals"

      ConditionalExpression; 

syntax QueryWhereClause

    = "where"  ConditionalExpression; 

syntax QuerySelectClause

    = "select"  ConditionalExpression; 

QueryGroupClause

    = "group"  Expression "by"  ConditionalExpression; 

QueryAccumulateClause

    = QueryLetClause  "accumulate"  ConditionalExpression; 

9.13.1 From clause

The from clause introduces an identifier that ranges over a collection. The following query body is interpreted once for each element in the collection with the identifier bound to that element. Thus if there are two from clauses in sequence, the query body following the second will be interpreted for the first element of the first collection and every element of the second collection, then for the second element of the first collection and every element of the second collection, etc. This ordering will be respected for ordered collections (lists). For unordered collections the elements may be processed in any order.

The following are examples of the from clause:

from n in {1,2,3,4,5}

select n

from n1 in {1,2,3,4,5}

from n2 in {1,2,3,4,5}

select n1*n2

from p in People

select { Name => p.Name, Age => p.Age }

9.13.2 Where clause

A where clause contains a predicate that filters further interpretation within a query expression. The expression

where true

has no effect.

The expression

where false

blocks further interpretation. The query expression will always return the empty set. 

The following are examples of the where clause:

from n in {1, 2, 3, 4, 5}

where n%2 == 0

select n

from p in People

where p.Age > 17

select p

from n1 in {1,2,3,4,5}

from n2 in {1,2,3,4,5}

where n1 != n2

select n1*n2

9.13.3 Select clause

The select clause is a query constructor and determines results of the query expression. Consider a select clause of the following form:

select Expression

Where expression has type T. The type of the query expression will be [T*] if all collections in the from and join expressions are ordered and will be {T*} otherwise. 

The following are examples of the select clause:

from n in {1,2,3,4,5}

select n%2

from p in People

select p.Name

from p in People

select {Name => p.Name, Age => p.Age}

9.13.4 Join clause

The join clause is a compact syntax for a common pattern. The expression

join x in Collection on Expression1 equals Expression2

is equivalent to

from x in Collection

where Expression1 == Expression2

The following are examples of the join clause:

from n1 in {1,2,3,4,5}

join n2 in {1,2,3,4,5} on n1 equals n2

select n1*n2

from c in Customers

join o in Orders on c.Id equals o.CustomerId

select { Customer => c.Name, OrderedOn => o.Submitted }

from a in Actors

join r in Roles on a.Id equals r.ActorId

join m in Moves on m.Id equals r.MovieId

select { Actor => a.FirstName + " " + a.LastName, Movie => m.Title }

9.13.5 Let clause

The let clause is a compact syntax for a common pattern. The expression

let x = Expression

is equivalent to

from x in {Expression}

The following are examples of the let clause:

from n in {1,2,3,4,5}

let pi = 3.1415

select { Radius => n, Area => n*n*pi }

from p in People

let FullName = p.First + " " + p.Last

select { Name => FullName, Age => p.Age }

from a in Actors

let FullName = a.FirstName + " " + a.LastName

join r in Roles on a.Id equals r.ActorId

join m in Moves on m.Id equals r.MovieId

select { Actor => FullName, Movie => m.Title }

9.13.6 Group By clause

The group by clause is a compact syntax for a common pattern. The expression:

from c in Collection

group c by F(c)

is equivalent to

from key in (Collection select F(value)).Distinct

select { Key => key, Value => (Collection where key == F(value)) }

The following are examples of the group by clause:

from n in {1,2,3,4,5}

group n by n%2

from p in People

group p by p.Age

from c in Cars

group c by c.Color

 

9.13.7 Accumulate clause

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.

The type of the expression in the let clause and the expression in the accumulate clause must be compatible. This type is the type of the expression.

Consider the following fragment:

from c in Collection

let a = Expression1

accumulate Expression2

Expression1 is evaluated and the result is bound to the identifier a. Expression2 is evaluated once for every element of Collection. Upon each evaluation the result is bound to the identifier a. 

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

from n in Numbers

let i = 0

accumulate i + n

The following computes minimum of a collection of {Integer32*}:

from n in Integers

let i = MaxInteger32

accumulate i < n ? i : n

The following computes the maximum of a collection of {Integer32*}:

from n in Integers

let i = MinInteger32

accumulate i > n ? i : n

The following returns false if a collection contains false and true otherwise:

from b in TruthValues

let r = true

accumulate b && r

The following returns true if a collection contains true and false otherwise:

from b in TruthValues

let r = false

accumulate b || r

9.14 Compact query expressions

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

9.14.1 Where operator

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

syntax WhereExpression

    = QueryExpression 

    | QueryExpression "where" WhereExpressions;

syntax WhereExpressions

    = WhereExpression 

    | WhereExpressions "," WhereExpression; 

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

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;

Formalizing this convention:

QueryExpression where Expression

Is a compact syntax for the following expression:

from value in QueryExpression

where Expression

select value

9.14.2 Select operator

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

syntax SelectExpression

    = WhereExpression 

    | WhereExpression "select" Expression;

The SelectExpression introduces the identifier value into the scope of the right hand side to refer to an element of the collection on the left. The right hand 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 { Name => value.Name, Age => value.Age }

{{}, {1}, {1,1}} select value#

The select operator can be rewritten to a full query expression:

Collection select Expression

Is equivalent to:

from value in Collection

select Expression

9.15 Binary and Collection operators

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

syntax InclusiveOrExpression

    = ExclusiveOrExpression 

    | InclusiveOrExpression "|" ExclusiveOrExpression;

syntax ExclusiveOrExpression

    = AndExpression 

    | ExclusiveOrExpression "^" AndExpression;

syntax AndExpression

    = SelectExpression 

    | AndExpression "&" SelectExpression; 

The type rules on binary and collection operators are defined in Number §7.4.3, and Collection §7.6.2.

9.16 Expressions

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

syntax Expression

    = InclusiveOrExpression;