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);