So far I haven’t had any issues with this, but I wanted to understand how this works in Roc.
Let’s say I am writing an interactive terminal program (e.g. a REPL or a game). In order to capture and maintain some state, I need to pass a function to the platform API to handle inputs, and then I need that function to call the same platform API, passing a call to itself inside a closure with the updated state. At least this is my understanding. Please correct me if I’m wrong.
Given this fact, should I be worried about the function call stack growing indefinitely and potentially crashing the program? Is there some kind of optimization for that? Or is there another way to handle persistent state without writing to a file/database?
Can you be more concrete in the specifications here? I don't understand why a function needs to be passed to the platform here.
If you are writing a REPL, you would write out your prompt, read the input, append that to whatever state, and then recurse passing that state as an arg
like this (psuedo-code):
main = |_| run_repl([])
run_repl = |state|
Stdout.write!("> ")
line = Stdin.read!()
# do something
run_repl(List.append(state, line))
Obviously you would want to have a condition based on the line that would be your base case, something like if line == ":exit" return 0 else start_repl(List.append(state, line))
Oh, if it is something like the above, no worries at all. Tail calls are optimized to loop without increasing the stack size.
Possibly helpful example of a terminal app: https://github.com/roc-lang/basic-cli/blob/main/examples/terminal-app-snake.roc
In order to capture and maintain some state
See also GameState
here
Last updated: Jul 05 2025 at 12:14 UTC