Module

Hylograph.Simulation.Emitter

Package
purescript-hylograph-simulation
Repository
afcondon/purescript-hylograph-simulation

Framework-Agnostic Event Emitter

Provides a universal subscription mechanism that any UI framework can consume. The core simulation library emits events through this interface, and thin framework adapters convert it to Halogen subscriptions, React hooks, etc.

Design

The emitter is based on a simple callback pattern:

  • subscribe takes a callback and returns an unsubscribe function
  • This is the minimal interface that all frameworks can work with

Framework Integration

Halogen: Convert to HS.Emitter using toHalogenEmitter from Hylograph.ForceEngine.Halogen:

import Hylograph.ForceEngine.Halogen (toHalogenEmitter)

handleAction Initialize = do
  { handle, events } <- liftEffect $ runSimulation config
  halogenEmitter <- liftEffect $ toHalogenEmitter events
  void $ H.subscribe $ halogenEmitter <#> SimEvent

React: Use in useEffect with cleanup. The unsubscribe function returned by subscribe works perfectly as a React cleanup function:

// In a React component:
useEffect(() => {
  const { handle, events } = runSimulation(config)();

  // Subscribe returns an unsubscribe function
  const unsubscribe = subscribe(events)(event => {
    if (event.tag === 'Tick') {
      setAlpha(event.alpha);
    } else if (event.tag === 'Completed') {
      console.log('Simulation converged!');
    }
  })();

  // Cleanup: unsubscribe when component unmounts
  return () => unsubscribe();
}, []);

Vanilla JS: Just call subscribe directly:

const { handle, events } = runSimulation(config)();
const unsubscribe = subscribe(events)(event => {
  console.log('Event:', event);
})();

#SimulationEmitter Source

newtype SimulationEmitter

Framework-agnostic event emitter

This is intentionally opaque - use subscribe to listen for events. The internal representation is a list of listeners with unique IDs.

#SimulationEvent Source

data SimulationEvent

Event types emitted by simulations

These are the same regardless of whether D3 or WASM is running the physics.

Constructors

Instances

#Unsubscribe Source

type Unsubscribe = Effect Unit

Unsubscribe function returned by subscribe

#create Source

create :: Effect { emitter :: SimulationEmitter, handle :: EmitterHandle }

Create a new emitter

Returns both the emitter (for subscribers) and a handle (for the simulation to emit events). This separation ensures only the simulation can emit.

#EmitterHandle Source

newtype EmitterHandle

Handle for emitting events (kept by the simulation, not exposed to users)

#subscribe Source

subscribe :: SimulationEmitter -> (SimulationEvent -> Effect Unit) -> Effect Unsubscribe

Subscribe to events

Returns an unsubscribe function that removes the listener.

unsubscribe <- subscribe emitter \event -> case event of
  Tick { alpha } -> log $ "Alpha: " <> show alpha
  Completed -> log "Simulation converged!"
  _ -> pure unit

-- Later, to stop listening:
unsubscribe

#emit Source

emit :: EmitterHandle -> SimulationEvent -> Effect Unit

Emit an event to all subscribers (internal use only)

This is called by the simulation implementation, not by users.