Perspectives Language Reference

This is the authoritive reference on the Perspectives Language. It is a work in progress. Currently, it is mostly about syntax. Semantical constraints will be added later. This is not the only source of information on PL:

Terminology

identifiers

All types are identified by qualified names. These are qualified names:

Name form Explanation
model:MyModel A domain name itself is qualified. These names are used in prefix declaration expressions: use: sys for model:System
model:MyModel$MyCase A fully qualified case name.
model:MyModel$MyCase$MyRole A fully qualifid role name.
model:MyModel$MyCase$MyRole$MyProperty A fully qualifid property name.
pre:MyCase A prefixed name is a qualified name as long as we have a use statement for the prefix: use: pre for model:MyModel

Quite often the parser/compiler can guess the qualified name from just the local part of that name. If not, an error is signalled. The use...for statement may be used to abbreviate longer namespaces, e.g. use mrl for model:MyModel$MyCase$MyRole lets us identify the property given in the table as mrl:MyProperty.

If the parser finds multiple qualified names with the same local part, either fully qualify the offending name or prefix it with enough segments to make it unique. For example, given the roles:

model:MyModel$ACase$MyRole
model:MyModel$AnOtherCase$MyRole

the single segment MyRole does not uniquely identify either. However, ACase$MyRole does and the system does not complain.

Finally, each context and role act as namespace containers. Hence, qualified names can become quite long!

Context declarations

There are five kinds of context. Each can be declared with a specific keyword. Each can house some, but not all, other context declarations.

Kind Explanation
domain A domain is the top level context of a model. A domain can contain roles and contexts, and use statements. The name of a domain must start with model:, as in model:TestDomain.
case A case revolves around a central concept (a role) and must contain the user roles that have a perspective on that concept
party A party is for the purpose of introducing user roles that can be referred from cases. Use party to structure an organisation. A party can contain other parties.
activity An activity is about doing things in a context with clear responsibilities. An activity may contain states.
state A state is a case or activity with specific values for roles or attributes. A state may introduce new roles

Context declarations can only be nested inside other Context definitions, with limitations:

domain case party activity state
domain x x x
case x x x
party x
activity x x
state x

Role declarations

There are five kinds of roles. Each can be declared with a specific keyword inside a context definition.

Kind Explanation
user A user role represents living persons that perform actions. Actions are collected in a perspective. A user role can only be filled by another user role.
thing A thing role has no perspective. It represents everything that is not an agent. A thing role can only be filled by another thing role.
context A context role is bound to the External Role of a context. It represents a context (instance) within another context (instance). A context role can only be filled by another context role, or by an external role.
bot A bot role is for a particular user role and has a perspective. Its actions are carried out automatically when their conditions are met. A bot role cannot be filled.
external An external role is a role that represents a context. An external role cannot be filled.

Use these keywords to create Roles. Restrictions apply on filling roles:

  1. user and thing roles can only be filled with the same kind;
  2. a context role can be filled by a context role or an external role (but in the expressions it looks as if one fills it with a Context type)
  3. external roles and bot roles cannot be filled.

Role declarations:

Expression Explanation
user: Driver Declare a User role with default attributes: not mandatory, functional.
user: Driver (mandatory, not functional) Declare a User role with explicit attributes.
user: Driver filledBy: Person User role with default attributes, filled with the Person role.
user: SystemUser filledBy: None SystemUser is the root user and is never filled with another role
user: Parent filledBy: Father, Mother User role filled by either of two roles.
user: Driver (mandatory, functional) filledBy: Person User role with attributes, filled with the Person role.
user: Driver filledBy: Person + LicenseHolder User role filled by two roles simultaneously. One role provides personal details, the other the drivers license.
thing: UnfinishedRides = filter Rides with not Finished Calculate a role with an expression.
thing: ListOfDocuments = callExternal "docNamesFromCouchdb" returns Document A call to a (fictitious) API function that results in instances of the type Document. We call a role that is created in this way a computed role.

Property

Property declarations:

Declaration Explanation
property: MyProperty A property with default attributes and a String range: (not mandatory, functional, String).
property: MyProperty (mandatory, not functional, Boolean) A property with defined attributes and range
property: PlannedDuration = Destination >> Planned – Origin >> Planned Calculate a property with an expression.

Aspect

Adding an aspect uses the same syntax for contexts and roles:

case: MyContext
aspect SomeOtherContext

and

case: MyContext
thing: MyRole
aspect SomeOtherRole

Perspective

Perspective declarations:

Expression Explanation
perspective on: SomeRole A perspective with all Verbs (Create, Consult, Change, Delete), for all properties of SomeRole
perspective on: SomeRole (AView) A perspective with all Verbs, but just for the properties in the view AView
perspective on: SomeRole: Change A perspective with just the Verb Change, but for all properties
perspective on: SomeRole (AView): Change, Delete A perspective just the properties in AView and just the Verbs Change and Delete.

Perspective expressions over multiple lines:

perspective on: SomeRole (AView): Consult, Create
Change with AnotherView

Here the Action with the Change Verb uses the view AnotherView on SomeRole, while the Actions with the Verbs Consult and Create use the default view AView.

perspective on: SomeRole (AView)
Change with AnotherView
if <expression>

The Action with the Verb Change is conditional on the (Boolean) value of the expression.

Perspective for a bot

A bot can have a perspective, too. It's Actions are all about creating, changing or deleting contexts, roles and property values.

bot: for SomeUser
perspective on: SomeRole
if <condition> then
<assignment>

The condition must be an expression with a Boolean Value (see Expressions, below).

<assignment> takes on one of three forms:

This is a let* expression:

let*
varName1 <- <expression>
varName2 <- <expression>
in
<assignment>

Assignment statements are explained below.

The current object

When a rule fires, it is applied in an environment in which the variable object is bound, automatically, to the current object set. This is the set of objects of the Perspective of the bot. This variable can be used in every assignment and in the expressions for the let* bindings.

Assignment statements

This is how to change the instances of a role in a context. Below, <role-expression> is a query evaluate with respect to the current context (the context of the bot executing the assignment). The same holds for <context-expression> and <value-expression>.

NOTE: the text Assignment has a more in-depth treatment of the subject.

Assignment Explanation
remove <role-expression> Select the role instances to remove, in any context
createRole RoleType[in <contextExpression>] Create a new instance of the role and attach it to the current context, or to the context selected with the optional <contextExpression>.
move <roleExpression> [to <contextExpression>] Move the selected role instances to the current context, or the selected context. Notice that both contexts have to have the same type!
delete <role-expression Delete all selected instances, from any context.
bind <binding> to RoleType [in <contextExpression>] To fill a role with another role, use bind (when A fills B, we say that B is bound to A. A is the binding; B is the binder). RoleType resolves to the current context or the selected context. New instances of RoleType are created as binders.
Bind_ <binding> to <binder> Instead of creating new instances, this syntax allows you to select an (empty) binder and fill it with the binding. Notice that both queries must select a single instance!
unbind <binding> [from RoleType] Select bindings - that is, the roles that are bound - and release them from their binders. When RoleType is given, just release them from binders of that Type.
unbind_ <binding> from <binder> Select a single binding and and release it from the selected single binder.

This is how to change the values of a property for some role. The assignment statements below change the roles in the current object set, unless one or more role is selected with <role-expression>. The current object set is determined by the object of the perspective.

Assignment Explanation
SomePropertyType =+ <value-expression [for <role-expression] Add a value to the list of values for SomePropertyType. It is an error if the type of <value-expression is not equal to the range of SomePropertyType. Changes the current object set, or another role if <role-expression> is given.
SomePropertyType =- <value-expression [for <role-expression] Remove the value from the list of values for SomePropertyType. It is an error if the type of <value-expression is not equal to the range of SomePropertyType.
SomePropertyType = <value-expression [for <role-expression] Completely replace all values of SomePropertyType with the values returned by <value-expression. It is an error if the type of <value-expression is not equal to the range of SomePropertyType.
delete property SomePropertyType [for <role-expression] Completely remove all values of SomePropertyType.

Creating Context and Role instances

Creating a context is different from creating a role. It is not possible to create a role instance without immediately inserting it into a context. However, it is entirely possible to create a context without binding it into some other context. This is not useful, however. So why not immediately bind a new context instance? Because it is not obvious where, as a context instance may be bound in multiple contexts. A role instance, however, is always bound in just a single context. See the assignment section above on how to create a role instance.

Create expression Explanation
createContext SomeContext Create a new instance of SomeContext and a new instance of its External role. The value of the expression is the External Role.

Combine a context creation expression with a role binding expression like this:

bind createContext SomeContextType to SomeContextRoleType [in <contextExpression]

External functions

Perspectives has a mechanism to extend the language with external functions. These functions should be thought of as external with respect to the core language. Such functions are defined in namespaces and added through modules that are compiled with the PDR. One can call them through the use of two keywords:

For example, this ContextRole declaration:

context: Modellen = callExternal cdb:Models() returns: Model$External

adds a Role that is computed by the function Models in the module identified by the (standard) prefix cdb: model:Couchdb. As another example, consider this statement in the then-part of a rule:

callEffect cdb:AddModelToLocalStore( object >> binding >> Url )

This external function loads a model file from an Url and adds it to the local store of model files in Couchdb.

The general form for calling these functions and statements is qualifiedName( <expression>* ), where each argument must be provided by ordinary Perspectives expressions (as detailed below).

View

A view is merely a list of properties.

view SomeProperty, AnotherProperty

It is an error if SomeProperty and AnotherProperty are not the identifiers of Properties.

Expressions

Expressions have a highly recursive structure. They are built from three primary forms: (assignments are not expressions!):

Here is the full expression syntax:

expression = simpleExpr | unaryExpr | binaryExpr | let*
simpleExpr = identifier | value | variable | >>= sequenceFunction | ‘this’ | ‘binding’ | ‘binder’ | ‘context’ | ‘extern’ | 'object'
value = number | Boolean | string | date
variable = lowerCase+
sequenceFunction = ‘sum’ | ‘product’ | ‘count’ | ‘minimum’ | ‘maximum’
unaryExpr = ‘not’ expr | ‘createRole’ identifier | ‘createContext’ identifier | ‘exists’ expression | 'available' expression
binaryExpr = ‘filter’ expression ‘with’ expression | expression operator expression | ‘bind’ expression ‘in’ expression
operator = '>>' | '==' | '<' | '>' | '<=' | '>=' | 'and' | 'or' | '+' | '-' | '*' | '/'
let* = ‘let*’ binding+ ‘in’ body
binding = variable ‘<-‘ expression
body = expr | assignment+

Operator Precedence

Operator precedence is ruled by weights for each operator. Parentheses can be used to override these precedences.

Operator Precedence
>> 8
== 0
/= 0
> 1
< 1
<= 1
>= 1
and 1
or 2
+ 3
- 2
* 5
/ 4
>>= 8
filter 9

Note that the unary operators not, exists and available have lower precedence than any of these binary operators. For example, not is always applied to the entire expression behind it. So if that expression is a conjunction, it is applied to the entire conjunction - not to its left term!

not Prop1 and Prop2

is the same as

not (Prop1 and Prop2)

Some examples:

SomeContextRole >> binding >> context >> AnotherRole
SomeRole >>= sum
SomeRole >>= sum / SomeRole >> count
not (ABooleanProperty and AnotherBooleanProperty)
filter SomeRole with not exists APropertyOnThatRole
filter SomeRole >> binding with BooleanPropertyOnTheBinding
ThisRole >> Date == ThatRole >> Date
SomeNumericProperty * 2
let*
a <- SomeRole >> binding
b <- AnotherRole >> binding
in
unbind a from SomeRole
unbind b from AnotherRole
bind a in AnotherRole
bind b in SomeRole

Simple value Expressions

Strings, Numbers, Booleans and DateTime values are written as follows:

Value expression Explanation
"a string" A string should be enclosed in quotes. Any character can be inside a string, that is not a double quote, a backslash or an ampersand. Escape character sequences are allowed, too, but they are not documented here. We refer to the source code of Text.Parsing.Parser.Token
true, false These are the entire population of the Boolean type.
123 Number representation has yet to be decided on. Right now only integers can be used.
'2019-10-04', '2011-10-10T14:48:00' Date and DateTime expressions are parsed by Javascript Date.parse(), enclosed in single quotes. See Date Time String Format for more details.

Copyright 2019 Joop Ringelberg and Cor Baars