Loop Effects

Sometimes, you need to repeat an effectful function, multiple times until a particular event occurs. In Roc, you can use a recursive function to do this.

We'll demonstrate this by adding numbers read from stdin until the end of input (Ctrl-D or end of file).

For a game loop example, check out snake.

Full Code

app [main!] {
    cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br",
}

import cli.Stdin
import cli.Stdout
import cli.Arg exposing [Arg]

## recursive function that sums every number that is provided through stdin
add_number_from_stdin! : I64 => Result I64 _
add_number_from_stdin! = |sum|
    when Stdin.line!({}) is
        Ok(input) ->
            num = Str.to_i64(input) ? |_| NotNum(input)
            add_number_from_stdin!((sum + num))

        Err(EndOfFile) -> Ok(sum)
        Err(err) -> Err(NotNum(Inspect.to_str(err)))

run! : {} => Result {} _
run! = |_|
    Stdout.line!("Enter some numbers on different lines, then press Ctrl-D to sum them up.")?

    sum = add_number_from_stdin!(0)?

    Stdout.line!("Sum: ${Num.to_str(sum)}")

main! : List Arg => Result {} _
main! = |_args|
    when run!({}) is
        Ok({}) -> Ok({})
        Err(NotNum(text)) ->
            Err(Exit(1, "Error: \"${text}\" is not a valid I64 number."))

        Err(err) ->
            Err(Exit(1, "Error: ${Inspect.to_str(err)}"))

Output

Run this from the directory that has main.roc in it:

$ roc main.roc < numbers.txt 
Enter some numbers on different lines, then press Ctrl-D to sum them up.
Sum: 178