Skuld.Effects.FxList (skuld v0.1.26)
View SourceFxList - 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
| Feature | FxList | FxFasterList |
|---|---|---|
| Throw (error handling) | ✓ Works | ✓ Works |
| Yield (suspend/resume) | ✓ Full | ✗ Limited |
| Performance | ~0.2 µs/op | ~0.1 µs/op |
| Memory | Higher | Lower |
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 enumerablefx_reduce(enumerable, init, f)- Reduce with effectful functionfx_each(enumerable, f)- Execute effectful function for each elementfx_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
@spec fx_each(Enumerable.t(), (term() -> Skuld.Comp.Types.computation())) :: Skuld.Comp.Types.computation()
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
@spec fx_filter(Enumerable.t(), (term() -> Skuld.Comp.Types.computation())) :: Skuld.Comp.Types.computation()
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]
@spec fx_map(Enumerable.t(), (term() -> Skuld.Comp.Types.computation())) :: Skuld.Comp.Types.computation()
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]
@spec fx_reduce(Enumerable.t(), term(), (term(), term() -> Skuld.Comp.Types.computation())) :: Skuld.Comp.Types.computation()
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