Stream: ideas

Topic: ✔ early return statement


view this post on Zulip Richard Feldman (Sep 07 2024 at 21:03):

(splitting this off from #ideas > opting out of short-circuiting)

an idea I've thought about in the past, which would certainly make it easier to understand how these things work:

view this post on Zulip Richard Feldman (Sep 07 2024 at 21:04):

I think I remember Rust planning to introduce something similar for their early return ? implementation

view this post on Zulip Richard Feldman (Sep 07 2024 at 21:04):

and there has been interest in early return independent of this topic

view this post on Zulip Richard Feldman (Sep 07 2024 at 21:18):

on the one hand, I do appreciate the simplicity of control flow that results from having no return, but on the other hand:

view this post on Zulip Richard Feldman (Sep 07 2024 at 21:21):

also, by coincidence the last time someone directly asked for return in Roc (this was not on Zulip) was less than a week ago :big_smile:

view this post on Zulip Richard Feldman (Sep 07 2024 at 21:54):

Richard Feldman said:

an idea I've thought about in the past, which would certainly make it easier to understand how these things work:

here's specifically how that idea would desugar, assuming we have a return statement:

foo? bar

desugars to:

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

view this post on Zulip Richard Feldman (Sep 07 2024 at 21:55):

and then this:

try fn arg

desugars to:

(\{} -> fn arg) {}

view this post on Zulip Richard Feldman (Sep 07 2024 at 21:57):

so if you replaced any of fn arg with expressions that used ? or ! or return, they would early return to the try because it wrapped them in a function

view this post on Zulip Richard Feldman (Sep 07 2024 at 21:57):

compared to our current desugaring rules for ? and ! this is astronomically more straightforward :sweat_smile:

view this post on Zulip Richard Feldman (Sep 07 2024 at 21:58):

which certainly appeals to me!

view this post on Zulip Richard Feldman (Sep 07 2024 at 22:00):

and I know this discussion started because of #ideas > Purity inference proposal v3 but I think the issues about the way short-circuiting works being nonobvious is a separate and reasonable thing to consider today, even setting aside that proposal

view this post on Zulip Richard Feldman (Sep 07 2024 at 22:12):

this example would work differently in this design; it would need a try to work:

Sky Rose said:

Would this work?

main : Str => U8
main = \path =>
    result =
        str = File.readUtf8! path ?> FileReadError
        num = Str.fromU32? str ?> ParseError
        File.writeUtf8! path (Str.toU32 num) ?> FileWriteError

    when result is
        Ok _ -> 0
        Err (FileReadError _) -> 1
        Err (ParseError _) -> 2
        Err (FileWriteError _) -> 3

view this post on Zulip Richard Feldman (Sep 07 2024 at 22:13):

so it would need to be this:

main : Str => U8
main = \path =>
    result = try
        str = File.readUtf8! path ?> FileReadError
        num = Str.fromU32? str ?> ParseError
        File.writeUtf8! path (Str.toU32 num) ?> FileWriteError

    when result is
        Ok _ -> 0
        Err (FileReadError _) -> 1
        Err (ParseError _) -> 2
        Err (FileWriteError _) -> 3

view this post on Zulip Sam Mohr (Sep 07 2024 at 22:59):

https://doc.rust-lang.org/stable/unstable-book/language-features/try-blocks.html

view this post on Zulip Sam Mohr (Sep 07 2024 at 22:59):

This is the feature I think you're thinking of

view this post on Zulip Sam Mohr (Sep 07 2024 at 23:02):

I'm personally Rust biased, but having ? propagate to the function boundary for "someone else to handle" is really what I want most of the time. I'd rather that be the default than propagate to the def/expr level

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 00:02):

Personally after using roc for a while, I really don't want early return

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 00:03):

I think it strongly goes against some nice guarantees in roc

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 00:03):

It is nice to know that things don't magically escape wrapping expressions.

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 00:03):

You lose that with being able to stick a return in any random corner

view this post on Zulip Sam Mohr (Sep 08 2024 at 00:04):

Do you not like all early returns, or would you be okay with ? doing early return but not otherwise?

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 00:04):

I do not think ? Should do it either

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 00:05):

Having one expression tree without jumping around is super nice for understanding code and recognizing guarantees easily.

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 00:06):

I know a deeply nested conditional statement will assign to the outer variables. It won't magically escape and return. That is really useful knowledge for debugging

view this post on Zulip Sam Mohr (Sep 08 2024 at 00:06):

You seem to be an "explicit over implicit" person, I presume you'd rather repeat yourself in having to use ? rather than it do it automatically, even in Rust?

view this post on Zulip Sam Mohr (Sep 08 2024 at 00:07):

(if you could change how Rust works)

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 00:07):

Nope, but that is cause rust has a very different mental model from roc overall

view this post on Zulip Sam Mohr (Sep 08 2024 at 00:07):

Okay, imperative code vs expression trees

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 00:07):

Roc is expressions all the way down. They are all self contained. It is great.

Rust is imperative and mutable and powerful in a different way

view this post on Zulip Sam Mohr (Sep 08 2024 at 00:09):

Until we add shadowing, you can still reorder all (pure) defs with ? in them and guarantee you'll get the same result

view this post on Zulip Sam Mohr (Sep 08 2024 at 00:09):

Not quite true for effectful code, but pretty close

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 00:09):

I dont think this is about reordering

view this post on Zulip Sam Mohr (Sep 08 2024 at 00:09):

It's not

view this post on Zulip Sam Mohr (Sep 08 2024 at 00:10):

I'm getting at a concept of atomicity

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 00:22):

I feel like I have grown to get a lot of comfort out of certain features in roc.

An expression tree with clearly contained logic is one piece. Another piece is explicit effects that are separated from pure logic.

view this post on Zulip Richard Feldman (Sep 08 2024 at 01:38):

Brendan Hansknecht said:

Having one expression tree without jumping around is super nice for understanding code and recognizing guarantees easily.
I know a deeply nested conditional statement will assign to the outer variables. It won't magically escape and return. That is really useful knowledge for debugging

I think we may have already lost this with our current ! and ? operators :sweat_smile:

view this post on Zulip Sam Mohr (Sep 08 2024 at 01:38):

Our current operators, yes

view this post on Zulip Richard Feldman (Sep 08 2024 at 01:39):

in #ideas > opting out of short-circuiting there's a discussion about whether we could have a rule set for ! and ? (just focusing on ? since it doesn't have any effects baggage) that restores this, and it's a big challenge haha

view this post on Zulip Sam Mohr (Sep 08 2024 at 01:39):

The other discussion in "opting out of short-circuiting" seems to be getting what Brendan and I think Agus want here

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 02:25):

Richard Feldman said:

I think we may have already lost this with our current ! and ? operators :sweat_smile:

Not really. Like yes, a minor bit, but only for a specific clear subset that is not deeply nested. If I see a nested expression under a variable I know what can or can't escape very easily

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

So I think this is quite different

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 02:28):

If I see:

someVar =
    # any number of infinite things
    # if when, idc all fine
    n + 1

It will never escape me in current roc. I know that someVar will be manifest and code after it will run.

view this post on Zulip Brendan Hansknecht (Sep 08 2024 at 02:28):

So yes, but current ! and ? barely effect this

view this post on Zulip Anton (Sep 09 2024 at 09:01):

It's hard to predict all the consequences of this proposal

view this post on Zulip Anton (Sep 09 2024 at 13:05):

make ? be syntax sugar for "early return if error" (like Rust does) and similar with !

I do really value that what's behind the sugar would be a lot simpler

view this post on Zulip Richard Feldman (Sep 09 2024 at 17:27):

Anton said:

It's hard to predict all the consequences of this proposal

yeah I think this is maybe in the same bucket as effect polymorphism: don't introduce a new thing because there are enough new things already in this proposal, and we can always revisit later

view this post on Zulip Simon Taeter (Feb 26 2025 at 15:46):

If I may put my two cents as a complete beginner:
I find the ?> symbol more explicit as it relates to the pipe operator compared to the ?? that is used in the tutorial.

view this post on Zulip Kiryl Dziamura (Feb 26 2025 at 15:59):

I think it's time to resolve this thread? The language changed a lot since then.
@Simon Taeter you would probably like to check #announcements channel to familiarize yourself with the changes in condensed format

view this post on Zulip Notification Bot (Feb 26 2025 at 16:01):

Anton has marked this topic as resolved.


Last updated: Jun 16 2026 at 16:19 UTC