Skip to content

Guidelines

In Effect, runMain is the primary entry point for executing an Effect application on Node.js.

Example (Running an Effect Application with Graceful Teardown)

1
import { Effect, Console, Schedule, pipe } from "effect"
2
import { NodeRuntime } from "@effect/platform-node"
3
4
const program = pipe(
5
Effect.addFinalizer(() => Console.log("Application is about to exit!")),
6
Effect.andThen(Console.log("Application started!")),
7
Effect.andThen(
8
Effect.repeat(Console.log("still alive..."), {
9
schedule: Schedule.spaced("1 second")
10
})
11
),
12
Effect.scoped
13
)
14
15
// No graceful teardown on CTRL+C
16
// Effect.runPromise(program)
17
18
// Use NodeRuntime.runMain for graceful teardown on CTRL+C
19
NodeRuntime.runMain(program)
20
/*
21
Output:
22
Application started!
23
still alive...
24
still alive...
25
still alive...
26
still alive...
27
^C <-- CTRL+C
28
Application is about to exit!
29
*/

The runMain function handles finding and interrupting all fibers. Internally, it observes the fiber and listens for sigint signals, ensuring a graceful shutdown of the application when interrupted (e.g., using CTRL+C).

Effect provides versions of runMain tailored for different platforms:

PlatformRuntime VersionImport Path
Node.jsNodeRuntime.runMain@effect/platform-node
BunBunRuntime.runMain@effect/platform-bun
BrowserBrowserRuntime.runMain@effect/platform-browser

Avoid using tacit (point-free) function calls, such as Effect.map(fn), or using flow from the effect/Function module.

In Effect, it’s generally safer to write functions explicitly:

Effect.map((x) => fn(x))

rather than in a point-free style:

Effect.map(fn)

While tacit functions may be appealing for their brevity, they can introduce a number of problems:

  • Using tacit functions, particularly when dealing with optional parameters, can be unsafe. For example, if a function has overloads, writing it in a tacit style may erase all generics, resulting in bugs. Check out this X thread for more details: link to thread.

  • Tacit usage can also compromise TypeScript’s ability to infer types, potentially causing unexpected errors. This isn’t just a matter of style but a way to avoid subtle mistakes that can arise from type inference issues.

  • Additionally, stack traces might not be as clear when tacit usage is employed.

Avoiding tacit usage is a simple precaution that makes your code more reliable.