Stream: ideas

Topic: `?` for mapping Result Err values


view this post on Zulip Anthony Bullard (Dec 25 2024 at 19:27):

Relevant issue: https://github.com/roc-lang/roc/issues/7088

@Sam Mohr said:

To promote Roc developers providing context for Results when propagating errors with try, we want to add a new operator ? that acts as a Result.mapErr equivalent. Some example usages:

```roc

fileContents =

try File.read! "file.txt" ? FailedToReadFile

fileContents =

try File.read! "file.txt"

    ? FailedToReadFile

{} = File.save! "file.txt" contents ? \err =>

Logging.error! "Failed to save file.txt: $(Inspect.toStr err)"



FailedToSaveFile err

```

It should be higher precedence than try such that try will propagate errors after ? is applied, but it does not need to be used with try.

We should always attempt to format onto the same line as the fallible code, but can optionally wrap to a newline with an indentation to imply continuation; this prevents code from reading like we apply ? after try.

The new ? operator should be able to handle mapping over errors using pure or effectful mappers, meaning it should be its own AST node. We can start with a pure-only implementation, since we are waiting on purity inference still.

If a pure handler is used with the new ?? "Result.withDefault" operator, we should raise a warning that the error mapping is unnecessary, since it will always be discarded by ??.

view this post on Zulip Anthony Bullard (Dec 25 2024 at 19:27):

Does this bind tighter than try? and how does this feel when we move to static dispatch with ? postfix operator?

fileContents =
    File.read!("file.txt")? ? FailedToReadFile

And Richard in a Zulip thread suggested that this should always be early return on error like try. Basically, append a mapErr with the provided Err tag and then desugar the try.

view this post on Zulip Anthony Bullard (Dec 25 2024 at 19:28):

Maybe a keyword is more appropriate? The same could be said for ?? I think.

Maybe orelse for ?? and errwith(or orthrow) for ?

I think

fileContents =
    try File.read!("file.txt") errwith FailedToWrite

or

fileContents =
    File.read!("file.txt")? errwith FailedToWrite

Read better than the above. But then with Static Dispatch do we gain anything when it would just be

fileContents =
  File.read!("file.txt").map_err(FailedToWrite)?

view this post on Zulip Sam Mohr (Dec 25 2024 at 19:28):

I think Richard's suggestion was that the spaced ? would be sufficient without the suffix ?, AKA

fileContents =
    File.read!("file.txt") ? FailedToReadFile

view this post on Zulip Sam Mohr (Dec 25 2024 at 19:29):

Which might be a little confusing, but it still follows the "? means propagate errors"

view this post on Zulip Sam Mohr (Dec 25 2024 at 19:30):

But now "right after ) means just propagate the error, space between ) and ? means map the error first"

view this post on Zulip Sam Mohr (Dec 25 2024 at 19:31):

But if we did want to go for operation(arg)? ? myErrorMapper, then the second ? should be something else, and a keyword like errwith would be a good way to do it

view this post on Zulip Anthony Bullard (Dec 25 2024 at 19:43):

I was confused by this line from the issue:

Relevant issue: https://github.com/roc-lang/roc/issues/7088

@Sam Mohr said:

It should be higher precedence than try such that try will propagate errors after ? is applied, but it does not need to be used with try.

view this post on Zulip Sam Mohr (Dec 25 2024 at 19:49):

That original description is out of date

view this post on Zulip Sam Mohr (Dec 25 2024 at 19:50):

This was before PNC was even suggested, so we thought ? wouldn't exist outside of this and try would be the only error propagator

view this post on Zulip Sam Mohr (Dec 25 2024 at 19:50):

Now that ? is definitely the long-term plan for error propagation, we should design around that

view this post on Zulip Sam Mohr (Dec 25 2024 at 19:51):

In the #ideas > static dispatch - homepage example thread, Richard said:

so foo() ? bar would desugar to

when foo() is
    Ok val -> val
    Err err -> return bar(err)

view this post on Zulip Sam Mohr (Dec 25 2024 at 19:51):

And that's what I'm referring to as the modern plan for this

view this post on Zulip Anthony Bullard (Dec 25 2024 at 19:51):

Awesome

view this post on Zulip Anthony Bullard (Dec 25 2024 at 19:52):

Thanks for the clarification

view this post on Zulip Sam Mohr (Dec 25 2024 at 19:52):

Yeah, no biggie, but that's why we prefer people come in here and validate that issues are up to date before picking them up

view this post on Zulip Sam Mohr (Dec 25 2024 at 19:53):

Because it's "tornado season" most of the year in Roc world

view this post on Zulip Sam Mohr (Dec 25 2024 at 19:53):

And stuff gets outdated often

view this post on Zulip Anthony Bullard (Dec 25 2024 at 20:17):

Yep

view this post on Zulip Anthony Bullard (Dec 25 2024 at 20:17):

I wonder how we could address this. Both in issues and in the tutorial

view this post on Zulip Anthony Bullard (Dec 25 2024 at 20:18):

I think we should have a CI job that tests all tutorial code samples

view this post on Zulip Anthony Bullard (Dec 25 2024 at 20:18):

And force people to update when they break with new syntax/semantics

view this post on Zulip Sam Mohr (Dec 25 2024 at 20:20):

The tutorial for sure, but examples in issues that are for future syntax will probably make testing that hard

view this post on Zulip Sam Mohr (Dec 25 2024 at 20:20):

If we write the tutorial with markdown, then we can add testing Roc blocks in markdown to achieve that effect

view this post on Zulip Richard Feldman (Dec 25 2024 at 20:28):

I think the pace of changes that will affect the tutorial is going to slow down a lot after 0.1

view this post on Zulip Richard Feldman (Dec 25 2024 at 20:28):

but definitely more things will change between now and then, so I think probably the best time to make an investment in tutorial infrastructure is after we land the 0.1 changes :big_smile:

view this post on Zulip Sam Mohr (Dec 25 2024 at 20:28):

For sure, but if it's possible to validate the tutorial's syntax in CI, that would be nice

view this post on Zulip Anton (Dec 27 2024 at 11:29):

The tutorial is already written in markdown :)

view this post on Zulip Sam Mohr (Dec 27 2024 at 11:32):

Oh yeah, good point! I was thinking about what I expect the future tutorial to be written as, which I expect would be a multi-page markdown tutorial, but it could be written in something else to facilitate being an interactive tool, like HTML or even Roc. It'd be good to preserve markdown for the above reasons

view this post on Zulip Anton (Dec 27 2024 at 11:32):

roc test can handle markdown files too. The last puzzle piece we need to implement is some type of (comment) syntax that allows us to hide imports for display but still use them for testing.

view this post on Zulip Sam Mohr (Dec 27 2024 at 11:33):

I was thinking of that. We want to allow single hash comments within doc examples, but we can probably use a second layer of doc comments for hidden code?

view this post on Zulip Anton (Dec 27 2024 at 11:43):

Yeah, that could work

view this post on Zulip Sam Mohr (Dec 27 2024 at 11:44):

That's exactly what Rust does: https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html#hiding-portions-of-the-example

view this post on Zulip Luke Boswell (Jan 15 2025 at 23:46):

Richard Feldman said:

so foo() ? bar would desugar to

when foo() is
    Ok val -> val
    Err err -> return bar(err)

Is this definitely our chosen direction?

Any chance we could sneak this in with the current batch of changes too?

view this post on Zulip Sam Mohr (Jan 15 2025 at 23:47):

Would it be return Err(bar(err)) or return bar(err)

view this post on Zulip Richard Feldman (Jan 15 2025 at 23:47):

the former

view this post on Zulip Sam Mohr (Jan 15 2025 at 23:47):

I guess that latter enables doing fallible operations

view this post on Zulip Richard Feldman (Jan 15 2025 at 23:48):

it's supposed to be like map_err

view this post on Zulip Sam Mohr (Jan 15 2025 at 23:48):

Can you do

foo() ? |ReadErr(data)|
    Stderr.line!("Failed to read file: ${data.to_str()}")?
    Err(my_err)

view this post on Zulip Sam Mohr (Jan 15 2025 at 23:49):

I think not

view this post on Zulip Sam Mohr (Jan 15 2025 at 23:49):

But yes, it's like map_err

view this post on Zulip Sam Mohr (Jan 15 2025 at 23:49):

So the former

view this post on Zulip Richard Feldman (Jan 15 2025 at 23:49):

the main use case is ? CustomErrorTag or ? |_| CustomErrorTag

view this post on Zulip Sam Mohr (Jan 15 2025 at 23:49):

Yep

view this post on Zulip Sam Mohr (Jan 15 2025 at 23:49):

Just realizing that it is less powerful than I thought

view this post on Zulip Sam Mohr (Jan 15 2025 at 23:49):

But that's okay

view this post on Zulip Luke Boswell (Jan 15 2025 at 23:50):

Ok got it.

when foo() is
    Ok val -> val
    Err err -> return Err(bar(err))

view this post on Zulip Sam Mohr (Jan 15 2025 at 23:50):

I say we go for it

view this post on Zulip Luke Boswell (Jan 15 2025 at 23:59):

Richard Feldman said:

the main use case is ? CustomErrorTag or ? |_| CustomErrorTag

Great, I can see lots of places we should use this in the examples

view this post on Zulip Sam Mohr (Jan 16 2025 at 00:01):

It's literally perfect for scripting

view this post on Zulip Sam Mohr (Jan 16 2025 at 00:02):

Just throw a Cmd.exec!("mv", [old, new]) ? FailedToSaveConfig on everything you want tracing on

view this post on Zulip Sam Mohr (Jan 16 2025 at 00:03):

It's the anyhow of Roc

view this post on Zulip Brendan Hansknecht (Jan 16 2025 at 01:32):

Just to verify, are we saying, we only get Cmd.exec!("mv", [old, new]) ? FailedToSaveConfig and not Cmd.exec!("mv", [old, new])?? Or is it both?

view this post on Zulip Richard Feldman (Jan 16 2025 at 01:32):

both

view this post on Zulip Brendan Hansknecht (Jan 16 2025 at 01:44):

awesome!

view this post on Zulip Niclas Ahden (Jan 17 2025 at 01:16):

Does Cmd.exec!("mv", [old, new])? ? FailedToSaveConfig early return before or after mapping the error?

view this post on Zulip Sam Mohr (Jan 17 2025 at 01:16):

You shouldn't use both

view this post on Zulip Sam Mohr (Jan 17 2025 at 01:16):

But if you did, it'd early return first (I'm pretty sure)

view this post on Zulip Niclas Ahden (Jan 17 2025 at 01:16):

Great, so it’s a syntax error?

view this post on Zulip Sam Mohr (Jan 17 2025 at 01:17):

It'd be a type error

view this post on Zulip Niclas Ahden (Jan 17 2025 at 01:17):

Superb! Thanks Sam!

view this post on Zulip Richard Feldman (Jan 17 2025 at 01:20):

yeah the precedence would be that the suffix ? happens first and then afterwards the infix ? happens

view this post on Zulip Richard Feldman (Jan 17 2025 at 01:20):

it wouldn't be a syntax error because if for whatever reason you had a Result (Result ... ...) ... you could technically actually want to do this :stuck_out_tongue:

view this post on Zulip Niclas Ahden (Jan 17 2025 at 01:23):

Oh, yes. Damn, programming is wild. It’d be cool to get a warning if you’re doing this when you probably don’t want to.

view this post on Zulip Sam Mohr (Jan 17 2025 at 01:24):

For now, the ? binop operator is just syntax sugar. If/when we eventually convert it to a proper operator, we could definitely give a better error message if you do that

view this post on Zulip Niclas Ahden (Jan 17 2025 at 01:24):

So this will be a common thing probably?

(Cmd.exec!("mv", [old, new]) ? FailedToSaveConfig)?

view this post on Zulip Sam Mohr (Jan 17 2025 at 01:27):

Nope, since ? does the early return

view this post on Zulip Niclas Ahden (Jan 17 2025 at 01:31):

Oh, sorry, I must be drunk. I read that it’s like map_err but reading the sugar again I see that it’s “map_err?:ok:

view this post on Zulip Sam Mohr (Jan 17 2025 at 01:31):

Yeppers peppers

view this post on Zulip Niclas Ahden (Jan 17 2025 at 01:36):

Again, thank you Sam, it’s awesome to see how quickly Roc is moving thanks to these discussions and contributions! It’s lovely to read and try everything out! You and everyone else here is making me excited about programming in a way I haven’t been in years!

view this post on Zulip Anton (Jan 17 2025 at 10:57):

Luke Boswell said:

Any chance we could sneak this in with the current batch of changes too?

Let's officially make this the last thing though

view this post on Zulip Sam Mohr (Jan 17 2025 at 11:00):

That's good haha

view this post on Zulip Sam Mohr (Jan 17 2025 at 11:00):

It's getting pretty crazy (really, it's been crazy for over a week)

view this post on Zulip Brendan Hansknecht (Jan 17 2025 at 20:30):

when it rains it floods!

view this post on Zulip Brendan Hansknecht (Jan 17 2025 at 20:30):

Honestly kinda great to just get so much in

view this post on Zulip Sam Mohr (Jan 17 2025 at 20:37):

Agreed

view this post on Zulip Notification Bot (Jan 17 2025 at 23:33):

9 messages were moved from this topic to #bugs > too few args by Luke Boswell.


Last updated: Jun 16 2026 at 16:19 UTC