# flo
**Repository Path**: mirrors_spotify/flo
## Basic Information
- **Project Name**: flo
- **Description**: A lightweight workflow definition library
- **Primary Language**: Unknown
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-08-18
- **Last Updated**: 2026-02-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
#  [](https://circleci.com/gh/spotify/flo/tree/master) [](https://codecov.io/github/spotify/flo) [](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.spotify%22%20flo*) [](LICENSE.txt)
**Please note that we, at Spotify, have ceased further development of flo, so no new features will come;
on the other hand, we will fix critical issues.**
 is a lightweight workflow definition library
* It's not a workflow management system
* It's not a workflow scheduler
__Some key features__
* Programmatic Java and Scala API for expressing workflow construction (_task [DAG] expansion_)
* Use of arbitrary program logic for DAG expansion
* Recursive definitions
* Lazy DAG expansion
* DAG serialization (for 3rd party persistence)
* Extensible DAG evaluation
## Dependency
```xml
com.spotifyflo-workflow${flo.version}
```
```sbt
"com.spotify" %% "flo-scala" % floVersion
```
JavaDocs here: http://spotify.github.io/flo/maven/latest/apidocs/
## Table of contents
- [Quick Example: Fibonacci](#quick-example-fibonacci)
- [`Task`](#taskt)
- [Tasks are defined by regular methods](#tasks-are-defined-by-regular-methods)
- [Task embedding](#task-embedding)
- [Tasks are lazy](#tasks-are-lazy)
- [Task DAGs as data structures](#task-dags-as-data-structures)
- [`EvalContext`](#evalcontext)
## Quick Example: Fibonacci
Fibonacci serves as a good example even though it's not at all the kind of thing that `flo` is
meant to be used for. Nevertheless, it demonstrates how a task DAG can be recursively defined with
arbitrary logic governing which inputs are chosen.
```java
class Fib {
static Task fib(long n) {
TaskBuilder builder = Task.named("fib", n).ofType(Long.class);
if (n < 2) {
return builder
.process(() -> n);
} else {
return builder
.input(() -> fib(n - 1))
.input(() -> fib(n - 2))
.process((a, b) -> a + b);
}
}
public static void main(String[] args) {
Task fib92 = fib(92);
EvalContext evalContext = MemoizingContext.composeWith(EvalContext.sync());
EvalContext.Value value = evalContext.evaluate(fib92);
value.consume(f92 -> System.out.println("fib(92) = " + f92));
}
}
```
Scala equivalent
```scala
import java.util.function.Consumer
import com.spotify.flo._
import com.spotify.flo.context.MemoizingContext
object Fib extends App {
def fib(n: Long): Task[Long] = defTask[Long](n) dsl (
if (n < 2) {
$ process n
} else {
$ input fib(n - 1) input fib(n - 2) process (_ + _)
}
)
val fib92 = fib(92)
val evalContext = MemoizingContext.composeWith(EvalContext.sync)
val value = evalContext.evaluate(fib92)
value.consume(new Consumer[Long] {
//noinspection ScalaStyle
override def accept(t: Long): Unit = Console.println(s"fib(92) = ${t}")
})
}
```
For more details on a high-level runner implementation, see [`flo-runner`][flo-runner].
# [`Task`][Task]
[`Task`][Task] is one of the more central types in `flo`. It represents some task which will
evaluate a value of type `T`. It has a parameterized name, zero or more input tasks and a
processing function which will be executed when inputs are evaluated. Tasks come with a few key
properties governing how they are defined, behave and are interacted with. We'll cover these in the
following sections.
## Tasks are defined by regular methods
Your workflow tasks are not defined as classes that extend [`Task`][Task], rather they are
defined by using the `TaskBuilder` API as we've already seen in the fibonacci example. This is in
many ways very similar to a very clean class with no mutable state, only final members and two
overridden methods for inputs and evaluation function. But with a very important difference, we're
handling the input tasks in a type-safe manner. Each input task you add will further construct the
type for your evaluation function. This is how we can get a clean lambda such as `(a, b) -> a + b`
as the evaluation function for our fibonacci example.
Here's a simple example of a `flo` task depending on two other tasks:
```java
Task myTask(String arg) {
return Task.named("MyTask", arg).ofType(Integer.class)
.input(() -> otherTask(arg))
.input(() -> yetATask(arg))
.process((otherResult, yetAResult) -> /* ... */);
}
```
This is how the same thing would typically look like in other libraries:
```java
class MyTask extends Task {
private final String arg;
MyTask(String arg) {
super("MyTask", arg);
this.arg = arg;
}
@Override
public List extends Task>> inputs() {
return Arrays.asList(new OtherTask(arg), new YetATask(arg));
}
@Override
public Integer process(List