EL Modules
Overview
The Expression Language may be used to write a self-standing, uniquely identified EL Module that in addition to EL statements, contains data context definitions, terminology and other meta-data, in a structure similar to an openEHR archetype. An EL Module constitutes a sufficient input to a parser / generator to build a complete computational representation as an instance of the Expression Language Object Model (ELOM).
EL Modules may be related in two ways. One is via the import keyword. This is primarily used to re-use existing definitions, including symbolic constants, routine declarations and so on, thus enabling a new EL Module to contain just the specific functionality for which it is authored.
An EL Module may also be a specialisation of another, i.e. list it as a parent. This allows direct inheritance of definitions from the parent into the namespace of the module.
The core sections of a Module are expressed in the sections described above, i.e. reference, data_context, preconditions and definition. The data context declarations may be thought of parameter lists of input and output variables from the external world (e.g. measured from a patient) for the definition section, which contains the main logic.
The symbols used in statements are names in the fashion of any typical programming language, but are also considered codes to which linguistic definitions can be attached for use in the UI and other human-consumable renderings (e.g. documents). This is done in a terminology section of an EL Module, in the same way as for the openEHR archetype formalism. For example, the symbol $heart_rate of type Integer may have the linguistic definitions "heart rate" (English) and "frequência cardíaca" (Portuguese).
Mappings between external variables and external entities are described by bindings in a data_bindings section. For example, the expression $heart_rate > 50 is an assertion about values found in some data - i.e. $heart_rate is an external variable that may be mapped to an entity representing an actual heart rate measurement. Each data binding states where a variable’s value is obtained from (e.g. some larger data entity such as a document or message, or via an API call). This provides clear semantics for statements in the expression text that mention such variables.
In addition to these computable sections, various meta-data sections are added, including the description and language sections defined by the openEHR Resource specification, and an identification line at the top. As with archetypes, the sections other than use_model, reference, data_context, and definition are expressed in openEHR ODIN syntax.
The structure of a self-standing EL Module is thus as follows:
<EL Module identifier>
import
<EL Module identifier> as <alias>
<EL Module identifier>
...
language
original_language = <...>
translations = <...>
description
<descriptive meta-data>
use_model
<model ids>
reference
<constant definitions>
data_context
<declarations>
preconditions
<expressions>
definition
<statements>
terminology
<symbol_definitions>
data_bindings
<symbol bindings>
computing_bindings
<method bindings>
An example of an EL Module representing the common clinical health measure 'BMI' (body mass index) is shown below.
openEHR-ELOM.bmi.v1
language
original_language = <[ISO_639-1::en]>
translations = <
["pt"] = <...>
>
description
lifecycle_state = <"unmanaged">
original_author = <...>
details = <
["pt"] = <
language = <[ISO_639-1::pt]>
purpose = <"Cálculo do peso do corpo">
use = <"...">
>
>
use_model
org.openehr.rm
data_context
in $body_weight, $height: Real
out $bmi: Real
definition
$bmi := $body_weight / $height ^ 2
terminology
symbol_definitions = <
["en"] = <
["body_weight"] = <
text = <"body weight">
description = <"weight of subject body">
>
["height"] = <...>
["bmi"] = <
text = <"body mass index">
description = <"body mass index">
>
>
["pt"] = <
["body_weight"] = <
text = <"peso do corpo">
description = <"peso do corpo do sujeito">
>
["height"] = <...>
["bmi"] = <
text = <"índice de massa corporal">
description = <"índice de massa corporal">
>
>
>
data_bindings
content_bindings = <
["openEHR-EHR-OBSERVATION.body_measurements.v1"] = <
["body_weight"] = <
target = <"/data/events[id3]/data/items[id5]/value/magnitude">
direction = <"in">
>
["height"] = <
target = <"/data/events[id3]/data/items[id6]/value/magnitude">
direction = <"in">
>
>
["openEHR-EHR-OBSERVATION.body_mass_index.v1"] = <
["body_mass_index"] = <
target = <"/data/events[id3]/data/items[id5]/value/magnitude">
direction = <"out">
>
>
>
Module Identification
TBD: xxxx
Module Import
Other EL Modules can be used by an EL Module, using the import keyword, optionally using an alias to establish a namespace.
TBD: really needed to have explicit import? Since a Module parser could easily find instances of qualified refs and just look for the other modules in the system.
Modules as Domain Function Definitions
Ignoring the meta-data, terminology and bindings, an EL Module consists of the following parts.
openEHR-ELOM.bmi.v1
description
<documentation>
data_context
in $body_weight, $height: Real
out $bmi: Real
definition
$bmi := $body_weight / $height ^ 2
The above is semantically equivalent to a function in a typical programming language, such as the following:
/*
* <documentation>
*/
Real bmi (Real body_weight, Real height) {
return body_weight / height ^ 2;
}
Example: Breast Cancer Treatment
The following example is an EL Module representing a breast cancer treatment therapy selection guideline. It contains several external input variable, including $cancer_diagnosis, $has_metastasis etc which are assumed to be bound to either data context elements or query results.
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_term
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: Boolean
in $ejection_fraction: Real
in $hs_dx_hf_stage_2_4: Boolean
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_term
{
if is_er_positive and not is_her2_positive and ki67_level < 0.20 then
return [1111|luminal A|]
-- Luminal B (HER2 negative)
elseif is_er_positive and not is_her2_positive and ki67_level >= 0.20 then
return [2222|Luminal B (HER2 negative)|]
-- Luminal B (HER2 positive)
elseif is_er_positive and is_her2_positive then
return [3333|Luminal B (HER2 positive)|]
-- HER2
elseif not is_er_positive and not is_pr_positive and is_her2_positive then
return [4444|HER2|]
-- Triple negative
elseif not is_er_positive and not is_pr_positive and not is_her2_positive then
return [55555|Triple negative|]
}
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
-- Luminal A
if tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [1111|luminal A|] then
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
-- consider contraindications to anthracyclines
if $has_dx_transmural_mi or $ejection_fraction < 0.4 or $hs_dx_hf_stage_2_4 then
$recommendation := Recommend_cmf_message
else
if $has_critical_cardio_pathology or $age > 75 then
$recommendation := Recommend_apirubicin_cycloPosphamide_message
else
$recommendation := Recommend_ac_message
end
end
end
-- Luminal B (HER2 negative)
elseif tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [2222|Luminal B (HER2 negative)|] then
if $tnm_t = "1a" and tnm_major_number ($tnm_n) = 0 then
else
end
-- Luminal B (HER2 positive)
elseif tumor_molecular_subtype ($er_positive, $pr_positive, $her2_positive, $ki67) = [3333|Luminal B (HER2 positive)|] then
if $tnm_t = "1a" and tnm_major_number ($tnm_n) = 0 then
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
if $tnm_t = "1a" and tnm_major_number ($tnm_n) = 0 then
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
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'">
>
>
>
>