I think what @gezepi recommended is fundamentally the only way to do it usefully.
The problem you have with state is layout convergence, see this post for details: Why is State Final not "final"? In that thread I suggest a solution that, applied to your situation, would look roughly like this:
#let rng = state("rng", (gen-rng(1), none))
#let randomize(options) = {
rng.update(((rng, _)) => shuffle(rng, options))
context rng.get().last()
}
The rng
state now contains the actual rng and the most recent result. The update is not done through a get…update cascade, so that’s good!
However, this function doesn’t give you an array; it gives you content: Why is the value I receive from context always content? - #2 by laurmaedje The typical way around this is to pull the context
keyword out:
#let rng = state("rng", (gen-rng(1), none))
#let randomize(options) = {
rng.update(((rng, _)) => shuffle(rng, options))
rng.get().last()
}
...
#context randomize(options)
But in this case that also doesn’t work: randomize()
uses update()
, and an update itself is content. So unless you really only need to output the randomized array (in which case content is fine), that’s also not a full solution. If that’s all you need, @gezepi’s second post (or my first snippet) gives you an answer.
Assuming you want to use the randomized array further down, you’ll need to separate the update from the result, e.g. like this:
#let randomize(options) = {
rng.update(((rng, _)) => shuffle(rng, options))
}
#let get-randomized() = {
rng.get().last()
}
...
#randomize(options)
#context get-randomized()
… but imo then you’re basically in the same situation as at the start, where you had to handle the rng variable (instead of the state update) separately on each call.