Skuld.SerializableCoroutine (skuld v0.28.0)

View Source

Helpers for building coroutines with serializable effect logs.

new/2 constructs a struct with EffectLogger installed innermost, so every effect invocation across all handlers is captured in a JSON-serializable log.

When the coroutine suspends, the log is accessible via get_log/1 (from the env in %Coroutine.ExternalSuspended{}). serialize/1 and deserialize/1 convert the log to/from JSON.

run handles all states — fresh start, live resume, and cold resume from serialised state.

Usage

sc = SerializableCoroutine.new(wizard, fn comp ->
  comp |> State.with_handler(0) |> Yield.with_handler() |> Throw.with_handler()
end)

# Run fresh — suspends at first yield
suspended = SerializableCoroutine.run(sc)

# Serialize and persist
json = SerializableCoroutine.serialize(SerializableCoroutine.get_log(suspended))

# Later: cold resume from serialised state
SerializableCoroutine.run(json, sc, "Alice")

# Or resume a live suspended coroutine directly
SerializableCoroutine.run(suspended, "Alice")

Summary

Types

t()

A serialisable coroutine capturing the computation and handler stack.

Functions

Deserialize a log from JSON.

Extract the EffectLogger log from a suspended coroutine.

Build a serialisable coroutine.

Run a serialisable coroutine.

Serialize a log to JSON.

Types

t()

@type t() :: %Skuld.SerializableCoroutine{
  comp: Skuld.Comp.Types.computation(),
  handlers_fun: (Skuld.Comp.Types.computation() ->
                   Skuld.Comp.Types.computation())
}

A serialisable coroutine capturing the computation and handler stack.

Functions

deserialize(json)

@spec deserialize(String.t()) ::
  {:ok, Skuld.Effects.EffectLogger.Log.t()} | {:error, Jason.DecodeError.t()}

Deserialize a log from JSON.

Returns {:ok, log} on success.

get_log(external_suspended)

Extract the EffectLogger log from a suspended coroutine.

The log is stored in the environment at suspension time. Returns nil if no log is found.

new(comp, handlers_fun)

Build a serialisable coroutine.

handlers_fun receives the computation after EffectLogger is installed. Install application-level handlers here (State, Throw, Yield, etc.).

Example

SerializableCoroutine.new(my_comp, fn comp ->
  comp |> State.with_handler(0) |> Throw.with_handler()
end)

run(serializable_coroutine)

@spec run(t()) :: Skuld.Coroutine.t()

Run a serialisable coroutine.

Clauses dispatch on the input type:

  • t() — start fresh, returns a Coroutine sum-type
  • t(), value — resume a live suspended fiber (delegates to Coroutine.run)
  • %Log{}, t(), value — cold resume from a deserialised log
  • binary, t(), value — deserialise JSON to a log, then cold resume
  • Coroutine.t(), value — resume a live Coroutine fiber directly

Examples

sc = SerializableCoroutine.new(wizard, handlers)

# Start fresh
suspended = SerializableCoroutine.run(sc)

# Resume a live suspended fiber
SerializableCoroutine.run(suspended, "Alice")

# Cold resume from a deserialised log
SerializableCoroutine.run(log, sc, "Alice")

# Cold resume from serialised JSON
SerializableCoroutine.run(json, sc, "Alice")

run(fiber, value)

run(log, sc, value)

@spec run(Skuld.Effects.EffectLogger.Log.t(), t(), term()) :: Skuld.Coroutine.t()
@spec run(String.t(), t(), term()) :: Skuld.Coroutine.t()

serialize(log)

@spec serialize(Skuld.Effects.EffectLogger.Log.t()) :: String.t()

Serialize a log to JSON.

Returns a JSON string suitable for storage.