---
title: "Concurrency: and groups"
image: "https://hem.run/social/hem.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://hem.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Concurrency: and groups

Sequential bindings become concurrent by joining them with `and`:

```hem title="dashboard.hem"
let dashboard = (id: string) => effect {
  let user  = fetchUser(id)!
  and orgs  = fetchOrgs(id)!
  and prefs = fetchPrefs(id)!
  render(user, orgs, prefs)
}
```

```ts emitted title="dashboard.ts — generated"
const { user, orgs, prefs } =
  yield *
  Effect.all(
{ user: fetchUser(id), orgs: fetchOrgs(id), prefs: fetchPrefs(id) },
{ concurrency: "unbounded" },
  );
```

(Plain-identifier patterns get the record form, self-documenting output; destructuring patterns fall back to the positional tuple.)

Grouped bindings are **simultaneous**: no member's initializer may reference a name bound in the same group, and the compiler enforces it. That check is what makes the implied concurrency sound:

<!-- Sibling references would make the concurrency implied by `and` unsound. -->
```hem
let dashboard = (id: string) => effect {
  let user = fetchUser(id)!
  and orgs = fetchOrgs(user.orgId)!
  {user, orgs}
}
```

```
error[HEM2007] dashboard.hem:3:24
  |
3 |   and orgs = fetchOrgs(user.orgId)!
  |                        ^^^^ Group member 2 references sibling binding `user`. Move that dependency into a separate `let` before the group.
```

The same shape works on promises in plain async code (`let a = await fa() and b = await fb()` lowers to `await Promise.all([fa(), fb()])`), with one difference in behavior: on failure, effect groups _interrupt_ their in-flight siblings (structured concurrency); promise groups reject while remaining promises continue running, because promises cannot be cancelled. Markers are all-or-nothing per group (every member `!`-ed, or every member awaited, or a pure group meaning "evaluate all, then bind"), the group's binder keyword heads the whole group: `use conn = Db.acquire and cache = Redis.acquire` acquires concurrently into the block scope (an extension-only form: without scopes, a partially failed concurrent acquisition would leak, so core `use` stays out of groups), and `let`/`use` don't mix within one group. First failure interrupts; the HEM41xx provenance still blames the exact member that introduced a requirement or error.

When one member depends on another, several approaches are available: split the dependency into a separate `let` (a sequential phase); name the chain and destructure it as a member (`let { user, posts } = loadUserPosts(id)! and notifications = fetchNotifications(id)!` for concurrent execution without new syntax); use `Effect.fork` + `Fiber.join` for ad-hoc DAGs; or use `Effect.forEach` with a concurrency option for collections. Plain async code follows the same patterns using standard JavaScript mechanisms: an async function for the named chain, and eager promises for fork/join operations.

Source: https://hem.run/tour/concurrency-and-groups/index.mdx
