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.

  1. They lack some types like Terminology, efficient intervals, and temporal relationship comparisons.
  2. 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.
  3. 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.
  4. 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);