All chapters21
On this page

Guide · Chapter 8

Parallel and Watch

Bounded parallel work, cancellation, and filesystem event streams.

1.7.1 controlled concurrency and atomicity

This release fixes resource lifetimes and delivers seven experimental APIs, disabled by default. Review state ownership, cooperative file locks, strict transactions and bounded-task guarantees before enabling them.

Read the complete guide and examplesIncludes the guarantee matrix, failure handling and actual validation evidence

parallel is a bounded concurrent map

example.hhy
let urls = [
    "https://example.com",
    "https://example.org"
]

urls
    |> stream
    |> parallel(2) { url ->
    http.get(url)
        |> timeout(5s)
        |> send
}
    |> print
BehaviorGuarantee
ConcurrencyAt most n workers, subject to RuntimeLimits
Output orderMatches input order
BackpressureInput and result buffers are bounded
ErrorsThe first unhandled Error cancels remaining work
ReturnConcurrent map; child Streams are not flattened

If a closure returns Stream, apply flat_map after parallel. Dry-run creates no workers but still checks closure Effects in order.

Sendable values and isolation

Workers receive frozen snapshots of input and captured values, never shared mutable objects. Null, Bool, numbers, String, units, Path, and ordinary Lists/Maps/system snapshots whose fields are sendable can be copied.

watch and FileEvent

example.hhy
watch(path("./src"))
    |> where { event -> event.kind == "created" or event.kind == "modified" }
    |> debounce(300ms)
    |> for_each { event ->
    print(event.path)
}

watch(path, { recursive? }) returns an infinite Stream<FileEvent>. FileEvent exposes read-only kind, path, old_path, and timestamp; kind is created, modified, removed, or renamed, and old_path exists only for renamed. Event sequences vary by platform and editor; first create a file in a test directory to verify the watcher, then check modification and rename behavior.

FieldContents
kindcreated, modified, removed, or renamed
pathTarget Path
old_pathOriginal Path for renamed; otherwise null
timestampEvent DateTime

watch is an infinite Stream. End it with Ctrl+C, timeout, or cancel. Recursive watch obeys max_open_files, and filesystems may merge rapid duplicate events.

debounce and every

debounce(window) is leading-edge: the first scalar or kind + path FileEvent emits immediately; duplicates inside the window are coalesced and reset the window. Different event keys do not block each other.

every(duration) returns an infinite tick Stream. If downstream is busy, backpressure prevents overlapping the same tick. Apply take or a business window before collect/sort/group.

Cancellation and cleanup

Ctrl+C, timeout, cancel(), and unhandled errors trigger one root CancellationToken. Watchers, sleep, HTTP, child processes, and workers poll it; cancellation closes queues and handles, terminates children, and propagates Stream close upstream.