Skip to main content

Conditionals and Loops

In a LittleHorse WfSpec, the Edge structure tells the workflow scheduler what Node to advance to next. The Edge has a conditions field, which allows you to specify different control flow paths based on variables in your WfRun. This is analogous to if/else in programming.

Concepts

tip

This section covers the low-level details of how conditionals work in LittleHorse. If you just want to see some examples, skip ahead to the next section.

An EdgeCondition has three parts:

  1. A "LHS" (left-hand side),
  2. A comparator, and
  3. A "RHS" (right-hand side).

The comparator is a boolean operator that operates on the LHS and the RHS and returns either true or false. The EdgeCondition evaluates to `

Just as if/else allows you to implement control flow in your programs, Conditional Branching allows you to add control flow to your LittleHorse Workflows.

Let's look at how an if statement works in Python:

if foo < bar:
do_something()

Look at the booean expression foo < bar. It consists of a left-hand-side (foo), comparator (<), and right-hand-side (bar).

In LittleHorse, we have Edge Conditions, which also have an LHS, Comparator, and RHS. The LHS and RHS are any VariableAssignment, meaning they can be a value taken from some Variable or a hard-coded literal value.

Comparator Types

The supported Comparator's are:

  • LESS_THAN
  • GREATER_THAN
  • LESS_THAN_EQ
  • GREATER_THAN_EQ
  • EQUALS
  • NOT_EQUALS
  • IN
  • NOT_IN

You can find a detailed description of them in the protobuf documentation.

The WorkflowCondition

Our SDK's all have a WorkflowCondition struct/object which makes it really easy to work with EdgeConditions in a way that feels just like using if/else. In fact, you may not even need to know that the EdgeCondition exists.

In LittleHorse, you can create an expression using WorkflowThread#condition in any of our SDK's. The method or function takes three parameters:

  1. The LHS
  2. The Comparator
  3. The RHS

The LHS and the RHS can be set in two ways:

  1. A literal value.
  2. A WfRunVariable, which means that the value of that Variable in the WfRun is used.

The following is equivalent to foo < 3:


import io.littlehorse.sdk.common.proto.Comparator;
import io.littlehorse.sdk.common.proto.VariableType;
// ...

WfRunVariable foo = wf.addVariable("foo", VariableType.INT);

WorkflowCondition condition = wf.condition(
foo,
Comparator.LESS_THAN,
3
);

IN Conditional

For the IN conditional you have to provide either a variable or a literal value on the LHS that might be contained on a collection of values that is provided on the RHS.


import io.littlehorse.sdk.common.proto.Comparator;
// ...

WfRunVariable foo = wf.addVariable("foo", VariableType.INT);

WorkflowCondition condition = wf.condition(
foo,
Comparator.IN,
[2, 3, 4]
);

If Statements

To do an if statement, you use WorkflowThread::doIf(). The method takes two arguments:

  1. A WorkflowCondition (see above).
  2. An IfElseBody implementation.

The IfElseBody is just a type: think of it as a functional interface that's the same as a ThreadFunc but it's used differently. Generally, an IfElseBody is provided by an anonymous function; however, in Python it is required to pass a proper function (not a lambda).

Here's an example of executing a my-task Task if foo < 3:

WfRunVariable foo = wf.addVariable("foo", VariableType.INT);

wf.doIf(
wf.condition(foo, Comparator.LESS_THAN, 3),
ifBody -> {
ifBody.execute("my-task");
}
);

Nested Conditions

Here's an example of executing a my-task Task if foo < 3 and foo > 1:

WfRunVariable foo = wf.addVariable("foo", VariableType.INT);
wf.doIf(wf.condition(foo, Comparator.GREATER_THAN, 1),
ifHandler -> {
wf.doIf(wf.condition(foo, Comparator.LESS_THAN, 3), ifBody -> {
ifBody.execute("my-task");
});
});

If Else Statements

To do an if/else statement, you can use WorkflowThread::doIfElse(), which is identical to doIf() but it takes an additional IfElseBody that is executed if the condition is false.

An example:

WfRunVariable foo = wf.addVariable("foo", VariableType.INT);

wf.doIf(
wf.condition(foo, Comparator.LESS_THAN, 3),
ifBody -> {
ifBody.execute("my-task");
},
elseBody -> {
elseBody.execute("my-other-task");
elseBody.execute("yet-another-task");
}
);

While Loops

The WorkflowThread in LittleHorse also has a doWhile() function/method. To use it, you pass in a WorkflowCondition and a WhileBody, which is just a lambda function or interface defining workflow logic.

info

The semantics of WorkflowThread#doWhile() are the same as a while loop in programming, not a do while. That is because while is a reserved word in most languages, so we couldn't add a function called while.

Here's an example that executes two tasks in a loop as long as foo < 3:

WfRunVariable foo = wf.addVariable("foo", VariableType.INT);

wf.doWhile(
wf.condition(foo, Comparator.LESS_THAN, 3),
loopBody -> {
loopBody.execute("my-task");
loopBody.execute("another-task");
}
);