Effect CommunityEC
Effect Community3y ago
3 replies
imagio

Challenges with Using forkDaemon in Service Construction

I keep ending up in a situation where I want to construct a service that uses forkDaemon to start some long running work associated with the service but this results in a self-referential layer. For example:

interface TestService {
    readonly _tag: "TestService"
    foo: string
}

const TestService = Context.Tag<TestService>()

const doBackgroundStuffWithTestService = pipe(
    Effect.tap(TestService, t => Effect.log(t.foo)),
    Effect.tap(() => Effect.sleep(Duration.seconds(1))),
    Effect.forever,
    Effect.forkDaemon
)

const makeTestService = () =>
    Effect.gen(function* (_) {
        const fiberDoingStuffInBackground = yield* _(doBackgroundStuffWithTestService)
        return { _tag: "TestService" as const, foo: "foo" }
    })

const makeLayer = () => Layer.effect(TestService, makeTestService())

const liveLayer: Layer.Layer<TestService, never, TestService> = makeLayer()


In the example the layer is self-referential because of doBackgroundStuffWithTestService which obviously won't work. One solution could be to pass anything I need as function arguments to doBackgroundStuffWithTestService but that doesn't seem as clean as just consuming the service as things grow in complexity.

In my app the use case is things like wrapping event listeners that have some relevance to a service. For example DeviceStateService needs to forkDaemon a fiber to keep a subscription ref in sync with react-native's AppState.addEventListener.

Has anybody else run into this pattern where it would be nice for layer to be able to consume the service that it's producing? Any suggestions on a better way to structure this would be appreciated, thanks!
Was this page helpful?