tiramisu/effect

Effect system for managing side effects in Tiramisu.

Effects represent side effects as immutable data, following The Elm Architecture. Your update function returns effects that the runtime executes for you.

Quick Example

import tiramisu/effect

type Msg {
  Tick
  PlayerMoved(Vec3(Float))
}

fn update(model: Model, msg: Msg, ctx: Context) {
  case msg {
    Tick -> #(
      update_physics(model),
      effect.batch([
        effect.tick(Tick),  // Request next frame
        effect.from(fn(dispatch) {
          // Custom side effect
          log_position(model.player_pos)
          dispatch(PlayerMoved(model.player_pos))
        }),
      ]),
    )
    PlayerMoved(_) -> #(model, effect.none())
  }
}

Types

Opaque effect type that can dispatch messages back to the application.

Effects are data descriptions of side effects to perform. The runtime executes them after your update function returns.

pub opaque type Effect(msg)

Values

pub fn batch(effects: List(Effect(msg))) -> Effect(msg)

Batch multiple effects to run them together.

All effects execute in order during the same frame.

Example

effect.batch([
  effect.tick(NextFrame),
  play_sound_effect("jump.wav"),
  update_scoreboard(score),
])
pub fn from(effect: fn(fn(msg) -> Nil) -> Nil) -> Effect(msg)

Create a custom effect from a function.

The function receives a dispatch callback to send messages back to your update function.

Example

effect.from(fn(dispatch) {
  log("Player score: " <> int.to_string(score))
  dispatch(ScoreLogged)
})
pub fn from_promise(p: promise.Promise(msg)) -> Effect(msg)

Create an effect from a JavaScript Promise.

When the promise resolves, it dispatches the resulting message.

Example

let fetch_promise = fetch_data()
effect.from_promise(promise.map(fetch_promise, DataLoaded))
pub fn map(effect: Effect(a), f: fn(a) -> b) -> Effect(b)

Map effect messages to a different type.

Useful when composing effects from subcomponents.

Example

let player_effect = player.update(player_model, player_msg)
effect.map(player_effect, PlayerMsg)
pub fn none() -> Effect(msg)

Create an effect that performs no side effects.

Use when you want to update state without triggering any effects.

Example

fn update(model, msg, ctx) {
  case msg {
    Idle -> #(model, effect.none())
  }
}
pub fn tick(msg: msg) -> Effect(msg)

Request the next animation frame and dispatch a message.

This is the primary way to create frame-based game loops. Call this in your update function to receive a message on the next frame.

Example

type Msg {
  Tick
}

fn update(model, msg, ctx) {
  case msg {
    Tick -> #(
      Model(..model, time: model.time +. ctx.delta_time),
      effect.tick(Tick),  // Request next frame
    )
  }
}
Search Document