No-code Logic for patient triage

The screen here contains something which is at one and the same time both a definition you can read and software you can use in computing things.

This is what Logiak Processes are all about.

Declarative / Imperative. It’s a mood.

When people talk about “coding”, they are almost always talking about defining procedures to instruct the computer what to do, step by gruesomely detailed step - commands -imperatives.

However, machine-oriented procedures are not what we humans are good at and is one reason why software is so difficult.

Procedures easily get convoluted, and software maintenance becomes hell.

But in the question of programming languages, there has always been a tantalising alternative possibility: instead of imperatives, couldn’t we do things declaratively? Not by commands, but by statements, definitions.

This alternative, declarative programming, is more attractive because it isn’t really programming at all, it is just defining things. It’s mathematical and logical reasoning. It’s stating what is true. It’s a human thing, its what we do anyway, and we are good at it. It’s how we think, rather than how machines think. It’s directly intelligible and therefore perfectly maintainable.

Not all of our knowledge can be well expressed declaratively. Some of our knowledge is about procedures, albeit at the human level, such as how to bake a cake, and we want to be able to represent such procedures too.

The ideal: to have both, but they be clearly separated

If we were able to build systems where we entirely and completely separate the declarative from the procedural, we would have the best of both worlds. The primary step forward however would to be free to build in our declarative knowledge without having to twist it into procedures.

We could maximize the use of what we are best at - declarative knowledge - reducing development time, increasing maintainability, increasing correctness.

This is what logiak allows you to do.

Programming via definitions - an example

Let us show this via a reasonably simple example (though no example is simple enough).

D-tree International supported a research project in Malawi, implementing a mobile app embodying a triage algorithm.

The app was used by community health workers to identify severe illness among sick children in busy clinic waiting rooms, so they could be brought forward to the front of the queue, to reduce their risk of dying during the long wait.

The algorithm used is called ETAT -Emergency, Triage, Assessment and Treatment, published by the WHO.

Researchers published a 2020 article about the study in Malawi:

In this article, they draw out the algorithm in a flow chart. The algorithm is small and therefore useful for our illustration of the separation of the declarative from the procedural:

The aim of the algorithm is to classify each child as either E (Emergency), P (Priority) or Q (queue).

Implementing the ETAT algorithm as an interactive app

The algorithm as flow chart looks like a process, which indeed it is. It has arrows showing a flow of control.

But is also embodies knowledge, declarative knowledge, about what constitutes an emergency.

Here is what we mean by “it embodies declarative knowledge”:

The algorithm clearly says, for example, that if the child is in a coma, then the child is seriously ill. That is a true statement - declarative knowledge.

If we were to try to implement this algorithm in an App using traditional procedural programming, we would have no problem with the step-by-step aspects indicated by the arrows, but we would have to implement all the declarative knowledge via some necessarily ugly (because unnatural) procedural means.

Now let us see how logiak beautifully isolates the purely declarative from the procedural and thereby allows us to “encode” the declarative knowledge entirely naturally.

1. Define the Process

The first thing we need to do is to define a Process to create a step-by-step sequence of user interactions as described by the algorithm chart above.

Here is what it looks like to do this (abbreviated of course):

2. Define the Logic

In a separate tab we define the logic of this application.

Here we will be totally free of any procedural concerns whatsoever and will be dealing only with purely declarative knowledge.

Anyone can do this.

2.1 User responses are statements

Firstly, note that a question posed to the user, combined with a user’s response together describes a state of affairs: i.e. the response to a question is a specific statement.

Consider the yes/no question:

Does the child have cold hands?

When the user responds, one of the following two statements becomes true:

The child has cold hands

The child does NOT have cold hands

Similarly with the question about capillary refill & weak and fast pulse.

Because we are interested in whether the user has ticked both options, one of these becomes true:

The child has capillary refill longer than 3 secs and weak and fast pulse

It is NOT the case that the child has capillary refill longer than 3 secs and weak and fast pulse

question + response = statement

2.2 Selection Conditions

Such statements from the user (responses to questions) form the basis for our logical computations.

In logiak, we define conditions (we might also call them definitions), which are made true or false by user responses. We call them Select conditions. Some of the conditions are essentially statements like the above - for example, we define the condition under which “The child has cold hands” is true by just giving a name to the response pattern where the user has clicked yes.

Select conditions are effectively names for user response patterns

The names we give to the conditions could be symbolic names such as “X”, but it is nicer and more convenient to give them descriptive names which communicate the state of affairs they represent.

For example, we define the condition which is made true by the user responding affirmatively to the question “Does the child have cold hands?” and call that condition Child has cold hands.

Here is what this looks like:

How easy is that?

2.3 Meta-conditions

We can build on such Select conditions by defining meta conditions.

Meta conditions combine existing conditions together.

For example, we can define this:

child has cold hands and capillary refill/pulse issues is true IF

  child has cold hands

  child has capillary refill longer than 3 seconds and weak and fast pulse

This gives a name to a more complex circumstance - this time, one in which the user has provided affirmative responses to two questions (the cold hands question, and the refill/pulse question).

This is what that looks like:

And how easy is that?

We can use any meta condition in the definition of other meta conditions, without limit.

We hope you can see that we can remarkably easily build up conditions which represent very complex combinations of user responses to questions, each possessing simple descriptive names such as child has an emergency condition.

Moreover, though this particular ETAT application does not require it, logiak allows us also to define conditions based on values (value conditions): these can be values of variables defined in the process, or values from the database.

So the evaluation of a high-level meta condition can potentially involve a large amount of computation about the state of our knowledge, both user responses and information we have already recorded in the database.

The highest level meta conditions will generally represent circumstances which are the most important for us to be able to determine (= compute).

In the ETAT application, because of its aim, ultimately we want to define are conditions like this:

child has an emergency condition (E) IF ….

child has a priority condition (P) IF …

child should be given a Q IF …..

It important to note that logiak conditions are highly modular: Each condition can be considered on its own terms, independently of other conditions. What is important is that the descriptive name we have given to the condition accurately represents the logic of its definition. Hence, it really super maintainable.

It’s why non-programming medics can define logiak Processes with literally hundreds of conditions as shown here below, since each one can be validated on its own terms.

A Process from Delphicare HIV care and treatment system

As an aside: we can note that perhaps the only unusual definition in this example is caused by the question about symptoms of dehydration, which requires at least two symptoms to be present. This is what the definition of that looks like in logiak, using a THRESHOLD operator.

Defining a select condition as true if a threshold of options are selected

3. Conditions are used as preconditions in the Process

Having defined the logical relationships we are interested in, in complete isolation from any procedural concerns, almost as if we are not doing software development at all, we can now return to the Process and use the knowledge we’ve defined.

3.1 Preconditions

If you recall, the Process when we left it was a sequence of questions, one after the other.

We can attach conditions as preconditions to Process steps.

A step which has a precondition will be used only if its precondition evaluates to true.

In the case of ETAT, if we have defined a condition “child has emergency sign” (i.e child should get E), we might just to introduce an interactive step after all the questions which has this condition as its precondition and which just tells the user “classify this child as an E”.

We’ve done the logic, and have been able to validate it without any ugly procedural structures interfering with its clarity, so we can know for sure the Process will yield the correct answer.

3.2 Pragmatics

The final thing to say is that there will almost always be pragmatic considerations we have to take into account. Logical correctness is usually not the only goal.

In the case of ETAT, first presenting the user with all of the questions and then letting logiak compute the classification with our definitions would be quite wrong.

Time is of the essence, and if a question is answered from which we can conclude the child is an emergency, then no more questions should be asked.

For example, if the user indicates the child has obstructed breathing, in response to the first question, then we don’t need to ask any more questions.

We know the child has a danger sign, and therefore should be treated as an E.

So typically, not only do we use preconditions to determine what to tell the user (and what to record in the database), but we also use preconditions to avoid asking the user unnecessary questions.

This can be done in several ways, one of which is to structure the questions in nested sections and attach preconditions to the sections so that control moves to the end of the Process as soon as it can.

Here is a final video showing the first danger sign question, followed by a section containing other questions, followed by a message to the user. We attach preconditions - to the message and to the section, then use the step-by-step debugger to demonstrate that the preconditions really do affect the flow of control: our definitions really are active in the interactive App. We really have programmed declaratively.

Appendix: the ETAT algorithm in action in Malawi