# Concurrent Dataflow (Incubating)

version 6.0.0-beta-2

## 1. Introduction

Dataflow programming lets you express dependencies between concurrent computations declaratively. Values are bound once and read many times — the runtime handles ordering automatically. You never need to think about which thread runs first.

Groovy’s dataflow support integrates natively with async/await and the `Awaitable` API.

## 2. DataflowVariable

A `DataflowVariable` is a single-assignment variable. It starts unbound, can be written to exactly once, and any thread that reads it before binding will block until a value is available.

    import groovy.concurrent.DataflowVariable

    def x = new DataflowVariable()
    def y = new DataflowVariable()
    def z = new DataflowVariable()

    async {
        z << await(x) + await(y)    // blocks until x and y are bound
    }

    async { x << 10 }              // bind x — order doesn't matter
    async { y << 5 }               // bind y

    println "Result: ${await(z)}"  // 15

Key properties:

- Write-once: calling `bind()` or `<<` a second time throws `IllegalStateException`

- Implements `Awaitable`: works with `await`, combinators (`Awaitable.all`), and `then` chaining

- Thread-safe: safe to bind from one thread and read from any number of others

### 2.1. Error binding

    def v = new DataflowVariable()
    v.bindError(new RuntimeException('computation failed'))
    // Any thread that awaits v will receive the exception

### 2.2. Chaining with `then`

    def v = new DataflowVariable()
    def doubled = v.then { it * 2 }

    async { v << 21 }
    assert await(doubled) == 42

### 2.3. Combining multiple variables

    def a = new DataflowVariable()
    def b = new DataflowVariable()
    def c = new DataflowVariable()

    async { a << 1 }
    async { b << 2 }
    async { c << 3 }

    def results = await(a, b, c)
    assert results == [1, 2, 3]

## 3. Dataflows

`Dataflows` is a convenience class that auto-creates `DataflowVariable` instances on demand. Property reads block until bound; property writes bind the variable.

    import groovy.concurrent.Dataflows

    def df = new Dataflows()

    async { df.z = df.x + df.y }   // blocks on x and y, binds z
    async { df.x = 10 }
    async { df.y = 5 }

    println "Result: ${df.z}"      // 15

This is the most concise way to write dataflow programs in Groovy. Variables are created lazily — no declarations needed.

    def df = new Dataflows()

    async { df.fullName = "${df.first} ${df.last}" }
    async { df.first = 'John' }
    async { df.last = 'Doe' }

    assert df.fullName == 'John Doe'

### 3.1. Accessing the underlying variable

Use `getVariable()` when you need the `DataflowVariable` itself (e.g., to pass to `Awaitable.all` or check `isBound`):

    def df = new Dataflows()
    def v = df.getVariable('x')   // DataflowVariable, does not block
    assert !v.isBound()
    async { df.x = 42 }
    assert await(v) == 42