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;