Skip to content

support for spawning child actors connected to life cycle of parent actor #7

@bhvngt

Description

@bhvngt

Thanks for publishing this library. I was looking out for xstate like library within effect-ts eco system and this appears to be the closest match.

As I was exploring the api, I was wondering if there is any support for spawning and managing child actor. I wrote a sample program to spawn child actors using spawnChild effect. However, for some reason, I am not able to get access to the child actor.

Here's my sample code

import { Effect, Schema, Option } from "effect";
import { ActorSystemDefault, ActorSystemService, Event, Machine, Slot, State } from "effect-machine";

const ChildState = State({ Idle: {}, Running: {} });
const ChildEvent = Event({ Start: {} });

const childMachine = Machine.make({
  state: ChildState,
  event: ChildEvent,
  initial: ChildState.Idle
}).on(ChildState.Idle, ChildEvent.Start, () => ChildState.Running).build();

const ParentState = State({ Idle: {}, Active: {} });
const ParentEvent = Event({ Activate: {} });

// Define the "Slot" for the effect
const ParentEffects = Slot.Effects({
  spawnChild: { id: Schema.String }
});

const config = {
  state: ParentState,
  event: ParentEvent,
  effects: ParentEffects,
  initial: ParentState.Idle
};

const parentMachine =
  Machine.make(config)
         .on(ParentState.Idle, ParentEvent.Activate, () => ParentState.Active)
         .spawn(ParentState.Active, ({ effects }) => effects.spawnChild({ id: "child-1" }))
         .build({
           spawnChild: ({ id }) =>
             Effect.gen(function* () {
               const system = yield* ActorSystemService;
               const childActor = yield* system.spawn(id, childMachine);
               yield* Effect.addFinalizer(() => Effect.log("closing child actor"));
               yield* childActor.send(ChildEvent.Start);
               yield* Effect.log(`Child actor ${id} spawned and started.`);
             }).pipe(Effect.catchTag("DuplicateActorError", e => Effect.log(e)), Effect.provide(ActorSystemDefault))
         });

Effect.gen(function* () {
  const system = yield* ActorSystemService;
  const actor = yield* system.spawn("parent", parentMachine);
  yield* actor.send(ParentEvent.Activate);
  yield* actor.waitFor(ParentState.Active);
  const childActor = yield* system.get("child-1");
  console.log(`Option.isSome(childActor)`, Option.isSome(childActor));
  if (Option.isSome(childActor)) {
    const childActorRef = childActor.value;
    childActorRef.subscribe((state) => console.log(state._tag))
    yield* childActorRef.waitFor(ChildState.Running);
  }
  yield* actor.stop;
}).pipe(Effect.scoped, Effect.provide(ActorSystemDefault), Effect.runPromise);

and here's my sample output

timestamp=2026-02-04T07:08:40.301Z level=INFO fiber=#2 message="Child actor child-1 spawned and started."

Option.isSome(childActor) false

timestamp=2026-02-04T07:08:40.306Z level=INFO fiber=#0 message="closing connection"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions