Stream: beginners

Topic: ✔ Equivalent of "finally"


view this post on Zulip Jared Cone (Sep 26 2024 at 02:19):

What would be the equivalent of "finally" for executing a task? I want to execute a task, store its result, then execute another task (cleanup that should always happen), then return the result. Here's kind of what I'm trying:

app = {}
    Tty.enableRawMode! {}
    loopTask = Task.loop app gameLoop |> Task.mapErr (\_ -> Exit 0 "Error")
    # TODO execute loopTask, cache its result, disable raw mode no matter what, then return result
    Tty.disableRawMode! {}
    result

view this post on Zulip Luke Boswell (Sep 26 2024 at 02:19):

Task.ok result

view this post on Zulip Luke Boswell (Sep 26 2024 at 02:20):

https://www.roc-lang.org/builtins/Task#ok

view this post on Zulip Jared Cone (Sep 26 2024 at 02:37):

I pass loopTask into Task.ok?

view this post on Zulip Jared Cone (Sep 26 2024 at 02:39):

to clarify, by store the result of loopTask, I mean the Result (which can be Ok or Err)

view this post on Zulip Luke Boswell (Sep 26 2024 at 02:41):

You are mapping the error using Task.mapErr so you won't get a result from Task.loop. If you want that you should use something like loopTask = Task.loop app gameLoop |> Task.result!

view this post on Zulip Luke Boswell (Sep 26 2024 at 02:47):

app [main] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }

import cli.Stdout

main =

    Stdout.line! "enableRawMode"

    result = Task.loop! {} gameLoop

    Stdout.line! "disableRawMode"

    Stdout.line "Got: $(Inspect.toStr result)"

gameLoop = \{} -> Task.ok (Done Answer)
$ roc example.roc
enableRawMode
disableRawMode
Got: Answer

view this post on Zulip Luke Boswell (Sep 26 2024 at 02:50):

There isn't any Result being used here though. Task.loop

Jared Cone said:

to clarify, by store the result of loopTask, I mean the Result (which can be Ok or Err)

What is the type of your gameLoop function?

view this post on Zulip Jared Cone (Sep 26 2024 at 02:51):

In your code snippet, if Task.loop! {} gameLoop fails, will Stdout.line! "disableRawMode" still execute? I thought it would not

view this post on Zulip Jared Cone (Sep 26 2024 at 02:53):

I think this might be what I wanted:

main : Task {} [Exit I32 Str]_
main =
    app = {}
    Tty.enableRawMode! {}
    Task.loop app gameLoop
    |> Task.attempt
        (\result ->
            Tty.disableRawMode! {}
            Task.fromResult result)
    |> Task.mapErr (\_ -> Exit 1 "Error")

gameLoop : AppState -> Task [Done {}, Step AppState] _

view this post on Zulip Luke Boswell (Sep 26 2024 at 02:59):

There's definitely some frustrating compiler bugs lurking around here. I keep seeing when I try different things

thread '<unnamed>' panicked at crates/compiler/mono/src/ir.rs:6151:56:
called `Option::unwrap()` on a `None` value

https://gist.github.com/lukewilliamboswell/fac30e97e14793c078369d6afa26aa63

view this post on Zulip Luke Boswell (Sep 26 2024 at 03:01):

Anyway, glad you have something working for now

view this post on Zulip Jared Cone (Sep 26 2024 at 03:01):

ty

view this post on Zulip Luke Boswell (Sep 26 2024 at 03:05):

I would have expected this to work. But it's hitting that error.

app [main] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }

import cli.Stdout

main =

    Stdout.line! "enableRawMode"

    gameState =
        Task.loop {} gameLoop
        |> Task.onErr! \_ ->
            Stdout.line! "got an error... cleaning up then exiting"
            Stdout.line! "disableRawMode"
            Task.err (Exit 1 "SomethingBlewUp")

    Stdout.line! "disableRawMode"

    Stdout.line! "Got: $(Inspect.toStr gameState)"

# crashes with this
#thread '<unnamed>' panicked at crates/compiler/mono/src/ir.rs:6151:56:
#called `Option::unwrap()` on a `None` value
#note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
gameLoop = \{} -> Task.err SomethingBlewUp

# works with this
#$ roc example.roc
#enableRawMode
#disableRawMode
#Got: Answer
#gameLoop = \{} -> Task.ok (Done Answer)

view this post on Zulip Luke Boswell (Sep 26 2024 at 03:06):

Looks like a lambda set specialization issue. This is what crashes

let spec_symbol_index = iter_lambda_set.next().unwrap().0;

view this post on Zulip Luke Boswell (Sep 26 2024 at 03:11):

Different error when running debug build of the compiler...

$ nix develop
$ cargo run -- example.roc
thread '<unnamed>' panicked at crates/compiler/mono/src/layout.rs:2065:17:
unspecialized lambda sets left over during resolution: LambdaSet([] + (<2127>FlexAble(*, [`Inspect.Inspect`]):`Inspect.toInspector`:2), ^<2130>), UlsOfVar(VecMap { keys: [2127], values: [VecSet { elements: [2126, 2129] }] })
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

view this post on Zulip Luke Boswell (Sep 26 2024 at 03:13):

Ok, so the issue is cause by Inspect.toStr here.. removing that and it works as I expected.

app [main] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }

import cli.Stdout

main =

    Stdout.line! "enableRawMode"

    # ignoring the game state
    _ =
        Task.loop {} gameLoop
        |> Task.onErr! \_ ->
            Stdout.line! "got an error... cleaning up then exiting"
            Stdout.line! "disableRawMode"
            Task.err (Exit 1 "exiting because we got an error running the game loop")

    Stdout.line! "disableRawMode"

    Stdout.line! "SUCCESS"

gameLoop = \{} -> Task.err SomethingBlewUp

#gameLoop = \{} -> Task.ok (Done Answer)
$ cargo run -- example.roc
    Finished dev [unoptimized + debuginfo] target(s) in 0.17s
     Running `target/debug/roc example.roc`
enableRawMode
got an error... cleaning up then exiting
disableRawMode
exiting because we got an error running the game loop

view this post on Zulip Brendan Hansknecht (Sep 26 2024 at 13:02):

As a general note, I think Task.result is closer to what you were looking for. That can be used to merge the ok and error case enabling a "finally"

view this post on Zulip Brendan Hansknecht (Sep 26 2024 at 13:16):

Stdout.line! "enableRawMode"
res = Task.loop ... |> Task.result!
Stdout.line! "disableRawMode"
Task.fromResult! res

view this post on Zulip Brendan Hansknecht (Sep 26 2024 at 13:17):

We probably could add a Task.finally to make this simpler

view this post on Zulip Richard Feldman (Sep 26 2024 at 14:14):

sure - plus, in the Purity Inference world, this entire category of problem goes away :smiley:

view this post on Zulip Brendan Hansknecht (Sep 26 2024 at 23:38):

Does it? You still have the case of wanting a finally after a group of ? on a result

view this post on Zulip Richard Feldman (Sep 26 2024 at 23:42):

I mean converting between Task and Result in general

view this post on Zulip Notification Bot (Sep 28 2024 at 02:48):

Jared Cone has marked this topic as resolved.


Last updated: Jul 06 2025 at 12:14 UTC