Skuld.Effects.FxList (skuld v0.1.26)

View Source

FxList - Effectful list operations with full control effect support.

The default effectful list module with full support for resumable Yield/Suspend semantics. Uses continuation chains for natural control effect propagation.

When to Use FxList vs FxFasterList

FeatureFxListFxFasterList
Throw (error handling)✓ Works✓ Works
Yield (suspend/resume)✓ Full✗ Limited
Performance~0.2 µs/op~0.1 µs/op
MemoryHigherLower

Use FxList (this module) when:

  • You need resumable Yield/Suspend semantics
  • Each element might yield and you need to resume from that exact point
  • Natural control effect propagation is important
  • This is the recommended default

Use FxFasterList when:

  • Performance is critical
  • You only use Throw for error handling (not Yield)
  • You don't need to resume suspended computations

How It Works

FxList elaborates the list operation into a chain of Comp.bind calls:

# fx_map([1, 2, 3], f) becomes:
Comp.bind(f.(1), fn r1 ->
  Comp.bind(f.(2), fn r2 ->
    Comp.bind(f.(3), fn r3 ->
      Comp.pure([r1, r2, r3])
    end)
  end)
end)

This means control effects propagate naturally through CPS - no special pattern matching needed. When a computation yields, the continuation chain captures the full iteration context for proper resumption.

Operations

  • fx_map(enumerable, f) - Map effectful function over enumerable
  • fx_reduce(enumerable, init, f) - Reduce with effectful function
  • fx_each(enumerable, f) - Execute effectful function for each element
  • fx_filter(enumerable, pred) - Filter with effectful predicate

Example with Yield

# Process items with interruptible iteration
comp =
  FxList.fx_map([1, 2, 3], fn x ->
    Comp.bind(Yield.yield({:processing, x}), fn _ ->
      Comp.pure(x * 2)
    end)
  end)
  |> Yield.with_handler()

# Each yield can be resumed to continue through the list
{%Suspend{value: {:processing, 1}, resume: r1}, _} = Comp.run(comp)
{%Suspend{value: {:processing, 2}, resume: r2}, _} = r1.(:ok)
{%Suspend{value: {:processing, 3}, resume: r3}, _} = r2.(:ok)
{[2, 4, 6], _} = r3.(:ok)

Summary

Functions

Execute an effectful function for each element, discarding results.

Filter an enumerable with an effectful predicate.

Map an effectful function over an enumerable.

Reduce an enumerable with an effectful function.

Functions

fx_each(enumerable, f)

Execute an effectful function for each element, discarding results.

Returns a computation that produces :ok.

Example

FxList.fx_each(1..1000, fn i ->
  comp do
    n <- State.get()
    _ <- State.put(n + 1)
    return(:ok)
  end
end)
# => computation returning :ok

fx_filter(enumerable, pred)

Filter an enumerable with an effectful predicate.

Example

FxList.fx_filter([1, 2, 3, 4], fn x ->
  comp do
    threshold <- Reader.ask()
    return(x > threshold)
  end
end)
# With threshold=2 => computation returning [3, 4]

fx_map(enumerable, f)

Map an effectful function over an enumerable.

Returns a computation that produces a list of results. Each element's computation is chained via Comp.bind, so control effects propagate naturally.

Example

FxList.fx_map([1, 2, 3], fn x ->
  comp do
    count <- State.get()
    _ <- State.put(count + 1)
    return(x * 2)
  end
end)
# => computation returning [2, 4, 6]

fx_reduce(enumerable, init, f)

Reduce an enumerable with an effectful function.

Each element's computation is chained via Comp.bind.

Example

FxList.fx_reduce([1, 2, 3], 0, fn x, acc ->
  comp do
    _ <- Writer.tell("processing #{x}")
    return(acc + x)
  end
end)
# => computation returning 6