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 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'">
                >
            >
        >
    >