openEHR logo

Expression Language (EL)

Issuer: openEHR Specification Program

Release: LANG Release-1.0.0

Status: DEVELOPMENT

Revision: [latest_issue]

Date: [latest_issue_date]

Keywords: openehr, expressions, rules

openEHR components
© 2016 - 2021 The openEHR Foundation

The openEHR Foundation is an independent, non-profit foundation, facilitating the sharing of health records by consumers and clinicians via open specifications, clinical models and open platform implementations.

Licence

image Creative Commons Attribution-NoDerivs 3.0 Unported. https://creativecommons.org/licenses/by-nd/3.0/

Support

Issues: Problem Reports
Web: specifications.openEHR.org

Amendment Record

Issue Details Raiser, Implementer Completed

LANG Release 1.0.0

2.0.0

SPECLANG-1. Add Expression Language specification.

T Beale

10 May 2020

Remove matches operator.

T Beale

10 Sep 2020

Add Container selectors.

T Beale

10 May 2020

Update to be based on BMM expression package.

T Beale

03 Mar 2020

Major rewrite. Added external model use;
added defined() and check() predicates;
refined quantifiers;
add BMM integration;
added various kinds of binding;
change 'EL Text' to 'EL Module' in most cases.

T Beale

19 Sep 2018

1.1.0

SPECPUB-6. Correct UML package nesting and paths in documents; insert base parent package; rename expressions package to expression.

T Beale

27 Nov 2017

1.0.1

Correct type of OP_DEF_EXISTS.evaluation_agent to FUNCTION<<Any>, Boolean>.

C Nanjo

31 May 2016

1.0.0

Initial writing. Taken from AOM2 2.0.6.

T Beale,
openEHR SEC

15 Feb 2016

Acknowledgements

Primary Author

  • Thomas Beale, Ars Semantica; openEHR Foundation Management Board.

Contributors

This specification benefited from formal and informal input from the openEHR and wider health informatics community. The openEHR Foundation would like to recognise the following people for their contributions.

  • Borut Fabjan, Program Manager, Better, Slovenia

  • Matija Kejžar, Senior Engineer, Better, Slovenia

  • Bostjan Lah, Architect, Better, Slovenia

Supporters

The work reported in this paper has been funded by the following organisations:

  • openEHR Foundation Industry and Organisation Partners

  • Ars Semantica, UK

  • Better, Slovenia (formerly Marand)

1. Preface

1.1. Purpose

This document specifies an abstract openEHR Expression Language (openEHR EL) that provides a syntax counterpart to the expression package in the openEHR Basic Meta-Model (BMM). This may be used within BMM models, to specify archetype rules, and in decision language expressions.

The intended audience includes:

  • Standards bodies producing health informatics standards;

  • Academic groups using openEHR;

  • Solution vendors.

Prerequisite documents for reading this document include:

Releated documents include:

1.3. Status

This specification is in the DEVELOPMENT state. The development version of this document can be found at https://specifications.openehr.org/releases/LANG/Release-1.0.0/expression_language.html.

Known omissions or questions are indicated in the text with a 'to be determined' paragraph, as follows:

TBD: (example To Be Determined paragraph)

1.4. Feedback

Feedback may be provided on the openEHR languages specifications forum.

Issues may be raised on the specifications Problem Report tracker.

To see changes made due to previously reported issues, see the LANG component Change Request tracker.

1.5. Conformance

Conformance of a data or software artifact to an openEHR specification is determined by a formal test of that artifact against the relevant openEHR Implementation Technology Specification(s) (ITSs), such as an IDL interface or an XML-schema. Since ITSs are formal derivations from underlying models, ITS conformance indicates model conformance.

2. Overview

The openEHR Expression Language (EL) defines a syntax and grammar for the expressions whose meta-model is defined in the expression package in the openEHR Basic Meta-Model (BMM). As such it may be considered the default syntax. Other syntaxes or syntaxes are certainly possible, and other expression serialisation are possible, such as object graph serialisation into XML, JSON, YAML etc. Consequently, the BMM expression package should be considered the normative definition of openEHR EL. Not all openEHR implementations using BMM expressions need support it: they might for example only serialse in JSON or use purely graphical visualisation.

Within openEHR, the uses of EL include expressing the following:

  • pre-, post-conditions and class invariants in BMM model definition files;

  • rules in archetypes;

  • rules in openEHR Guideline Definition Language (GDL);

  • expressions within decision logic models (DLMs) designed for use with openEHR Task Planning.

It may also be used in any other suitable context.

Key features of EL include:

  • variable declarations, assignments and expressions;

  • strong typing;

  • standard logical operators including universal and existential quantification, as well as user-defined operators;

  • standard arithmetic and relational comparison operators, enabling the use of numerics;

  • parentheses for overridding operator precedence;

  • functions and agents (lambdas).

2.1. Design Background

The openEHR Expression Language is based on a limited first-order predicate logic language. It has similarities with OMG’s OCL (Object Constraint Language), and to the assertion syntax used in the Object-Z language (Smith, 2000). See Sowa (2000), Hein (2002), Kilov & Ross (1994) for an explanation of predicate logic in information modelling. It also draws on the semantics and some of the syntax (particularly agent-related) of the Eiffel Language (ECMA-367). It is not exactly the same as any of these languages because:

  • it has a different target meta-model, namely the BMM expression model;

  • the syntax is designed to be comprehensible to developers familiar with modern mainstream object-oriented and functional languages such as Java, C#, Python, TypeScript etc.

2.2. Requirements

The semantic requirements including the usual arithmetic, boolean, and relational operators, functions, logical quantifiers, operator precedence, constant values, and variables. In addition, there is a need to support multi-lingual translations for symbolic variables, in a similar way to the openEHR Archetype Definition Language (ADL2).

2.3. Execution Model

The assumed execution model of the Expression language is that EL statements are evaluated by an evaluator against a data context, which determines the truth values of the expression(s). The data context is the origin for some or all of the variables mentioned in the expressions, which may be read from and written to. It may concretely be a retrieved data structure, or data via an API call to the EHR, demographics, laboratory system etc.

The data context may be specified in two ways. It may be inferrable from the artefact or computing context in which the EL statements appear, or it may be specified explicitly. In the former case, the EL instance is minimally a value-returning logical proposition such as systolic_pressure > 0, where the declaration of variables or properties such as systolic_pressure are inferred from e.g. a data binding, and any manifest values obey this EL specification. The implicit case is shown below.

EL design structure implicit
Figure 1. EL execution model - implicit context

In the explicit form, an EL expressions appear within a BMM model definition, or within a context that explicitly imports a BMM model.

EL design structure explicit
Figure 2. EL execution model - explicit context

In both cases, the result of parsing into computable form for evaluation must result in instance of the BMM EL meta-types.

2.4. Formal Representation

Formally, an openEHR EL expression is any of the following evaluable entities:

  • terminal entities:

    • instance references, including literals, constants, variables and function calls;

    • predicates: Boolean tests on instance structures;

    • agents: i.e. delayed routine calls;

  • complex expressions created from:

    • equality operator: equality operator;

    • primitive operators: arithmetic operators, relational operators, boolean operators;

    • collection operators: logical quantification operators;

    • decision tables: if/then/else like structures containing conditional expressions.

These correspond to the meta-types defined in the BMM expression package, each of which is described in detail in the following sections.

3. EL Basics

3.1. Syntax style

The syntax style used in EL is inspired by elements of common languages available today, including TypeScript, Kotlin, Java, etc, with divergences to provide a syntax that is more easily readable to non-IT professionals as well as IT professionals.

The lexical style used in EL is a form of so-called 'snake_case' rather than so-called 'CamelCase', in common with other openEHR specifications, but either may be used in real applications. One reason for using snake-case is be to render EL Modules more readable to the non-IT professional. Upper- and lower-case are not formally distinguished, and the use of upper case is a matter of style only, as follows:

  • class names: upper-case first letter followed by alphanumerics with underscores where spaces would occur in natural language, e.g. Iso8601_date_time, Arrayed_list<T>;

  • property, routine and variable names: lower-case first letter, followed by alphanumerics with underscores, e.g. employee_group, average_pressure();

  • constants and class (static) functions: upper-case first letter, followed by alphanumerics with underscores, e.g. Maximum_speed.

TBD: specify equivalence between snake-case and CamelCase, or a tool-level switch?

3.2. Commenting

Comments are of two styles. For end-of-line commenting, and for creating visual dividing lines, the leader pattern '--' is used. Dividing lines are simply a longer line, e.g '---------'.

Comment-only lines start with the bar character ('|'). The example below shows both forms.

    |
    | patient fit to undertake regime
    |
    patient_fit:
        Result := not
            (platelets.in_range ([very_low]) or  -- platelets can't be too low
             neutrophils.in_range ([very_low]))

TBD: allow comment-lines using '========'?

3.3. Typing

The Expression language is fully typed, with type definitions being supplied by one or more models, represented in the openEHR BMM specification. All operators are assumed to be implemented by and map to functions defined on types, including operators such as '+' mapping to the function add() defined on primitive types such as Integer. Accordingly, such operators are defined within the BMM as being operator aliases of their implementing function, which is made possible by the BMM meta-types BMM_ROUTINE and descendants.

An important implementation consequence of this approach is that an expression that is parsed to a classic operator-based AST may be evaluated by progressively searching for operator-aliased functions within the model definition, invoking with the arguments found in the AST structure, and returning Results for the next such computation. Of course, use of built-in native types and functions (rather than always dispatching via a BMM) to handle primitive type operators is likely to be used in the interests of efficiency. Function-matching can be implemented by matching the inferred signature of the operator and its argument(s) with functions having both a conformant (not necessarily identical) signature, and the same operator.

Such model definitions will therefore include primitive type definitions, either the openEHR Foundation Types, i.e. primitive types, container types and interval types, or ones that correspond very closely. In the interests of completeness, EL assumes the openEHR Foundation Types, so as to have a minimal basis.

3.4. EL Foundation Types

The EL syntax for these is described below.

3.4.1. Primitive Types

The EL primitive types are shown below.

Name Description

Boolean

Boolean value

Integer

Integer value

Integer64

Large integer value

Real

Real value

Double

Large real value

Date

ISO 8601-format date

Date_time

ISO 8601-format date/time

Time

ISO 8601-format time

Duration

ISO 8601-format duration

String

String

Uri

Uri in IETF RFC 3986 format

Terminology_code

Terminology code reference

Automatic type promotion from Integer to Real applies to all integer and real values and expressions, in the same fashion as most programming languages.

3.4.2. Container Types

The same container types as defined in the Foundation Types, structure package are assumed in EL, under the following names.

Name Description

Container<T>

Abstract parent of List, Set and Map types

List<T>

Linear list of items of any primitive type, allowing order and repeated membership

Set<T>

Set of items of any primitive type; no order, unique membership

Map<K:Ordered, V>

Indexed linear container

3.4.3. Interval Type

The same Interval type as defined in the Foundation Types, interval package is assumed in EL, under the following names.

Name Description

Interval<T:Ordered>

Interval of any ordered type

Point_interval<T:Ordered>

Sub-type used to efficiently represent closed intervals whose boundaries are the same

Proper_interval<T:Ordered>

Sub-type used to efficiently represent intervals whose boundaries are different

Automatic type promotion from Interval<Integer> to Interval<Real> applies to all integer and real values and expressions, in the same fashion as most programming languages.

3.4.4. Complex Types

Complex types are imported from a formal model definition, expressed in openEHR BMM format, or any formal equivalent. The types in a model definition included in this way become available within the formalism in the same way as the foundation types, and may be used in expressions in the same way.

4. Terminal Entities

Terminal entities in EL correspond to the EL_TERMINAL meta-type in BMM, and its descendants. These come in three categories:

  • instance references: references to instances generated by direct references to literals, constants, variables, or else function calls;

  • predicates: logical conditions on instance references;

  • agents: delayed routine call objects.

The following sub-sections describe these types.

4.1. Instance References

4.1.1. Literals

Literal values are mostly instances of the types declared in the imported models. Assumed literals correspond to the openEHR Foundation and Based types, and are expressed in the openEHR ODIN syntax, with the exception of List<T>, Set<T> and Map<K,V>, which are distinguished in EL with specific types of brackets. The corresponding classes are described in the openEHR Foundation Types specification.

Type Literal values Description

Boolean

True, False

Integer

10, -4, 1024

Signed integer values from −231 to 231-1

Real

10.0, 0.345

Signed real values from 3.4028235 × 1038

Double

10.0, 0.345

Double precision real values

Date

2004-08-12

ISO 8601-format date

Date_time

2004-08-12T12:00:59+0100

ISO 8601-format date/time

Time

12:00:59

ISO 8601-format time

Duration

P2Y8M

ISO 8601-format duration

String

"this is a string"

Uri

<https://en.wikipedia.org>

Uri in IETF RFC 3986 format

Terminology_code

[identifier]
[snomed_ct::389086002]
[snomed_ct::389086002|Hypoxia|]

Local terminology code
Terminology code in openEHR format

Array<T>

[val, val, …​]

List<T>

(val, val, …​)

Set<T>

{val, val, …​}

Map<K:Ordered, V>

{
    prim_val : val,
    prim_val : val,
    ...
    prim_val : val
}

A table of values of any type V,
keyed by primitive values of any Ordered descendant K,
typically String, Integer or Date/time types.
<val> are literals of any other type.

Object

{
    identifier : val,
    identifier : val,
    ...
    identifier : val
}

An object of any type T,
whose property names are identifier.

Interval<T>

|N..M|

the two-sided interval N ≥ x ≤ M

|>N..M|

the two-sided interval N > x ≤ M

|N..<M|

the two-sided interval N ≥ x < M

|<N|

the one-sided interval x < N

|>N|

the one-sided interval x > N

|<=N|

the one-sided interval x ≤ N

|>=N|

the one-sided interval x ≥ N

|N +/-M|

the two-sided interval of N ±M

|N±M|

the two-sided interval of N ±M

One exception to the above is tuples, which are direct instances of the BMM meta-type BMM_TUPLE. They take the literal form [a, b, c], where a, b, and c are generally of different types.

TBD: consider to not bother with Array and reserve [] for tuples.

4.1.2. Constants

Constants are syntactically represented using labels of which the first letter is capitalised, and may be of any type, including complex types. The following are EL expressions containing constants.

    Mph_to_kmh_factor = 1.6
    Safe_glucose_limits.has (3.5)

4.1.3. Variables

Symbolic variables are valid within the scope of the routine in which they are declared. They are represented by plain names such as is_smoker. An EL variable reference corresponds to a descendant of the BMM meta-type EL_INSTANTIABLE_REF<BMM_VARIABLE>, i.e. one of:

  • EL_INSTANTIABLE_REF<BMM_PARAMETER> (declared in the routine signature);

  • EL_INSTANTIABLE_REF<BMM_LOCAL> (declared as a routine local variable); or

  • EL_INSTANTIABLE_REF<BMM_RESULT> (the variable Result representing a function return value).

4.1.4. Property References

Property references are valid within the scope of the class in which they are declared, and may be used in any routine definition or assertion in the class. They are represented by plain names such as diabetic_status.

4.1.5. Function Calls

In EL expressions, computational functions may be called in the same way as for typical programming languages. An EL property reference corresponds to the BMM meta-type EL_FUNCTION_CALL, which contains an instance of the BMM meta-type EL_FUNCTION_AGENT, which in turn has as its closed_args a tuple containing a set of items each of which is in turn an expression of any kind.

Consequently, EL function calls (similarly to most programming languages) may be of any level of complexity. The simplest type of function call is to a function whose signature is <[],T>, i.e. one taking no arguments are returning a value of type T. In EL, this may be called with or without parentheses, e.g. age or age().

The following example assumes a function tnm_major_number (tnm_val: String): Integer that extracts various elements of Tumour/Node/Metastasis ('TNM') cancer staging values, such as 'Tis', 'G3' and so on, and shows two forms of call to this function.

    tnm_major_number (tnm_t)
    tnm_major_number ("Tis")

More complex function calls may include arguments of other function calls, agents, tuples, operator expressions and normal instance references.

To be evaluated, function calls must be mappable to class methods in external libraries that are available at expression evaluation time.

4.1.6. 'Built-in' Functions

Some commonly used functions such as current_date() or similar are often thought of as 'built-in' to a language environment. In the openEHR EL context, there are no built-in functions as such; useful utility functions must be supplied by classes or interfaces included as part of an imported model. In the openEHR environment, many utility calls are available in the openEHR Base Types. They will resolve correctly as long as this model is imported, which it normally will be as part of a larger model, such as the openEHR RM.

As a consequence, the total set of available utility functions for use in an EL expression is just what is available from the sum of all imported models. Assuming the openEHR Foundation and Base Types, the following kinds of functions are available for use in EL expressions:

    {Env}.current_date                                -- obtain today's date as a Iso8601_date

    blood_glucose_list: List<Real>
    {Statistical_evaluator}.max (blood_glucose_list)  -- compute a maximum of Numerics

    {Locale}.language                                 -- the primary language in the locale as a Coded_term

4.1.7. Container Item Access

Access to members of instances of a container type is achieved by the [] operator, which is an alias for various functions defined on the relevant types, as follows.

Operator Function Meaning

[i]

Array<T>.item(i: Integer): T

i-th element of an array; 1-based

[i]

List<T>.item(i: Integer): T

i-th element of a list; 1-based

[k]

Map<K,V>.item(k: K): V

element at key k of a Map

TBD: to achieve this generically, the above map of operators to member functions of appropriate types needs to be supplied in the model supplying the types themselves.

Container element access may be used on any expression whose effective type is a container, including function calls.

4.1.8. Container Item Selection and Matching

Selection of a container item matching a predicate may be achieved with an extension to the [] syntax, by supplying an agent argument whose signature is <[T], Boolean>, or in functional form, (v:T): Boolean. Here, 'selection' is understood to mean: obtain the first matching item, if it exists. This enables a reference of the following form to be constructed (final line).

    class Book {
        title: String
        pub_date: Date
    }

    book_list: List<Book>

    book_list [(b:Book) {b.title.contains("Quixote")}]

Since the signature is invariant with respect to the container type (here, List<Book>), a shorter form can be used in which the b is assumed:

    book_list [{title.contains("Quixote")}]

Similarly, any expression constructable based on the container type may be used. The following example contains an operator.

    book_list [{pub_date >= PY2003}]

For this to work, there must be an appropriate container function available. In the case of the openEHR Foundation types, this is select (<[T], Boolean>): T? defined on Container<T>; any equivalent function in a different model will do. The return type is nullable.

TBD: to achieve this generically, the map of operators to member functions of appropriate types needs to be supplied in the model supplying the types themselves.

TBD: different syntax for Container::matches()? Or could use matches plus [1] to get same effect e.g. book_list [{pub_date >= PY2003}][1] gives first book with matching pub_date.

4.1.9. Self Reference

As with many programming languages, a pre-defined reference to the current object is available via the plain name self. Unlike some languages, 'self' is not needed as a qualifier for properties or functions, and is generally only used as an argument in function calls.

4.2. Predicates

EL predicates are special meta-operators that are used to state tests on runtime object structures (in a similar way to Xpath).

4.2.1. Attached() Predicate

The attached() predicate is the EL equivalent of the expressions such as someVar == null (C, C++, C#, Java), some_var is None (Python) and similar. In EL, a reference is understood as being attached (or not) to a value. Attached status is therefore tested using attached (ref), and may be applied to any target of a BMM EL_INSTANCE_REF, which includes references to variables, properties, constants, functions and tuples.

Attached() returns a Boolean value, and thus may be negated, to form expressions such as:

    not attached (test_result) or else test_result.data.events[1].data.value > 6.5

4.3. Agents

Delayed routine calls for both functions and procedures may occur as terminals in an EL expression. The evaluation type (eval_type) of an agent is its signature. Syntactically, these take various forms. An agent can be created using a function or procedure visible in the current scope, using the keyword agent. The arguments list may range from empty to full. For a completely empty list, the routine name on its own may be used.

    |
    | define a naive obstetric risk function
    |
    obstetric_risk (age: Duration[1]; previous_pregnancies: Integer[1]): Coded_term[1]

    |
    | generate an agent with signature <[Duration, Integer], Coded_term>
    |
    agent obstetric_risk

For a partial argument list, ? symbols are used for the non-filled arguments. This generates an agent whose signature corresponds to the remaining open arguments. In the following example, an agent of the signature <[Integer], Coded_term> is generated, which, since the age of 38 years is supplied, may be thought of as a new function called obstetric_risk_38_years().

    agent obstetric_risk ('P38Y', ?)

Theoretically, an agent could be created with all arguments supplied, without the intention of immediate execution, e.g. agent obstetric_risk ('P38Y', 2), which would generate an agent of signature <[],Coded_term>. This could be later executed by simply using the receiver variable or parameter reference in the normal way, in a later expression.

Agents for procedure calls can be created in the same way as described above. In each case, the evaluation type is a signature of the form <[args]>, i.e. having no return type.

4.4. Qualified Referencing

Any terminal entity may appear as itself (in the relevant syntactic form described below) or in a form qualified by an instance reference, using standard 'dot' referencing. The qualifier provides the reference context, and is represented by the EL_SCOPED_REF property scope. Multiple qualifiers may be used in a single reference, as long as class feature visibility is satisfied, allowing such things as the following:

    person1.name
    employees[1].name.first_name
    blood_pressure.history.events[3].data.data.systolic

    agent obstetric_risks.basic_risk

Qualified referencing can be combined with selector agents to obtain an effect similar to the use of Xpath on XML data, as follows.

    book_list [{title.contains("Quixote")}].pub_date.year

5. Complex Expressions

Complex expressions in EL consist of non-atomic value-returning expressions, in a typed, operator-based syntax common to many programming languages and logics. In EL, the syntactic use of operators is understood as a shorthand for specific functions assumed to be available on types inferred from the context of the operator use. An EL implementation would therefore map such operators to the appropriate methods in a class library.

5.1. Equality Operator

The equality operator = in EL is understood as the function equal() defined on the openEHR Foundation type Any, of which every other class is a descendant. For all primitive value types (types for which use in expressions directly generates values rather than instance references), the semantics are value comparison, while for all other types, the semantics are reference comparison. For non-openEHR models, '=' will normally map to a similarly-named method, e.g. equals().

To obtain value comparison for non-value types, the function Any.is_equal(), which may be redefined in any sub-type, is used.

5.2. Primitive Operators

Primitive operators in EL are the infix or prefix syntax form of various functions available on primitive types. For example, the operator - (minus) is defined on the class Numeric (an inheritance ancestor of the classes Integer, Real etc) as the following:

    | in Numeric
    subtract (other: Numeric): Numeric
        alias infix '-'

    | redefined in Integer as
    subtract (other: Integer): Integer

This means that where the expression 100 - 5 is encountered in EL, what is really invoked is {Integer}.subtract(), specifically 100.subtract(5).

For convenience, the operators for the Numeric and Boolean types from the openEHR Foundation Types are reproduced below.

Operators Function Meaning

Arithmetic Operators - Numeric operands and result; descending precendence order

^

exponent()

Exponentiation

*

multiply()

Multiplication

/

divide()

Division

%

modulus()

Modulo (whole number) division

+

add()

Addition

-

subtract()

Subtraction

Relational Operators - Numeric, Date/time operands and Boolean result; equal precedence

=

equal()

Value equality

!=,

not_equal()

Inequality relation

<

less_than()

Less than relation

<=,

less_than_or_equal()

Less than or equal relation

>

greater_than()

Greater than relation

>=,

greater_than_or_equal()

Greater than or equal relation

Logical Operators - Boolean operands and result; descending precendence order

not, ~

not()

Negation, "not p"

and,

conjunction()

Logical conjunction, "p and q"

or,

disjunction()

Logical disjunction, "p or q"

xor,

exclusive_disjunction()

Exclusive or, "only one of p or q"

implies,

implication()

Material implication, "p implies q", or "if p then q"

In addition, some operators are defined on the other primitive types, including the following.

Operator Function Meaning

String Operators

+

String.append(other: String): String

String concatenation, appending

Iso8601_date arithmetic operators

+

add(d: Iso8601_duration): Iso8601_date

Add a precise duration to a date

++

add_nominal(d: Iso8601_duration): Iso8601_date

Add a nominal duration to a date

-

subtract(d: Iso8601_duration): Iso8601_date

Subtract a precise duration from a date

--

subtract_nominal(d: Iso8601_duration): Iso8601_date

Subtract a nominal duration from a date

-

diff(d: Date): Iso8601_duration

Difference of two dates

Iso8601_date_time arithmetic operators

+

add(d: Iso8601_duration): Iso8601_date_time

Add a precise duration to a date/time

++

add(d: Iso8601_duration): Iso8601_date_time

Add a nominal duration to a date/time

-

subtract(d: Iso8601_duration): Iso8601_date_time

Subtract a precise duration from a date/time

--

subtract_nominal(d: Iso8601_duration): Iso8601_date_time

Subtract a nominal duration from a date/time

-

diff(d: Date_time): Iso8601_duration

Difference of two date/times

Iso8601_time arithmetic operators

+

add(d: Iso8601_duration): Iso8601_time

Add a duration to a time

-

subtract(d: Iso8601_duration): Iso8601_time

Subtract a duration from a time

-

diff(d: Time): Iso8601_duration

Difference of two times

Iso8601_duration arithmetic operators

+

add(d: Iso8601_duration): Iso8601_duration

Add a duration to a duration

-

subtract(d: Iso8601_duration):Iso8601_duration

Subtract a duration from a duration

Operator semantics that require further explanation are described below.

5.2.1. Logical Negation

All Boolean operators take Boolean operands and generate a Boolean result. The not operator can be applied as a prefix operator to all operators returning a Boolean result as well as a parenthesised Boolean expression.

5.2.2. Precedence and Parentheses

The precedence of operators follows the order shown in the operator tables above. To change precedence, parentheses can be used in the fashion typical of most programming languages, as shown below.

    systolic_bp > 140 and (is_smoker or is_hypertensive)

5.3. Collection Operators

The two standard quantification operators from predicate logic there exists (∃ operator) and for all (∀ operator) are defined in EL for the container types found in the openEHR Foundation Types.

The textual syntax of there exists is as follows:

    there_exists v in container_var | <Boolean expression mentioning v>

Here, the | symbol is usually read in English as 'such that'. The symbolic equivalent may also be used:

    ∃ v : container_var | <Boolean expression mentioning v>

The above may also be expressed in EL as its functional equivalent:

    list_of_reals: List<Real>

    |
    | an expression that will return true if list_of_reals
    | contains a value greater than 140.0
    |
    list_of_reals.there_exists (
        agent (v: Real): Boolean {
            v > 140.0
        }
    )

The for_all operator has similar textual syntax:

    for_all v in container_var | <Boolean expression mentioning v>

Here, the | symbol is normally read in English as as 'it holds that'. The symbolic equivalent may also be used:

    ∀ v : container_var | <Boolean expression mentioning v>

The above may also be expressed in EL as its functional equivalent:

    list_of_reals: List<Real>

    |
    | an expression that will return true if list_of_reals
    | consists of values all greater than 140.0
    |
    list_of_reals.for_all (
        agent (v: Real): Boolean {
            v > 140.0
        }
    )

5.4. Decision Table Expressions

In EL, a decision table is a construct that expresses the equivalent logic of a multi-branch construct that returns a single expression as a result. There are two flavours, both familiar to programmers in mainstream languages: the condition chain (i.e. an if/then/else construct) and the case table (i.e. a case statement). The evaluation of both constructs determines which of a number of possible expressions to return as the result, based on the prior evaluation of branch conditions, whose particular form depends on which flavour of construct is used. Both constructs are thus purely functional, i.e. their branches cannot contain statements (i.e. assignments, procedure calls etc), only expressions.

5.4.1. Condition Chain (if/then)

The syntax for a condition chain (the if/then equivalent) takes a standard form and a compact form. The standard form is as follows.

    choice in
        <condition_1>:  <expression_1>,
        <condition_2>:  <expression_2>,
        ...
        <condition_N>:  <expression_N>,
        *:              <else expression>
    ;

In the above, the '*' character is understood as a wildcard, meaning 'all other cases'. A final row containing '*' is thus equivalent to a catch-all 'else' branch in the if/then/else chain of a procedural language.

A realistic example is illustrated below, making use of line comments to visually aid the author.

    molecular_subtype: Terminology_term
        Result := choice in
            =========================================================
            er_positive and
            her2_negative and
            not ki67.in_range ([high]):    [luminal_A],
            ---------------------------------------------------------
            er_positive and
            her2_negative and
            ki67.in_range ([high]):        [luminal_B_HER2_negative],
            ---------------------------------------------------------
            er_positive and
            her2_positive:                 [luminal_B_HER2_positive],
            ---------------------------------------------------------
            er_negative and
            pr_negative and
            her2_positive and
            ki67.in_range ([high]):        [HER2],
            ---------------------------------------------------------
            er_negative and
            pr_negative and
            her2_negative and
            ki67.in_range ([high]):        [triple_negative],
            ---------------------------------------------------------
            *:                             [none]
            =========================================================
        ;

For the common degenerate case where there is a single condition, the standard form looks as follows:

    calculate_score: Integer
        Result := choice in
            ============
            expr1:    2,
            ------------
            *:        0
            ============
        ;

While perfectly understandable (and legal syntax), the following compact form may be used instead:

    calculate_score: Integer
        Result := expr1 ? 2 : 0

The above syntax is adopted from the C language family. It may be used to construct intelligible conditional arithmetic operations such as summing, e.g.:

    ipi_raw_score: Integer
        Result := Result.add (
            =============================================
            age > 60                             ? 1 : 0,
            staging  {[stage_III], [stage_IV]} ? 1 : 0,
            ldh.in_range ([normal])              ? 1 : 0,
            ecog > 1                             ? 1 : 0,
            extranodal_sites > 1                 ? 1 : 0
            =============================================
        )
        ;

5.4.2. Case Table

The Case Table syntax form (case statement equivalent) is logically no different from the more general condition chain, except that every branch condition expression takes the form Expr ∈ Constri, where Expr is the same expression left-hand side for all branches, each having a variable right-hand side in the form of a value range constraint. Here the operator is read as 'is in', i.e. set-membership. The case table construct is designed to enable the value of a single determining expression to be tested against any number of value ranges. This is illustrated in the following example:

    gfr_range: Real

    risk_assessment: Real
        Result := case gfr_range in
            =================
            |>20|:      1,
            |10 - 20|:  0.75,
            |<10|:      0.5
            =================
        ;

This expression returns one of the values 1, 0.75 or 0.5, depending on the evaluated value of gfr_range, but it could equally return the value of a more complex expression, including further instances of Case tables, Condition chains, operator expressions etc.

5.4.3. Two-dimensional Tables (experimental)

Two-dimensional decision tables are common in all sectors. Although they can be reduced to a condition chain, EL provides a more direct syntax that enables them to be expressed in a form visually very close to their logical form.

item in
    ==========================================================================
                   {    isEconomy(p),   isBusiness(p),      isFirstClass(p) },
    --------------------------------------------------------------------------
    isChild(p):    {    50,             250,                1000            },
    --------------------------------------------------------------------------
    isAdult(p):    {    250 + trip.d,   450 + trip.d,       750 + trip.d    },
    --------------------------------------------------------------------------
    isMilitary(p): {    90,             250,                750 - 2 * p.age }
    ==========================================================================
;

References

Hein, J. L. (2002). Discrete Structures, Logic and Computability (Second.). Jones and Bartlett.

Kilov, H., & Ross, J. (1994). Information Modelling - an object-oriented approach. Prentice Hall.

Smith, G. (2000). The Object Z Specification Language. Kluwer Academic Publishers. Retrieved from http://www.itee.uq.edu.au/ smith/objectz.html

Sowa, J. F. (2000). Knowledge Representation: Logical, philosophical and Computational Foundations. California: Brooks/Cole.