Skip to content

Middleware Usage

Declaring Side-effect Handlers

Sonnets immediately execute the provided root Stanza in a forking manner. Stanzas must be declared as part of Sonnet instantiation.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
import {
import Sonnet
Sonnet
} from "redux-sonnet"
declare const
const initialize: Effect.Effect<void, never, never>
initialize
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<void>
declare const
const watchThingOne: Effect.Effect<void, never, never>
watchThingOne
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<void>
declare const
const watchThingTwo: Effect.Effect<void, never, never>
watchThingTwo
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<void>
/**
* Run `initialize` first, and then run `watchThingOne` and `watchThingTwo`
* concurrently.
*/
const
const rootStanza: Effect.Effect<void, never, never>

Run initialize first, and then run watchThingOne and watchThingTwo concurrently.

rootStanza
=
const initialize: Effect.Effect<void, never, never>
initialize
.
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<void, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <void, Effect.Effect<void, never, never>>(f: (a: void) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<void, E, R>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
(() =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const all: <readonly [Effect.Effect<void, never, never>, Effect.Effect<void, never, never>], {
discard: true;
concurrency: "unbounded";
}>(arg: readonly [Effect.Effect<void, never, never>, Effect.Effect<...>], options?: {
discard: true;
concurrency: "unbounded";
} | undefined) => Effect.Effect<...>

Combines multiple effects into one, returning results based on the input structure.

Details

Use this function when you need to run multiple effects and combine their results into a single output. It supports tuples, iterables, structs, and records, making it flexible for different input types.

For instance, if the input is a tuple:

// ┌─── a tuple of effects
// ▼
Effect.all([effect1, effect2, ...])

the effects are executed sequentially, and the result is a new effect containing the results as a tuple. The results in the tuple match the order of the effects passed to Effect.all.

Concurrency

You can control the execution order (e.g., sequential vs. concurrent) using the concurrency option.

Short-Circuiting Behavior

This function stops execution on the first error it encounters, this is called "short-circuiting". If any effect in the collection fails, the remaining effects will not run, and the error will be propagated. To change this behavior, you can use the mode option, which allows all effects to run and collect results as Either or Option.

The mode option

The { mode: "either" } option changes the behavior of Effect.all to ensure all effects run, even if some fail. Instead of stopping on the first failure, this mode collects both successes and failures, returning an array of Either instances where each result is either a Right (success) or a Left (failure).

Similarly, the { mode: "validate" } option uses Option to indicate success or failure. Each effect returns None for success and Some with the error for failure.

@seeforEach for iterating over elements and applying an effect.

@seeallWith for a data-last version of this function.

@example

// Title: Combining Effects in Tuples
import { Effect, Console } from "effect"
const tupleOfEffects = [
Effect.succeed(42).pipe(Effect.tap(Console.log)),
Effect.succeed("Hello").pipe(Effect.tap(Console.log))
] as const
// ┌─── Effect<[number, string], never, never>
// ▼
const resultsAsTuple = Effect.all(tupleOfEffects)
Effect.runPromise(resultsAsTuple).then(console.log)
// Output:
// 42
// Hello
// [ 42, 'Hello' ]

@example

// Title: Combining Effects in Iterables import { Effect, Console } from "effect"

const iterableOfEffects: Iterable<Effect.Effect> = [1, 2, 3].map( (n) => Effect.succeed(n).pipe(Effect.tap(Console.log)) )

// ┌─── Effect<number[], never, never> // ▼ const resultsAsArray = Effect.all(iterableOfEffects)

Effect.runPromise(resultsAsArray).then(console.log) // Output: // 1 // 2 // 3 // [ 1, 2, 3 ]

@example

// Title: Combining Effects in Structs import { Effect, Console } from "effect"

const structOfEffects = { a: Effect.succeed(42).pipe(Effect.tap(Console.log)), b: Effect.succeed("Hello").pipe(Effect.tap(Console.log)) }

// ┌─── Effect<{ a: number; b: string; }, never, never> // ▼ const resultsAsStruct = Effect.all(structOfEffects)

Effect.runPromise(resultsAsStruct).then(console.log) // Output: // 42 // Hello // { a: 42, b: 'Hello' }

@example

// Title: Combining Effects in Records import { Effect, Console } from "effect"

const recordOfEffects: Record<string, Effect.Effect> = { key1: Effect.succeed(1).pipe(Effect.tap(Console.log)), key2: Effect.succeed(2).pipe(Effect.tap(Console.log)) }

// ┌─── Effect<{ [x: string]: number; }, never, never> // ▼ const resultsAsRecord = Effect.all(recordOfEffects)

Effect.runPromise(resultsAsRecord).then(console.log) // Output: // 1 // 2 // { key1: 1, key2: 2 }

@example

// Title: Short-Circuiting Behavior import { Effect, Console } from "effect"

const program = Effect.all([ Effect.succeed("Task1").pipe(Effect.tap(Console.log)), Effect.fail("Task2: Oh no!").pipe(Effect.tap(Console.log)), // Won't execute due to earlier failure Effect.succeed("Task3").pipe(Effect.tap(Console.log)) ])

Effect.runPromiseExit(program).then(console.log) // Output: // Task1 // { // _id: 'Exit', // _tag: 'Failure', // cause: { _id: 'Cause', _tag: 'Fail', failure: 'Task2: Oh no!' } // }

@example

// Title: Collecting Results with mode: "either" import { Effect, Console } from "effect"

const effects = [ Effect.succeed("Task1").pipe(Effect.tap(Console.log)), Effect.fail("Task2: Oh no!").pipe(Effect.tap(Console.log)), Effect.succeed("Task3").pipe(Effect.tap(Console.log)) ]

const program = Effect.all(effects, { mode: "either" })

Effect.runPromiseExit(program).then(console.log) // Output: // Task1 // Task3 // { // _id: 'Exit', // _tag: 'Success', // value: [ // { _id: 'Either', _tag: 'Right', right: 'Task1' }, // { _id: 'Either', _tag: 'Left', left: 'Task2: Oh no!' }, // { _id: 'Either', _tag: 'Right', right: 'Task3' } // ] // }

@example

//Example: Collecting Results with mode: "validate" import { Effect, Console } from "effect"

const effects = [ Effect.succeed("Task1").pipe(Effect.tap(Console.log)), Effect.fail("Task2: Oh no!").pipe(Effect.tap(Console.log)), Effect.succeed("Task3").pipe(Effect.tap(Console.log)) ]

const program = Effect.all(effects, { mode: "validate" })

Effect.runPromiseExit(program).then((result) => console.log("%o", result)) // Output: // Task1 // Task3 // { // _id: 'Exit', // _tag: 'Failure', // cause: { // _id: 'Cause', // _tag: 'Fail', // failure: [ // { _id: 'Option', _tag: 'None' }, // { _id: 'Option', _tag: 'Some', value: 'Task2: Oh no!' }, // { _id: 'Option', _tag: 'None' } // ] // } // }

@since2.0.0

all
([
const watchThingOne: Effect.Effect<void, never, never>
watchThingOne
,
const watchThingTwo: Effect.Effect<void, never, never>
watchThingTwo
], {
discard: true
discard
: true,
concurrency: "unbounded"
concurrency
: "unbounded"
})
)
)
const
const sonnet: Sonnet.Sonnet<never, never>
sonnet
=
import Sonnet
Sonnet
.
const make: <never, never>(rootEffect: Effect.Effect<void, never, Sonnet.SonnetService>, layer: Layer<Sonnet.SonnetService, never, never>, memoMap?: MemoMap | undefined) => Sonnet.Sonnet<...>

Construct a redux-sonnet middleware.

NOTE: side-effect handlers are provided up-front to guarantee declarative behavior.

@example

import { configureStore, Tuple as RTKTuple } from "@reduxjs/toolkit"
import { Sonnet, Stanza } from "redux-sonnet"
import { Stream, Layer } from "effect"
const stanza = Stanza.fromStream(Stream.make(
{ type: "ACTION-1" },
{ type: "ACTION-2" },
{ type: "ACTION-3" },
))
const sonnet = Sonnet.make(
// ^?
stanza,
Sonnet.defaultLayer,
)
const store = configureStore({
// ^?
reducer: () => {},
middleware: () => new RTKTuple(sonnet)
})

@since0.0.0

make
(
const rootStanza: Effect.Effect<void, never, never>

Run initialize first, and then run watchThingOne and watchThingTwo concurrently.

rootStanza
,
import Sonnet
Sonnet
.
const defaultLayer: Layer<Sonnet.SonnetService, never, never>

@since0.0.0

defaultLayer
)

Deciding Data Structures

Sonnets accept a Layer which describes the data structures used to communicate between side-effect declarations (Stanzas) and the Redux dispatcher.

Data structures are modeled as such:

  • Actions — A Queue which has configurable capacity and bounding behaviors.
  • Reducer State — A SynchronizedRef which has current value exposed as well as a Stream of its changes over time.
  • Dispatch Actions — An unbounded Queue. * this needs consideration

Default

const
const sonnet: Sonnet.Sonnet<never, never>
sonnet
=
import Sonnet
Sonnet
.
const make: <never, never>(rootEffect: Effect.Effect<void, never, Sonnet.SonnetService>, layer: Layer<Sonnet.SonnetService, never, never>, memoMap?: MemoMap | undefined) => Sonnet.Sonnet<...>

Construct a redux-sonnet middleware.

NOTE: side-effect handlers are provided up-front to guarantee declarative behavior.

@example

import { configureStore, Tuple as RTKTuple } from "@reduxjs/toolkit"
import { Sonnet, Stanza } from "redux-sonnet"
import { Stream, Layer } from "effect"
const stanza = Stanza.fromStream(Stream.make(
{ type: "ACTION-1" },
{ type: "ACTION-2" },
{ type: "ACTION-3" },
))
const sonnet = Sonnet.make(
// ^?
stanza,
Sonnet.defaultLayer,
)
const store = configureStore({
// ^?
reducer: () => {},
middleware: () => new RTKTuple(sonnet)
})

@since0.0.0

make
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

Represents an effect that does nothing and produces no value.

When to Use

Use this effect when you need to represent an effect that does nothing. This is useful in scenarios where you need to satisfy an effect-based interface or control program flow without performing any operations. For example, it can be used in situations where you want to return an effect from a function but do not need to compute or return any result.

@since2.0.0

void
,
import Sonnet
Sonnet
.defaultLayer
const defaultLayer: Layer<Sonnet.SonnetService, never, never>

@since0.0.0

)

Configurable

const
const sonnet: Sonnet.Sonnet<never, never>
sonnet
=
import Sonnet
Sonnet
.
const make: <never, never>(rootEffect: Effect.Effect<void, never, Sonnet.SonnetService>, layer: Layer<Sonnet.SonnetService, never, never>, memoMap?: MemoMap | undefined) => Sonnet.Sonnet<...>

Construct a redux-sonnet middleware.

NOTE: side-effect handlers are provided up-front to guarantee declarative behavior.

@example

import { configureStore, Tuple as RTKTuple } from "@reduxjs/toolkit"
import { Sonnet, Stanza } from "redux-sonnet"
import { Stream, Layer } from "effect"
const stanza = Stanza.fromStream(Stream.make(
{ type: "ACTION-1" },
{ type: "ACTION-2" },
{ type: "ACTION-3" },
))
const sonnet = Sonnet.make(
// ^?
stanza,
Sonnet.defaultLayer,
)
const store = configureStore({
// ^?
reducer: () => {},
middleware: () => new RTKTuple(sonnet)
})

@since0.0.0

make
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

Represents an effect that does nothing and produces no value.

When to Use

Use this effect when you need to represent an effect that does nothing. This is useful in scenarios where you need to satisfy an effect-based interface or control program flow without performing any operations. For example, it can be used in situations where you want to return an effect from a function but do not need to compute or return any result.

@since2.0.0

void
,
import Sonnet
Sonnet
.
const layer: (options: Sonnet.Sonnet<LA, LE>.Options) => Layer<Sonnet.SonnetService, never, never>

@since0.0.0

layer
({
replay: 8,
Sonnet<LA, LE>.Options.replay?: number | undefined

Number of actions to replay to late subscribers.

@default0

backing: {
Sonnet<LA, LE>.Options.backing?: {
strategy: "unbounded";
} | {
strategy: "bounded";
capacity: number;
} | {
strategy: "dropping";
capacity: number;
} | {
strategy: "sliding";
capacity: number;
} | undefined

Configuration for queue-backed action stream.

strategy: "bounded"
strategy
: "bounded",
capacity: number
capacity
: 8
}
})
)

Advanced

Any Effect service which satisfies Sonnet.Service may be constructed manually.

See Sonnet.Service for reference.

Adding to Redux

With @reduxjs/toolkit

const
const sonnet: Sonnet.Sonnet<never, never>
sonnet
=
import Sonnet
Sonnet
.
const make: <never, never>(rootEffect: Effect.Effect<void, never, Sonnet.SonnetService>, layer: Layer<Sonnet.SonnetService, never, never>, memoMap?: MemoMap | undefined) => Sonnet.Sonnet<...>

Construct a redux-sonnet middleware.

NOTE: side-effect handlers are provided up-front to guarantee declarative behavior.

@example

import { configureStore, Tuple as RTKTuple } from "@reduxjs/toolkit"
import { Sonnet, Stanza } from "redux-sonnet"
import { Stream, Layer } from "effect"
const stanza = Stanza.fromStream(Stream.make(
{ type: "ACTION-1" },
{ type: "ACTION-2" },
{ type: "ACTION-3" },
))
const sonnet = Sonnet.make(
// ^?
stanza,
Sonnet.defaultLayer,
)
const store = configureStore({
// ^?
reducer: () => {},
middleware: () => new RTKTuple(sonnet)
})

@since0.0.0

make
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

Represents an effect that does nothing and produces no value.

When to Use

Use this effect when you need to represent an effect that does nothing. This is useful in scenarios where you need to satisfy an effect-based interface or control program flow without performing any operations. For example, it can be used in situations where you want to return an effect from a function but do not need to compute or return any result.

@since2.0.0

void
,
import Sonnet
Sonnet
.
const defaultLayer: Layer<Sonnet.SonnetService, never, never>

@since0.0.0

defaultLayer
)
const store =
configureStore<void, UnknownAction, Tuple<[Sonnet.Sonnet<never, never>]>, Tuple<[StoreEnhancer<{
dispatch: {};
}>, StoreEnhancer]>, void>(options: ConfigureStoreOptions<...>): EnhancedStore<...>

A friendly abstraction over the standard Redux createStore() function.

@paramoptions The store configuration.

@returnsA configured Redux store.

@public

configureStore
({
const store: EnhancedStore<void, UnknownAction, Tuple<[StoreEnhancer<{
dispatch: {};
}>, StoreEnhancer]>>
ConfigureStoreOptions<void, UnknownAction, Tuple<[Sonnet<never, never>]>, Tuple<[StoreEnhancer<{ dispatch: {}; }>, StoreEnhancer]>, void>.reducer: void | Reducer<void, UnknownAction, void>

A single reducer function that will be used as the root reducer, or an object of slice reducers that will be passed to combineReducers().

reducer
: () => {},
ConfigureStoreOptions<void, UnknownAction, Tuple<[Sonnet<never, never>]>, Tuple<[StoreEnhancer<{ dispatch: {}; }>, StoreEnhancer]>, void>.middleware?: (getDefaultMiddleware: GetDefaultMiddleware<void>) => Tuple<[Sonnet.Sonnet<never, never>]>

An array of Redux middleware to install, or a callback receiving getDefaultMiddleware and returning a Tuple of middleware. If not supplied, defaults to the set of middleware returned by getDefaultMiddleware().

@example middleware: (gDM) => gDM().concat(logger, apiMiddleware, yourCustomMiddleware)

@seehttps://redux-toolkit.js.org/api/getDefaultMiddleware#intended-usage

middleware
: () => new
new Tuple<[Sonnet.Sonnet<never, never>]>(items_0: Sonnet.Sonnet<never, never>): Tuple<[Sonnet.Sonnet<never, never>]> (+1 overload)
Tuple
(
const sonnet: Sonnet.Sonnet<never, never>
sonnet
)
})

With redux

const
const sonnet: Sonnet.Sonnet<never, never>
sonnet
=
import Sonnet
Sonnet
.
const make: <never, never>(rootEffect: Effect.Effect<void, never, Sonnet.SonnetService>, layer: Layer<Sonnet.SonnetService, never, never>, memoMap?: MemoMap | undefined) => Sonnet.Sonnet<...>

Construct a redux-sonnet middleware.

NOTE: side-effect handlers are provided up-front to guarantee declarative behavior.

@example

import { configureStore, Tuple as RTKTuple } from "@reduxjs/toolkit"
import { Sonnet, Stanza } from "redux-sonnet"
import { Stream, Layer } from "effect"
const stanza = Stanza.fromStream(Stream.make(
{ type: "ACTION-1" },
{ type: "ACTION-2" },
{ type: "ACTION-3" },
))
const sonnet = Sonnet.make(
// ^?
stanza,
Sonnet.defaultLayer,
)
const store = configureStore({
// ^?
reducer: () => {},
middleware: () => new RTKTuple(sonnet)
})

@since0.0.0

make
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

Represents an effect that does nothing and produces no value.

When to Use

Use this effect when you need to represent an effect that does nothing. This is useful in scenarios where you need to satisfy an effect-based interface or control program flow without performing any operations. For example, it can be used in situations where you want to return an effect from a function but do not need to compute or return any result.

@since2.0.0

void
,
import Sonnet
Sonnet
.
const defaultLayer: Layer<Sonnet.SonnetService, never, never>

@since0.0.0

defaultLayer
)
const store =
applyMiddleware<unknown, any>(middleware1: Middleware<unknown, any, any>): StoreEnhancer<{
dispatch: unknown;
}, {}> (+6 overloads)

Creates a store enhancer that applies middleware to the dispatch method of the Redux store. This is handy for a variety of tasks, such as expressing asynchronous actions in a concise manner, or logging every action payload.

See redux-thunk package as an example of the Redux middleware.

Because middleware is potentially asynchronous, this should be the first store enhancer in the composition chain.

Note that each middleware will be given the dispatch and getState functions as named arguments.

@parammiddlewares The middleware chain to be applied.

@returnsA store enhancer applying the middleware.

applyMiddleware
(
const sonnet: Sonnet.Sonnet<never, never>
sonnet
)(
function createStore<S, A extends Action, Ext extends {} = {}, StateExt extends {} = {}>(reducer: Reducer<S, A>, enhancer?: StoreEnhancer<Ext, StateExt>): Store<S, A, UnknownIfNonSpecific<StateExt>> & Ext (+1 overload)

@deprecated

We recommend using the configureStore method of the @reduxjs/toolkit package, which replaces createStore.

Redux Toolkit is our recommended approach for writing Redux logic today, including store setup, reducers, data fetching, and more.

For more details, please read this Redux docs page: https://redux.js.org/introduction/why-rtk-is-redux-today

configureStore from Redux Toolkit is an improved version of createStore that simplifies setup and helps avoid common bugs.

You should not be using the redux core package by itself today, except for learning purposes. The createStore method from the core redux package will not be removed, but we encourage all users to migrate to using Redux Toolkit for all Redux code.

If you want to use createStore without this visual deprecation warning, use the legacy_createStore import instead:

import { legacy_createStore as createStore} from 'redux'

createStore
)(() => {})
const store: Store<void, Action<string>, {}> & Store<unknown, Action<string>, unknown> & {
dispatch: unknown;
}

Provisioning Resources

Side-effect handlers, or Stanzas, are resourceful, meaning they can request fulfillment of some set of services.

Layers

The second argument to Sonnet.make() is a Layer comprising the resources available to running side-effect handlers. See the below example for usage guidance. Note that SonnetService must be provisioned for all Sonnet instantiations.

Re-using a ManagedRuntime

If the integrating application already makes use of a ManagedRuntime, a MemoMap may be provided at Sonnet instantiation to facilitate layer memoization.

import {
import Context

@since2.0.0

@since2.0.0

Context
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Layer
Layer
,
import ManagedRuntime
ManagedRuntime
} from "effect"
import {
import Sonnet
Sonnet
} from "redux-sonnet"
class
class MyService
MyService
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"MyService">(id: "MyService") => <Self, Shape>() => Context.TagClass<Self, "MyService", Shape>

@example

import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("MyService")<
class MyService
MyService
,
{ readonly
_: Effect.Effect<unknown, never, never>
_
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<unknown> }
>() {}
const
const layer: Layer.Layer<MyService, never, never>
layer
=
import Layer
Layer
.
const succeed: <typeof MyService>(tag: typeof MyService, resource: {
readonly _: Effect.Effect<unknown>;
}) => Layer.Layer<MyService, never, never> (+1 overload)

Constructs a layer from the specified value.

@since2.0.0

succeed
(
class MyService
MyService
,
{
_: Effect.Effect<unknown, never, never>
_
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const succeed: <undefined>(value: undefined) => Effect.Effect<undefined, never, never>

Creates an Effect that always succeeds with a given value.

When to Use

Use this function when you need an effect that completes successfully with a specific value without any errors or external dependencies.

@seefail to create an effect that represents a failure.

@example

// Title: Creating a Successful Effect
import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)

@since2.0.0

succeed
(void 0) }
)
const existingRuntime =
import ManagedRuntime
ManagedRuntime
.
const make: <MyService, never>(layer: Layer.Layer<MyService, never, never>, memoMap?: Layer.MemoMap | undefined) => ManagedRuntime.ManagedRuntime<MyService, never>

Convert a Layer into an ManagedRuntime, that can be used to run Effect's using your services.

@since2.0.0

@example

import { Console, Effect, Layer, ManagedRuntime } from "effect"
class Notifications extends Effect.Tag("Notifications")<
Notifications,
{ readonly notify: (message: string) => Effect.Effect<void> }
>() {
static Live = Layer.succeed(this, { notify: (message) => Console.log(message) })
}
async function main() {
const runtime = ManagedRuntime.make(Notifications.Live)
await runtime.runPromise(Notifications.notify("Hello, world!"))
await runtime.dispose()
}
main()

make
(
const layer: Layer.Layer<MyService, never, never>
layer
)
const existingRuntime: ManagedRuntime.ManagedRuntime<MyService, never>
const sonnet =
import Sonnet
Sonnet
.
const make: <MyService, never>(rootEffect: Effect.Effect<void, never, MyService | Sonnet.SonnetService>, layer: Layer.Layer<MyService | Sonnet.SonnetService, never, never>, memoMap?: Layer.MemoMap | undefined) => Sonnet.Sonnet<...>

Construct a redux-sonnet middleware.

NOTE: side-effect handlers are provided up-front to guarantee declarative behavior.

@example

import { configureStore, Tuple as RTKTuple } from "@reduxjs/toolkit"
import { Sonnet, Stanza } from "redux-sonnet"
import { Stream, Layer } from "effect"
const stanza = Stanza.fromStream(Stream.make(
{ type: "ACTION-1" },
{ type: "ACTION-2" },
{ type: "ACTION-3" },
))
const sonnet = Sonnet.make(
// ^?
stanza,
Sonnet.defaultLayer,
)
const store = configureStore({
// ^?
reducer: () => {},
middleware: () => new RTKTuple(sonnet)
})

@since0.0.0

make
(
const sonnet: Sonnet.Sonnet<MyService, never>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

Represents an effect that does nothing and produces no value.

When to Use

Use this effect when you need to represent an effect that does nothing. This is useful in scenarios where you need to satisfy an effect-based interface or control program flow without performing any operations. For example, it can be used in situations where you want to return an effect from a function but do not need to compute or return any result.

@since2.0.0

void
,
import Layer
Layer
.
const mergeAll: <[Layer.Layer<MyService, never, never>, Layer.Layer<Sonnet.SonnetService, never, never>]>(layers_0: Layer.Layer<MyService, never, never>, layers_1: Layer.Layer<...>) => Layer.Layer<...>

Combines all the provided layers concurrently, creating a new layer with merged input, error, and output types.

@since2.0.0

mergeAll
(
const layer: Layer.Layer<MyService, never, never>
layer
,
import Sonnet
Sonnet
.
const defaultLayer: Layer.Layer<Sonnet.SonnetService, never, never>

@since0.0.0

defaultLayer
),
const existingRuntime: ManagedRuntime.ManagedRuntime<MyService, never>
existingRuntime
.memoMap
ManagedRuntime<MyService, never>.memoMap: Layer.MemoMap
)

Cleanup

TODO: Unit test and document runtime disposal.