What is do-notation?
Do-notation is a pattern borrowed from functional languages that lets you write monadic pipelines as straightforward imperative code. Instead of nesting.andThen() callbacks, you write a generator function where each yield* statement either unwraps the Ok value and continues, or short-circuits the entire function with the Err.
The result is code that reads top-to-bottom like ordinary async/await, while still being fully type-safe and never silently swallowing errors.
chas.go() for Result and ResultAsync
chas.go() runs an async function* generator and returns a ResultAsync. Inside the generator, you can yield* both synchronous Result values and ResultAsync values; they’re both handled the same way.
How it works:
yield* someResult— ifsomeResultisOk, the expression evaluates to the inner value; if it’sErr, the generator exits immediately andchas.go()returns thatErr.- The
returnvalue at the end becomes theOkvalue of the finalResultAsync. - The
Errtype of the returnedResultAsyncis the union of all error types produced by everyyield*call.
yield* into the final Err union, so the compiler tells you exactly which failures you need to handle.
Task.go() for Task pipelines
When your pipeline is built entirely from Task instances (especially when you want lazy execution, retries, or dependency injection) use Task.go() instead. It has the same yield* semantics but returns a Task you can configure further before calling .execute().
Task is lazy, nothing runs until you call .execute(). You can attach .retry(), .timeout(), .circuitBreaker(), and other operators to the composed task after building it with Task.go().
Side-by-side comparison
The example below fetches a user, parses their config, and saves their profile. Both versions are equivalent: one uses chained.andThen() calls, the other uses chas.go().
- .andThen() chains
- do-notation with chas.go()
asyncAndThen the moment any step returns a ResultAsync, and you must manually close the variable scope to reference earlier values in later steps.Error handling after chas.go()
The return value of chas.go() is a ResultAsync, so you handle errors the same way you would with any other result: