Reusing Dependencies Between Multiple Runtimes in Effect Typescript

Working again on reusing dependencies between multiple runtimes, i concluded that the easiest way to go forward would be to provide dependencies through layers lower in the tree and through effects (returned by layers), higher in the tree.

Now, there are two problems. The first is: how can i use an Effect as a service type of a tag, where the requirements are allowed to be anything (in this case Foo is a requirement of Foobar, which we want to infer automatically through usage).

The second is: If we consider the Foo tag to be from a different module, isn't it preferable to avoid coupling here and what are the options here? Maybe something closer to having an interface which the service type of the provided implementation covers, instead of relying on Tags as identifiers?

class Foo extends Context.Tag(`Foo`)<Foo, string>() {}

class Foobar extends Context.Tag(`Bar`)<
  Foobar,
  // TODO: how can we use Effect.Effect<string> here, to avoid leaking implementation details? Using Foo inside the returned Effect doesn't automatically track it?
  Effect.Effect<string, never, Foo>
>() {}

const layerParent = Layer.succeed(Foo, 'foo');
const runtimeParent = ManagedRuntime.make(layerParent);

const layerChild = pipe(
  Layer.succeed(
    Foobar,
    // TODO: if Foo comes from a different module, can we prevent coupling against the module through these tags?
    Effect.andThen(Foo, (foo) => foo + 'bar')
  )
);
const runtimeChild = ManagedRuntime.make(layerChild);

const program = Effect.gen(function* () {
  const effect = yield* Foobar;
  return effect;
});

const fromChild = Effect.runSync(Effect.provide(program, runtimeChild));
const fromParent = Effect.provide(fromChild, runtimeParent);

const result = Effect.runSync(fromParent);
console.log(result); // prints foobar
Was this page helpful?