openEHR Expression Language (EL)
Goals
The openEHR Expression Language (EL) and Object Model (ELOM) is intended to provide a formal basis for computable expressions that appear within archetypes (ADL2 rules), Task Plans, Guidelines, and eventually, full Care Pathways.
Archetypes
In archetypes we would like to be able to write things like the following:
rules
check $map_bp_value = $diastolic_bp_value + 0.33 * ($systolic_bp_value - $diastolic_bp_value)
check $pulse_pressure_bp_value = $systolic_bp_value - $diastolic_bp_value
check $is_smoker = True implies defined ($smoking_details)
These kinds of expressions are relatively simple, and most people with any background in computing, logic, maths etc would find such expressions more or less natural.
To make this work, we need a way to bind the $vars to archetype paths. The EL provides a way to do this.
Task Planning
Task Plans have conditional nodes, which contain expressions controlling the branching. Similarly to Archetypes, the expressions are relatively simple. (see specification here)
Guidelines
Guidelines are larger artefacts that attempt to capture whole sets of decisions and potentially actions, for diagnostic and therapeutic purposes. Historically languages like Arden Syntax were designed for this purpose, and indeed, Arden was indirectly an inspiration for the EL Module structure proposed here. HL7 also created Gello, but this has little uptake. There is also the Guideline Interchange format (GLIF 3), which was unfortunately modified to use HL7v3 RIM structures, another standard no longer in use (and completely unsuited).
openEHR has a simple Guideline Definition Language (GDL), already in use, designed and implemented by Cambio in Sweden. This is actively being worked upon by them to create more powerful version, based on usage to date.
One possibility for EL and ELOM is to provide a basis for the next version of GDL, if possible, since the core need for expressions, bindings and so on are essentially the same. This would avoid having multiple separate overlapping specifications.
Care Pathways
Care Pathways of the kind published by NICE (UK) and many other institutions (e.g. Intermountain Healthcare CPMs) are something like structured sets of guidelines to achieve a whole clinical goal, such as 'treat sepsis', 'treat ischaemic stroke' etc. We aim ultimately to be able to express such informal artefacts in a formal way with EL/ELOM in the future.
Specification
Status: the EL specification is in development. The essential idea is to define 3 levels of language:
- EL expressions - single line formal expressions using Boolean, arithemetic and relational operators.
- EL statements - another level of language enabling assignments, if/then structures, when/then structures, procedure calls
- EL modules - a self-standing module with identifier, descriptive meta-data (like an archetype), terminology, symbol bindings, model importation etc, that can represent whole guidelines, or at least be a basis for more sophisticated guideline languages, such as a new version of GDL.
Do We Really Need Another Language?
As per the above section on guidelines, there are no currently available languages that are completely adapted to expressing all the needs described above. Arden Syntax possibly still come closest, but lacks various features such as formal binding. Could a mainstream programming language do the job? Some certainly get close to expressing the EL expression level semantics, but there are a number of issues.
- They lack some types like Terminology, efficient intervals, and temporal relationship comparisons.
- Decision support style logic is a concept completely lacking from standard languages - this is when the branches of a multi-branch if statement are understood as suggestions or recommendations, not fixed logic, and the user (or another agent) can choose a path whose logical condition is not met.
- Another concern with trying to make an existing programming language the language of EL is that we have no way to influence the language - its definition is completely out of our hands and will evolve, taking no account of our needs, which are not likely to be identical. Consider how much trouble the leap from Python 2x to 3 caused, and continues to cause.
- It is quite likely that even if we could find a programming language to use, we would want to implement only a subset. But where would the subset be defined? How would we prevent EL authors from using the non-implemented syntax.
These considerations lead to the conclusion that the minimum requirement is a grammar and parser for the language, even if it happened to be a pure subset of another language. Once we have this, we have some control over what syntax is accepted, and what semantics it generates.
Nevertheless, it is an attractive idea to see if some elements of the EL can be adopted/adapted from general purpose languages, which would allow direct authoring of parts of guidelines in such languages. Another possible approach is to construct a language as an extension of an existing language, and write a cross-compiler for it, targetting the original language.
We already have various examples of typical guidelines that are not directly expressible in any existing language, i.e. using first-order constructs. Of course, any guideline or other expression could certainly be implemented in a mainstream language, but this is a different activity.
One reason for this page is to discuss the relationship between EL and existing languages.
Re-use of Existing Software Libraries
Despite all the above, one of the main goals (already in the specification) is to allow existing software libraries etc, to be connected to an EL modules, enabling an easy way to a) re-use existing clinical rules etc where they exist and b) an easy way to implement typical standard computing functions (e.g. string processing etc) in a convenient language and simply use such functions from within EL.
Example
The following is a partial rendering of the Breast cancer treatment selection guideline shown in this BPMN diagram, in an EL Module form:
openEHR-ELOM.breast_cancer_treatment.v1
language
original_language = <[ISO_639-1::en]>
description
lifecycle_state = <"unmanaged">
original_author = <...>
use_model
org.openehr.rm
data_context
in $cancer_diagnosis: Terminology_code
in $has_metastasis, $er_positive, $pr_positive, $her2_positive: Boolean
in $ki67: Real
in $tnm_t, $tnm_n, $tnm_g: String
in $has_dx_transmural_mi, $hs_dx_hf_stage_2_4, $has_dx_severe_diabetes: Boolean
in $ejection_fraction: Real
out $recommendation: String
preconditions
$cancer_diagnosis = [ICD_10::C50|Breast cancer|]
definition
tumor_molecular_subtype (is_er_positive, is_pr_positive, is_her2_positive: Boolean; ki67_level: Real): Terminology_code
{
if is_er_positive and not is_her2_positive and ki67_level < 0.20 then
return [1111|luminal A|]
elseif is_er_positive and not is_her2_positive and ki67_level >= 0.20 then
return [2222|Luminal B (HER2 negative)|]
elseif is_er_positive and is_her2_positive then
return [3333|Luminal B (HER2 positive)|]
elseif not is_er_positive and not is_pr_positive and is_her2_positive then
return [4444|HER2|]
elseif not is_er_positive and not is_pr_positive and not is_her2_positive then
return [55555|Triple negative|]
}
has_contraindications_to_anthracyclines(): Boolean
{
return $has_dx_transmural_mi or $ejection_fraction < 0.4 or $hs_dx_hf_stage_2_4
}
chemo_1_recommendation (): Message
{
-- RECOMMENDATION
-- consider contraindications to anthracyclines
if has_contraindications_to_anthracyclines() then
return Recommend_cmf_message
else
-- RECOMMENDATION
if $has_critical_cardio_pathology or $age > 75 then
return Recommend_apirubicin_cycloPosphamide_message
else
return Recommend_ac_message
end
end
}
chemo_2_recommendation (): Message
{
-- RECOMMENDATION
if $has_dx_severe_diabetes or $allergic_to_taxanes or $has_taxane_intolerance then
if has_contraindications_to_anthracyclines() then
return Recommend_dc_message
else
-- CHOICE
return [Recommend_ac4p4_message, Recommend_ac4p12_message, Recommend_ac4d4_message]
end
else
-- RECOMMENDATION
return chemo_1_recommendation ()
end
}
-- REQUIRED
if $has_metastasis then
-- Luminal A
if tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [1111|luminal A|] then
-- Luminal B (HER2 negative)
elseif tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [2222|Luminal B (HER2 negative)|] then
-- Luminal B (HER2 positive)
elseif tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [3333|Luminal B (HER2 positive)|] then
-- HER2 type
elseif tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [4444|HER2|] then
-- Triple negative
elseif tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [55555|Triple negative|] then
end
else
-- REQUIRED
-- Luminal A
if tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [1111|luminal A|] then
-- REQUIRED
if tnm_major_number ($tnm_t) < 3 and tnm_major_number ($tnm_n) < 2 and tnm_major_number ($tnm_g) < 3 then
$recommendation := No_intervention_message
else
-- RECOMMENDATION
$recommendation := chemo_1_recommendation ()
end
-- Luminal B (HER2 negative)
elseif tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [2222|Luminal B (HER2 negative)|] then
-- REQUIRED
if $tnm_t = "1a" and tnm_major_number ($tnm_n) = 0 then
$recommendation := No_intervention_message
else
-- RECOMMENDATION
$recommendation := chemo_2_recommendation ()
end
-- Luminal B (HER2 positive)
elseif tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [3333|Luminal B (HER2 positive)|] then
-- REQUIRED
if $tnm_t = "1a" and tnm_major_number ($tnm_n) = 0 then
$recommendation := No_intervention_message
elseif $tnm_t matches {"T1b", "T1c"} and tnm_major_number ($tnm_n) = 0 then
elseif tnm_major_number ($tnm_t) matches {|2..4|} and tnm_major_number ($tnm_n) > 0 then
else
end
-- HER2 type
elseif tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [4444|HER2|] then
-- REQUIRED
if $tnm_t = "1a" and tnm_major_number ($tnm_n) = 0 then
$recommendation := No_intervention_message
elseif $tnm_t matches {"T1b", "T1c"} and tnm_major_number ($tnm_n) = 0 then
elseif tnm_major_number ($tnm_t) matches {|2..4|} and tnm_major_number ($tnm_n) > 0 then
else
end
-- Triple negative
elseif tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [55555|Triple negative|] then
-- REQUIRED
if $tnm_t = "1a" and tnm_major_number ($tnm_n) = 0 then
$recommendation := No_intervention_message
elseif tnm_major_number ($tnm_t) > 1 and tnm_major_number ($tnm_n) > 0 then
-- RECOMMENDATION
$recommendation := chemo_2_recommendation ()
end
end
user_alert ($recommendation)
end
terminology
symbol_definitions = <
["en"] = <
["er_positive"] = <
text = <"Oestrogen receptor positive">
description = <"Oestrogen receptor positive">
>
>
>
data_bindings
content_bindings = <
["openEHR-EHR-OBSERVATION.cancer_investigation.v1"] = <
["er_positive"] = <
target = <"/data/events[id3]/data/items[id5]/value/magnitude">
direction = <"in">
>
>
query_bindings = <
["https://oncology.health.org/cdr/"] = <
["has_dx_transmural_mi"] = <
query_id = <"dx_transmural_mi">,
parameters = <
["$type"] = <"'xxx'">
>
>
>
>
The core definition part could be done in various syntax styles. Here is a Ruby-ish version, contributed by Dr Marcus Baw.
The following is the definition part in a TypeScript-like syntax.
One of the key questions is: who is likely to create such artefacts, and would they rather use the style of syntax shown above, or a programming language syntax shown below. Developers would generally prefer the style shown below, but what about clinical guideline authors? HL7 appear to believe that a more natural language style is better, going by the http://wiki.hl7.org/index.php?title=Clinical_Quality_Language. Or will it be technical people authoring these guidelines? This seems unlikely, as the subject matter is incomprehensible to most software developers, but is bread and butter for clinicians.
tumor_molecular_subtype (Boolean is_er_positive, is_pr_positive, is_her2_positive; Real ki67_level): Terminology_code
{
if (is_er_positive && ! is_her2_positive && ki67_level < 0.20) {
return [1111|luminal A|];
else if (is_er_positive && ! is_her2_positive && ki67_level >= 0.20)
return [2222|Luminal B (HER2 negative)|];
else if (is_er_positive && is_her2_positive)
return [3333|Luminal B (HER2 positive)|];
else if (not is_er_positive && ! is_pr_positive && is_her2_positive)
return [4444|HER2|];
else if (not is_er_positive && ! is_pr_positive && ! is_her2_positive)
return [55555|Triple negative|];
}
if ($has_metastasis) {
if (tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) == [1111|luminal A|]) {
}
else if (tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) == [2222|Luminal B (HER2 negative)|]) {
}
else if (tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) == [3333|Luminal B (HER2 positive)|]) {
}
else if (tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) == [4444|HER2|]) {
}
else if (tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) == [55555|Triple negative|]) {
}
}
else {
if (tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) == [1111|luminal A|]) {
if tnm_major_number ($tnm_t) < 3 && tnm_major_number ($tnm_n) < 2 && tnm_major_number ($tnm_g) < 3)
$recommendation = No_intervention_message;
else
-- consider contraindications to anthracyclines
if ($has_dx_transmural_mi || $ejection_fraction < 0.4 || $hs_dx_hf_stage_2_4))
$recommendation = Recommend_cmf_message;
else
if ($has_critical_cardio_pathology || $age > 75))
$recommendation = Recommend_apirubicin_cycloPosphamide_message;
else
$recommendation = Recommend_ac_message;
}
}
}
}
else if (tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) == [2222|Luminal B (HER2 negative)|]) {
if ($tnm_t == "1a" && tnm_major_number ($tnm_n) == 0) {
else {
}
}
else if (tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) == [3333|Luminal B (HER2 positive)|]) {
if ($tnm_t == "1a" && tnm_major_number ($tnm_n) == 0) {
}
else if ($tnm_t ∈ {"T1b", "T1c"} && tnm_major_number ($tnm_n) == 0) {
}
else if (tnm_major_number ($tnm_t) ∈ {|2..4|} && tnm_major_number ($tnm_n) > 0) {
}
else {
}
}
-- HER2 type
else if tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) == [4444|HER2|]) {
if ($tnm_t == "1a" && tnm_major_number ($tnm_n) == 0) {
}
else if ($tnm_t ∈ {"T1b", "T1c"} && tnm_major_number ($tnm_n) == 0) {
}
else if (tnm_major_number ($tnm_t) ∈ {|2..4|} && tnm_major_number ($tnm_n) > 0) {
}
else {
}
}
else if (tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) == [55555|Triple negative|]) {
}
}
user_alert ($recommendation);