Stream: ideas

Topic: alternatives to `try` for errors


view this post on Zulip Niclas Ahden (Sep 17 2024 at 15:40):

I'm out of the loop regarding the status of ? in Roc so please take this for what it is and excuse my ignorance.

My context is Ruby (so ! as effectful makes sense), Haskell, Elm, and Rust (so ? as error handling makes sense). If I want to perform an effect that may error, and I want to early-return said error, I'd expect to write fireZeMissiles!? (meaning (fireZeMissiles!)?). If I want to map the error, I'd expect to write fireZeMissiles!? MissileMalfunction.

Perhaps that's not feasible, but it's what I naturally just thought "yeah, that's how it probably works".

# My understanding of the proposal:

userParam
    |> try Str.toU64 ? ParamParseError
    |> try getUser! ? UserLookupError

# vs. what I thought it'd be:

userParam
    |> Str.toU64? ParamParseError
    |> getUser!? UserLookupError

Without mapping the first error:

userParam
    |> try Str.toU64
    |> try getUser! ? UserLookupError

# vs. what I thought it'd be:

userParam
    |> Str.toU64?
    |> getUser!? UserLookupError

Effectful error mapping:

userParam
    |> try Str.toU64 ? ParamParseError
    |> try getUser! ? \e -> logAndReport! UserLookupError e

# vs. what I thought it'd be:

userParam
    |> Str.toU64? ParamParseError
    |> getUser!? \e -> logAndReport! UserLookupError e

Passing in the piped arg as context:

userParam
    |> try Str.toU64 ? ParamParseError userParam
    |> try \id -> getUser! id ? UserLookupError id

# vs. what I thought it'd be:

userParam
    |> Str.toU64? ParamParseError userParam
    |> \id -> (getUser! id)? UserLookupError id
  1. I appreciate that this is extremely terse and therefore easy for my brain to parse (fewer keywords and chars are usually easier for me, as long as there aren't more operators than I can keep in my head (Haskell)).

  2. I saw !? mentioned as an operator, but in my suggestion above ! and ? are separate and the fact that they may appear next to each other is just a coincidence. When explaining them to someone else I wouldn't intermingle them at all.

  3. To me it's visually easier to grasp that |> getUser!? pipes to getUser!, than that |> try getUser! pipes to getUser!. Probably because with try I am piping "past" something.

  4. When promoting Roc to others I find that error handling is one of the topics that gets the most interest. It's a high-pain area while also being easy to explain. Many people like Rust's ? and I'd like to say "Yes, Roc has ?. It's got the best of Rust _and_ it removes the verbosity of defining error structs for every little variation, because you can just _make errors up_ and the compiler will ensure you're dealing with them." Now, try is essentially ? from Rust, but wouldn't it be nice to be able to sell those Rustaceans on familiarity by having exactly ??

This may be completely infeasible syntax-wise, or totally irrelevant as I've missed previous discussions. If so, sorry! Hopefully it's helpful as another data point for "this is what my brain would expect" as that usually helps with design.

Bonsoir!

view this post on Zulip Brendan Hansknecht (Sep 17 2024 at 16:05):

Yeah, !? has been discussed a lot with various options. For example in #ideas>what if we leaned into the ridiculousness of `!?`

This is an alternative that is explicitly avoiding !?.

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:06):

PS. I've been writing a lot of Rust lately and coming over to Roc for a while, and seeing this proposal, is such a breath of fresh air. Thanks for working on this! Roc is the language I have the most fun writing and it's just _incredibly great_.

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:10):

@Brendan Hansknecht I guess the only novelty in my suggestion would be the error mapping then? foo!? FooError doesn't seem to have been discussed? Although Richard did have a strong reaction against !? so perhaps it's doomed :joy:

view this post on Zulip Brendan Hansknecht (Sep 17 2024 at 16:11):

Personally, I think that would be confusing cause FooError looks to be an arg to foo. It looks like you are passing an Enum of value FooError to foo.

view this post on Zulip Brendan Hansknecht (Sep 17 2024 at 16:14):

Specifically Str.toU64? ParamParseError userParam. That looks like 2 args passed to Str.toU64. Relatedly, if you saw Str.toU64? userParam is userParam a function to map to error or is it an arg for Str.toU64

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:14):

Fair point! try foo! ? FooError avoids that completely at the cost of verbosity. I, personally, would prefer foo!? FooError just to save myself some typing/mental parsing, but the longer version is probably easier to teach :)

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:16):

If I know that ? is early-return (like Rust) and that , on top of that, a function cannot contain ? in its name, then it'd be abundantly clear to me that Str.toU64? userParam is error handling. But this is optimizing for someone who knows the language, rather than someone who is learning, for sure.

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

Note, I don't think try is specifically required for your idea. Just the postfix ?

So it would be Str.toU64 userParam ? ParamParseError and Str.toU64 userParam ?

And also foo! ? FooError or foo! someArg ? FooError

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:19):

Indeed! I just cut the space to make it 1:1 with Rust

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:25):

I'm willing to sacrifice a space for the greater good. If people find foo! ? FooError and foo! ? more palatable, then I'm all for it! :)

view this post on Zulip Brendan Hansknecht (Sep 17 2024 at 16:27):

Oh, I recall one of the issues with only using ?:

observedFileData =
     try Tracing.span "get-file-data" \{} =>
         File.read! "some-file.txt" ? FileReadErr

would become:

observedFileData =
    Tracing.span "get-file-data" \{} =>
        File.read! "some-file.txt" ? FileReadErr
    ?

# maybe would require parens for clarity:
observedFileData =
    (Tracing.span "get-file-data" \{} =>
        File.read! "some-file.txt" ? FileReadErr
    )?

Which is pretty strange looking

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:28):

Don't do this to me Brendan, we were so close...

view this post on Zulip Brendan Hansknecht (Sep 17 2024 at 16:29):

haha, yeah, that is what I think all of the time. Apparently mixing postfix operators with space based function calling leads to messes pretty easily.

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:30):

And now I'll go full-circle to ?> I guess :joy:

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:30):

It is indeed nice to have the "try" mechanism at the start of the line

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:30):

Brendan Hansknecht said:

Should it be changed to double ? In the type? ??

I suggested this in the doc as an alternative, I'm down but people weren't as big on the look compared to alternatives/the current syntax.

Also, ?? means "extract or default" at the moment, so ?? also meaning "type of defaultable field" would be close but different

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:31):

I'd be okay with ?> if we always tried to keep it on the same line

view this post on Zulip Brendan Hansknecht (Sep 17 2024 at 16:32):

yeah, but it is a lot closer than ? which will mean "map the error type of a result".

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:32):

Sometimes it should go to a different line, but it should be preferred to be a one liner

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:33):

Wait, I thought the suggestion was to use ?> for mapErr

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:33):

Is that wrong?

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:33):

I was thinking ?> Str.toU64 [SOME OPERATOR] ParamParseError

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:33):

So ?> would be early-return

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:33):

Oh nvm

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:33):

Yeah, not ideal

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:34):

Is there some discussion about that?

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:34):

The > implies piping in Roc

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:36):

Yeah, in my mind it's "we're happily piping with |> and then if there's danger we use ?>". So if I look at a long pipe I'll see the places where it can early-return right away:

|> eat
|> breathe
?> drinkMaybePoison ? ImminentDeathError
|> rest
?> runWithScissors ? CatastrophicBleedingError

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:37):

So ?> means piping, but can you also use it for a single expression?

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:38):

Cause then it kinda means two things

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:38):

You win again, Batman

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:39):

Seriously, idk

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:39):

Hey man, I've been in this cave thinking about this for like 3 weeks

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:39):

There is no silver bullet, I think. Every option is just less bad than another

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:39):

Can you give an example where try is used for a single expression? for inspiration and research purposes only

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:40):

fileContents = try File.read! "file.txt" ? FileReadErr

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:41):

fileContents ?= File.read! "file.txt" ? FileReadErr
fileContents <? File.read! "file.txt" ? FileReadErr

no

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:42):

<? reads nicely, but it's weird

view this post on Zulip Sam Mohr (Sep 17 2024 at 16:42):

Yeah, I really don't mean to dissuade your attempts at creativity! It's just a hard problem, so I'm giving terse answers to questions I'm frustrated have no good solution

view this post on Zulip Niclas Ahden (Sep 17 2024 at 16:42):

No problem at all mate, I'm having fun

view this post on Zulip Notification Bot (Sep 17 2024 at 16:42):

41 messages were moved here from #ideas > try-else error context adding syntax by Brendan Hansknecht.


Last updated: Jun 16 2026 at 16:19 UTC