Conditionals
Programming languages conditional branching (like if/else
statements) to allow your programs to respond to different inputs. In a LittleHorse WfSpec
, conditional branching serves an identical purpose.
Basic Concepts
LittleHorse Conditionals work almost exactly like if
does in most programming languages. Depending on whether an expression evaluates to true
or false
, a block of logic (code or WfSpec
) is either executed or not.
Workflow Conditions
The WorkflowCondition
in the LittleHorse DSL is analogous to the "thing" that goes between the parentheses in your if ()
statements (or just after the if
in Python and GoLang). LittleHorse WorkflowCondition
s evaluate at runtime to true
or false
.
Under the hood (at the protobuf level), a WorkflowCondition
consists of three parts:
- The Left-Hand-Side (LHS),
- The
Comparator
, - and the Right-Hand-Side (RHS).
The LHS and the RHS can be anything: a Variable
in a WfRun
, a constant, a handle to the output of a previous node (eg. a TaskRun
output or an ExternalEvent
payload), or a complex Expression
.
The Comparator compares the LHS and the RHS. LittleHorse supports the following comparisons:
LESS_THAN
, which returns true if the LHS is<
the RHS.GREATER_THAN
, which returns true if the LHS is>
the RHS.LESS_THAN_EQ
, which returns true if the LHS is<=
the RHS.GREATER_THAN_EQ
, which returns true if the LHS is>=
the RHS.EQUALS
, which returns true if the LHS is==
the RHS.NOT_EQUALS
, which returns true if the LHS is!=
the RHS.IN
, which returns true if the LHS is==
one of the elements in the RHS (if the RHS is aJSON_ARR
variable) or if the LHS is==
one of the keys in the RHS (if the RHS is aJSON_OBJ
variable).NOT_IN
, which is the opposite ofIN
.
You can find a detailed description of them in the protobuf documentation.
DoIf
, DoIfElse
, and DoWhile
In all of our SDK's, the WorkflowThread
has a few methods that let you work with conditionals. Each method takes in a WorkflowCondition
and one or more ThreadFunc
s, which are lambda functions or function pointers that define workflow logic when given a WorkflowThread
:
WorkflowThread#doIf()
, which takes in aWorkflowCondition
and a workflow logic body (a lambda function). The resultingWfSpec
executes the logic in the lambda if theWorkflowCondition
evaluates totrue
at runtime.WorkflowThread#doIfElse()
, which takes in aWorkflowCondition
and two logic bodies. The resultingWfSpec
executes the first body if the condition evaluates totrue
and the second one if the condition evaluates tofalse
.WorkflowThread#doWhile()
, which takes in aWorkflowCondition
and a workflow logic body. It behaves exactly likewhile (someCondition) {/* */}
in programming languaes: the logic body is executed repeatedly so long as the condition keeps evaluating totrue
.
In Practice
Let's see conditionals used in a practical example.
Creating the WfSpec
We'll write a WfSpec
(similar to the example in the Variables Concepts page), which:
- Takes in a
user-id
and amessage
to send as input. - Fetches the user's preferred contact method.
- If the user prefers contact via hologram, it sends them a hologram; otherwise, it sends them the message through their Comlink.
Background: The Tasks
This example will need three TaskDef
s. There isn't any new magic here, so for the sake of brevity here is the code:
package io.littlehorse.quickstart;
import java.util.List;
import io.littlehorse.sdk.common.config.LHConfig;
import io.littlehorse.sdk.worker.LHTaskMethod;
import io.littlehorse.sdk.worker.LHTaskWorker;
class MyTasks {
// This Task Method returns a POJO, which is automatically serialized to a JSON_OBJ variable
// value in LittleHorse.
@LHTaskMethod("fetch-contact-method")
public String fetchUser(String userId) {
if (List.of("obiwan", "padme", "satine").contains(userId)) {
return "COMLINK";
} else {
return "HOLOGRAM";
}
}
@LHTaskMethod("send-comlink-message")
public String sendComlink(String userId, String message) {
String result = "sent comlink " + message + " to user " + userId;
System.out.println(result);
return result;
}
@LHTaskMethod("send-hologram")
public String sendHologram(String userId, String message) {
String result = "sent hologram " + message + " to user " + userId;
System.out.println(result);
return result;
}
}
public class Main {
public static void main(String[] args) throws Exception {
LHConfig config = new LHConfig();
MyTasks taskFuncs = new MyTasks();
LHTaskWorker contactMethodService = new LHTaskWorker(taskFuncs, "fetch-contact-method", config);
LHTaskWorker comlinkService = new LHTaskWorker(taskFuncs, "send-comlink-message", config);
LHTaskWorker hologramService = new LHTaskWorker(taskFuncs, "send-hologram", config);
hologramService.registerTaskDef();
contactMethodService.registerTaskDef();
comlinkService.registerTaskDef();
Runtime.getRuntime().addShutdownHook(new Thread(hologramService::close));
Runtime.getRuntime().addShutdownHook(new Thread(contactMethodService::close));
Runtime.getRuntime().addShutdownHook(new Thread(comlinkService::close));
hologramService.start();
contactMethodService.start();
comlinkService.start();
}
}
The WfSpec
This is where the magic happens. We will use the doIfElse()
method.
package io.littlehorse.quickstart;
import io.littlehorse.sdk.common.config.LHConfig;
import io.littlehorse.sdk.wfsdk.WfRunVariable;
import io.littlehorse.sdk.wfsdk.Workflow;
import io.littlehorse.sdk.wfsdk.WorkflowThread;
public class Main {
public static final String WFSPEC_NAME = "send-message";
public static void wfLogic(WorkflowThread wf) {
WfRunVariable userId = wf.declareStr("user-id").required();
WfRunVariable message = wf.declareStr("message").required();
WfRunVariable preferredContact = wf.declareStr("contact-method");
// Fetch + save the preferred contact method
preferredContact.assign(wf.execute("fetch-contact-method", userId));
wf.doIfElse(preferredContact.isEqualTo("COMLINK"), ifHandler -> {
// It is CRUCIAL here to use `ifHandler`, not `wf`.
ifHandler.execute("send-comlink-message", userId, message);
// Feel free to add more logic in here.
}, elseHandler -> {
elseHandler.execute("send-hologram", userId, message);
});
}
public static void main(String[] args) throws Exception {
LHConfig config = new LHConfig();
Workflow wfGenerator = Workflow.newWorkflow(WFSPEC_NAME, Main::wfLogic);
wfGenerator.registerWfSpec(config.getBlockingStub());
}
}
We should be able to see the WfSpec
in our dashboard now:

Running the WfSpec
Let's run the WfSpec
, and send anakin
the message You're on this council, but we do not grant you the rank of master.
:
lhctl run send-message user-id anakin message "You're on this council, but we do not grant you the rank of master."
If we look at the WfRun
on the dashboard, you can see that it turns out Anakin prefers Holograms rather than Comlink messages (but if you run it for padme
, you'll find out that our favorite Senator prefers Holograms).

If you pay attention, obiwan
and satine
have the same preferred contact method.
Gotchas
When building your IfBody
, it is important to NOT use the WorkflowThread
of the base. If you do that, you'll get an error saying that you need to use the handler.
Here's an example of doing it wrong:
public void wfLogic(WorkflowThread wf) {
var myVariable = wf.declareStr("input");
wf.doIf(myVariable.isEqualTo("something"), handler -> {
wf.execute("some-task");
});
}
And here is the right way:
public void wfLogic(WorkflowThread wf) {
var myVariable = wf.declareStr("input");
wf.doIf(myVariable.isEqualTo("something"), handler -> {
handler.execute("some-task");
});
}
Don't worry, LittleHorse won't let you mess it up; we'll give you an error message rather than fail silently. But keep that in mind!
Further Resources
Congrats on learning how conditionals work in LittleHorse! If you want to dive further, check out the following:
- How to build a workflow that uses loops!
- The
Node
andEdge
protobuf structures in ourWfSpec
schema.
We are considering enhancements to the Dashboard which will allow you to visualize the comparisons that occurred at each conditional branch. The goal is let you easily determine why a certain WfRun
went down a certain path.
If you have any suggestions on how this should look, drop us a note in Slack!