Stream: ideas

Topic: the ? operator in Purity Inference design


view this post on Zulip Isaac Van Doren (Aug 28 2024 at 23:00):

How will that syntax work with the purity inference idea? Would it only be available in functions that return Result {} _ and the else branch would be Ok {}? It seems a little bit less desirable now than when it was proposed in the context of ! because you can't use it if your function is effectful and doesn't return a result.

view this post on Zulip Agus Zubiaga (Aug 28 2024 at 23:01):

nvm I remember Richard told me you can do this in the purity inference proposal

if File.exists file then
    File.delete file
else
   {}

view this post on Zulip Agus Zubiaga (Aug 28 2024 at 23:01):

but elseless-if would be nicer of course

view this post on Zulip Agus Zubiaga (Aug 28 2024 at 23:03):

Isaac Van Doren said:

How will that syntax work with the purity inference idea? Would it only be available in functions that return Result {} _ and the else branch would be Ok {}? It seems a little bit less desirable now than when it was proposed in the context of ! because you can't use it if your function is effectful and doesn't return a result.

I think it should give you an “ignored result” warning if it returns anything other than {}

view this post on Zulip Agus Zubiaga (Aug 28 2024 at 23:03):

That’s probably something to discuss on the other topic, though

view this post on Zulip Isaac Van Doren (Aug 28 2024 at 23:05):

It seems like you wouldn't be able to use an elseless if it at all with a function that returns a result because otherwise the compiler would have to be able to determine if the else branch should return {} or Ok {}.

view this post on Zulip Isaac Van Doren (Aug 28 2024 at 23:07):

But yes this is definitely a different topic :smile:

view this post on Zulip Agus Zubiaga (Aug 28 2024 at 23:25):

I haven’t thought it through but I think you just need to add a ? to get rid of the Result

view this post on Zulip Agus Zubiaga (Aug 28 2024 at 23:26):

and if it’s still not {} and you want to ignore it, you have to do _ = …

view this post on Zulip Isaac Van Doren (Aug 28 2024 at 23:27):

If ? is still doing Result.try like it is today, then the else branch would need to return Ok {} in that case instead of {} wouldn't it?

view this post on Zulip Sam Mohr (Aug 28 2024 at 23:29):

Yes

view this post on Zulip Sam Mohr (Aug 28 2024 at 23:29):

In the new Task world, all effectful functions return a Result

view this post on Zulip Sam Mohr (Aug 28 2024 at 23:30):

Unless we change how Tasks work back to allowing non-Result values, which is probably the right idea

view this post on Zulip Sam Mohr (Aug 28 2024 at 23:30):

We needed them to be Result-like to make ! usable (I think), but that wouldn't be necessary anymore

view this post on Zulip Richard Feldman (Aug 28 2024 at 23:31):

in #ideas > Purity Inference the design is that Task no longer exists in userspace (although the data structure behind the scenes is the same), and effectfulness is totally decoupled from error handling

view this post on Zulip Richard Feldman (Aug 28 2024 at 23:31):

so, just like in Rust, if you want to opt into error handling, you introduce Result and Ok and ? and so on

view this post on Zulip Richard Feldman (Aug 28 2024 at 23:32):

and if the effect can't fail, you don't use Result in any way

view this post on Zulip Richard Feldman (Aug 28 2024 at 23:32):

(unlike today with Task)

view this post on Zulip Sam Mohr (Aug 28 2024 at 23:33):

That will actually help with some of this Task Str * cruft we're dealing with at the moment, since it'll now be Str and we won't need to do the Task.mapErr \_ -> crash "unreachable" workaround!

view this post on Zulip Sam Mohr (Aug 28 2024 at 23:33):

Off-topic, but whatever

view this post on Zulip Isaac Van Doren (Aug 28 2024 at 23:39):

Right, I'm just trying to understand how the "I want to conditionally do an effect without an else branch" syntax will work in the purity inference world. Like this:

foo = \{} ->
    if Bool.true then
        File.delete "file.txt"

    Stdout.line "The file was deleted"

(note: this is not the previously proposed early return syntax but the conditional effect syntax :sweat_smile:)

view this post on Zulip Richard Feldman (Aug 28 2024 at 23:39):

yeah that would work the way you'd expect it to

view this post on Zulip Richard Feldman (Aug 28 2024 at 23:40):

the omitted else would be sugar for:

{} = if ... then ... else {}

view this post on Zulip Richard Feldman (Aug 28 2024 at 23:41):

so it adds else {} for you

view this post on Zulip Isaac Van Doren (Aug 28 2024 at 23:42):

So I couldn't use that syntax if File.delete returned a result instead?

view this post on Zulip Sam Mohr (Aug 28 2024 at 23:43):

You could do this, I think:

foo = \{} ->
    if Bool.true then
        File.delete? "file.txt"

    Stdout.line "The file was deleted"
    Ok {}

view this post on Zulip Sam Mohr (Aug 28 2024 at 23:43):

(assuming Stdout.line here doesn't return a Result)

view this post on Zulip Richard Feldman (Aug 28 2024 at 23:45):

yeah just like how Rust does it (in this case)

view this post on Zulip Isaac Van Doren (Aug 28 2024 at 23:51):

I just can't figure out how to desugar that to use Result.try :sweat_smile:

view this post on Zulip Sam Mohr (Aug 28 2024 at 23:55):

foo = \{} ->
    if Bool.true then
        File.delete? "file.txt"
    else
        Stdout.line "The file was deleted"
        Ok {}

If you put it in this form, you'll notice that the File.delete? is the "return" of the function, so the ? is dropped, like how the last ! is dropped

view this post on Zulip Agus Zubiaga (Aug 28 2024 at 23:57):

That’s different, though. The Stdout.line needs to run in both branches.

view this post on Zulip Agus Zubiaga (Aug 28 2024 at 23:57):

I think this what it’d desugar to:

foo = \{} ->
    Result.try
        (if Bool.true then
            File.delete "file.txt"
         else
            Ok {}
        )
        \{} ->

            Stdout.line "The file was deleted"
            Ok {}

view this post on Zulip Sam Mohr (Aug 28 2024 at 23:58):

Hmm, I guess this is why we need the else keyword, since I disagree about the desugaring

view this post on Zulip Isaac Van Doren (Aug 28 2024 at 23:59):

I think this what it’d desugar to:

Yeah that looks right to me.

Isn't it a problem that the compiler has to know that the else branch returns Ok {} instead of {}?

view this post on Zulip Sam Mohr (Aug 29 2024 at 00:00):

@Agus Zubiaga do you think my intermediate desugar above is wrong?

view this post on Zulip Isaac Van Doren (Aug 29 2024 at 00:01):

Yes because the Stdout.line only runs in one branch instead of both

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 00:01):

I think what you wrote makes sense for an early return, but we are discussing elseless-if in the context of purity inference

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 00:01):

Admittedly in the wrong Zulip topic, though :grinning:

view this post on Zulip Sam Mohr (Aug 29 2024 at 00:02):

Hmmmmm :thinking: :thinking: :thinking: :thinking:

view this post on Zulip Isaac Van Doren (Aug 29 2024 at 00:02):

Yes, if someone could move this to a different topic that would be great :smiley:

view this post on Zulip Sam Mohr (Aug 29 2024 at 00:02):

Maybe this needs to be discussed properly

view this post on Zulip Notification Bot (Aug 29 2024 at 00:04):

A message was moved here from #ideas > "early returns" via formatter by Richard Feldman.

view this post on Zulip Notification Bot (Aug 29 2024 at 00:04):

A message was moved from this topic to #ideas > the ? operator in Purity Inference design by Richard Feldman.

view this post on Zulip Richard Feldman (Aug 29 2024 at 00:05):

moved!

view this post on Zulip Isaac Van Doren (Aug 29 2024 at 00:20):

To hopefully clarify things, the question is how can both of these functions work simultaneously?

conditionalEffectThatReturnsResult : {} -> Result! {} _
conditionalEffectThatReturnsResult = \{} ->
    Stdout.line "this is printed every time"

    if condition then
        effectThatReturnsAResult? {}

    Stdout.line "This is printed if effectThatReturnsAResult returns an Ok"
    Ok {}

conditionalEffectThatReturnsUnit : {} -> {}!
conditionalEffectThatReturnsUnit = \{} ->
    Stdout.line "this is printed every time"

    if condition then
        effectThatReturnsUnit {}

    Stdout.line "This is printed every time"

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 00:23):

I think in the world of implicit tasks (no !), we can't have special if without else syntax.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 00:23):

It doesn't work anymore

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 00:23):

Cause there is no way to tell if it is valid and a task or invalid and a pure function

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 00:24):

At a minimum it is really strange/inconsistent syntax that would have different expected results base on purity

view this post on Zulip Richard Feldman (Aug 29 2024 at 00:34):

I see the problem, yeah

view this post on Zulip Richard Feldman (Aug 29 2024 at 00:34):

so this would work in Rust because ? is sugar for an early return

view this post on Zulip Richard Feldman (Aug 29 2024 at 00:34):

and therefore shortcuts to the end of the function

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:23):

thinking about this more, I think we might need to make ? work the way it does in Rust in the Purity Inference design

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:23):

consider this code that works today:

foo = \arg ->
    if arg == 0 then
        Stdout.line! "arg was zero!"
        File.writeUtf8 path "it was zero"
    else
        Task.ok {}

    File.writeUtf8! path2 "got this arg: $(arg)"
    Stdout.line! "wrote to file"
    Task.ok arg

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:24):

the reason this works is that if is a statement - that is, we aren't using it as an expression - and we insert a Task.await in between statements automatically

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:24):

in the Purity Inference world, the same would be true inside effectful functions: we'd insert an implicit "await the next effect" whenever there's a statement

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:25):

however, there's a crucial difference: in the Purity Inference world, awaiting an effect does not involve error handling

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:25):

in other words, Task.await handles both errors and effects, whereas those are separated in the Purity Inference design

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:25):

so now let's take that same example and move it to the Purity Inference world, but with ? instead of !

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:26):

foo = \arg ->
    if arg == 0 then
        Stdout.line? "arg was zero!"
        File.writeUtf8 path "it was zero"
    else
        Ok {}

    File.writeUtf8? path2 "got this arg: $(arg)"
    Stdout.line? "wrote to file"
    Ok arg

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:27):

in the previous example, the if was evaluating to Task {}, which is what statements are supposed to do

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:27):

in the Purity Inference world, statements should evaluate to {} instead of Task {}

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:27):

but this if is not evaluating to {}, it's evaluating to Result {} _

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:28):

and there's no automatic short-circuiting of that in between statements like there was with Task.await (because it did both effects and error handling at the same time)

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:28):

I think this might reveal how Rust ended up with the ? design that they did: it doesn't really work the other way

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:29):

so if we changed ? to work the way that it does in Rust, then that would mean that foo? is essentially syntax sugar for:

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

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:30):

and then an actual return keyword is needed (or at least the concept, regardless of whether it's user-facing, because an early return is exactly what ? would do) - which would be a relevant consideration for #ideas > "early returns" via formatter

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:31):

an alternative design might be to introduce a rule like:

"any if statement that includes a ? effectively gets a ? wrapped around the whole thing"

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:33):

that would be a more minor change than introducing a full concept of "early return," although it feels harder to explain

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:37):

another option, which I don't love, is to make it opt-in with if?

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:38):

I don't love that because you'd basically always want that whenever you had an if statement that used ? inside it, so adding it would essentially be a chore that could have been inferred

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:38):

but the upside would be that it would make the control flow more explicit

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:55):

I think there are interesting pros and cons to the idea of introducing an explicit return operator

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:55):

obviously it's convenient in some cases

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:56):

but it does have an innate tradeoff with control flow being less obvious; it's no longer just "nested expressions all the way down"

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:56):

although to be fair, the ! and ? operators we already have today muddy those waters

view this post on Zulip Richard Feldman (Aug 29 2024 at 13:57):

having an explicit return operator would remove a class of beginner questions, which is nice

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:18):

Whether we have a user-facing return keyword or not, I think this exactly how I would expect ? to work with the statement version of if

view this post on Zulip Richard Feldman (Aug 29 2024 at 14:21):

as in you'd expect it to do an early return, like it does in Rust?

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:23):

Yeah, I know it’s different from what you’d expect in a expression if, but I think it makes sense for the statement version

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:28):

Well, would it always be an early return from the the current function? What if it's in a value def?

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:29):

like

bar = \arg ->
    foo =
        if arg == 0 then
            Stdout.line? "arg was zero!"
            File.writeUtf8 path "it was zero"
        else
            Ok {}

        File.writeUtf8? path2 "got this arg: $(arg)"
        Stdout.line? "wrote to file"
        Ok arg

    Result.withDefault "something went wrong in foo" foo

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:30):

In this case, we don't want an Err in foo to return bar because bar doesn't return a Result at all

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:32):

I feel like that should work, but I haven't thought about the desugaring yet

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:44):

Richard Feldman said:

an alternative design might be to introduce a rule like:

"any if statement that includes a ? effectively gets a ? wrapped around the whole thing"

I guess what I am describing is what you meant with this

view this post on Zulip Sam Mohr (Aug 29 2024 at 14:45):

I think this is how people expect ? to work anyway

view this post on Zulip Sam Mohr (Aug 29 2024 at 14:46):

And the automatic ? propagation I think acts like early return, right?

view this post on Zulip Sam Mohr (Aug 29 2024 at 14:46):

So maybe since ! is going away, we don't need to preserve the Result.try impl of ?

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:47):

I think it shouldn't work exactly like early return, as in "return from the current function"

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:47):

It's more like "return from the nearest def"

view this post on Zulip Sam Mohr (Aug 29 2024 at 14:48):

I'm not sure if an early return operator/keyword is a good idea, but at least this kind of needs to be an early return

view this post on Zulip Richard Feldman (Aug 29 2024 at 14:55):

Agus Zubiaga said:

Richard Feldman said:

an alternative design might be to introduce a rule like:

"any if statement that includes a ? effectively gets a ? wrapped around the whole thing"

I guess what I am describing is what you meant with this

should it work the same way for if expressions or just if statements?

view this post on Zulip Richard Feldman (Aug 29 2024 at 14:56):

    answer =
        if arg == 0 then
            Stdout.line? "arg was zero!"
            File.readUtf8 path
        else
            Ok ""

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:56):

That already returns a Result, doesn't it? The ? desugaring can happen inside the if

view this post on Zulip Richard Feldman (Aug 29 2024 at 14:57):

yeah the question is whether (for consistency with if statements) answer should be a Result or if it should have an implicit ? too

view this post on Zulip Richard Feldman (Aug 29 2024 at 14:57):

meaning it would be a Str and would short-circuit

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:57):

Ah, I see what you mean

view this post on Zulip Richard Feldman (Aug 29 2024 at 14:57):

I think it's more intuitive if only statements short-circuit

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:57):

well, if it's implicit then you cannot catch it from the outside, right?

view this post on Zulip Richard Feldman (Aug 29 2024 at 14:58):

even if it's less consistent

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:58):

yeah, I think so too

view this post on Zulip Richard Feldman (Aug 29 2024 at 14:58):

because what else could it possibly mean haha

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 14:58):

it gives you more control if they don't

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 15:00):

foo = \arg ->
    answerResult =
        if arg == 0 then
            Stdout.line? "arg was zero!"
            File.readUtf8 path
        else
            Ok ""

     # I can recover from the Err here
     when answerResult ...

     # and if I just want it to propagate, all I have to do is
     answer = answerResult?
     ...

view this post on Zulip Sam Mohr (Aug 29 2024 at 15:02):

That's pretty inconvenient IMO, but yes, it gives more control if you don't want an early return

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 15:02):

No, I'm saying you could do both

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 15:03):

If it’s a statement if (not assigned to a def), it’s essentially wrapped in a ?

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 15:05):

If it’s an expression if (assigned to def/field or passed to function) then it remains a Result

view this post on Zulip Richard Feldman (Aug 29 2024 at 15:05):

(the if statement gets wrapped in a ? if and only if at least one of its branches contains a ?)

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 15:06):

Right, because you might just run an effectful function that doesn’t return Result

view this post on Zulip Sam Mohr (Aug 29 2024 at 15:07):

Works for me! It allows us to use ? in blocks as "early returns to the top of blocks".

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 15:08):

An additional requirement could be that a statement if must return {} after unwrapped

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 15:08):

To prevent people from ignoring results

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 15:09):

If they want to ignore it, they can do:

_ = ignoredThing
{}

view this post on Zulip Richard Feldman (Aug 29 2024 at 15:09):

yeah Rust does that too

view this post on Zulip Richard Feldman (Aug 29 2024 at 15:09):

which I think is correct - means you can't accidentally swallow errors

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 15:14):

Yeah, errors or meaningful values from function calls

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 15:16):

Statement if could be nice for dbg too:

foo = \x ->
    if x > 10 then dbg x
    

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 15:18):

Or multiple expect too

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 15:18):

If it’s only one there’s no point because you can just put it in the condition

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 15:54):

I'm not quite sure the agreement this landing on, but I definitely don't think ? should default to an early return. It should still be local enough to slow capturing the result to a variable.

Frankly, without ! it is kinda weird we have multiple statement bodies at all.

Like it is a strange dichotomy in the language that some functions make valid statements but others don't. Imagine I make a set of tasks with no errors:

main = \{} ->
    task1 {}
    task2 {}
    if task4 ... then
        task5 ...
    else
        {}
    task3 ....

:point_up: working with impure functions but failing with pure functions feels really strange. Especially given there is no syntax difference between the two.

view this post on Zulip Richard Feldman (Aug 29 2024 at 15:57):

Brendan Hansknecht said:

I'm not quite sure the agreement this landing on, but I definitely don't think ? should default to an early return. It should still be local enough to slow capturing the result to a variable.

yeah that's kinda where we ended up

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 15:57):

Ok, just making sure :smiley:

view this post on Zulip Richard Feldman (Aug 29 2024 at 15:57):

example:

foo = \arg ->
    answerResult = # this is a Result
        if arg == 0 then
            Stdout.line? "arg was zero!"
            File.readUtf8 path
        else
            Ok ""

     # I can recover from the Err here
     when answerResult ...

     # and if I just want it to propagate, all I have to do is
     answer = answerResult?
     ...

view this post on Zulip Richard Feldman (Aug 29 2024 at 15:59):

the change would be to statement-style if, like so:

foo = \arg ->
    if arg == 0 then
        Stdout.line? "arg was zero!"
        File.writeUtf8 path
    else
        Ok {}

    ...

view this post on Zulip Richard Feldman (Aug 29 2024 at 15:59):

this would become equivalent to:

foo = \arg ->
    {} = (
        if arg == 0 then
            Stdout.line? "arg was zero!"
            File.writeUtf8 path
        else
            Ok {}
    )?

    ...

view this post on Zulip Richard Feldman (Aug 29 2024 at 16:00):

in other words, using ? inside an if statement (not an expression) would add a ? around the entire if

view this post on Zulip Richard Feldman (Aug 29 2024 at 16:00):

(behind the scenes)

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 16:00):

the answerResult stuff is confusing in this example

view this post on Zulip Richard Feldman (Aug 29 2024 at 16:00):

because there's really nothing else you could possibly want it to do :sweat_smile:

view this post on Zulip Richard Feldman (Aug 29 2024 at 16:00):

oops yeah, I just edited that out so it's not confusing haha

view this post on Zulip Sam Mohr (Aug 29 2024 at 16:02):

It's confusing if you assume you get Rust rules IMO. If you assume early return, it's not that. If you engage with it as Roc rules, it makes sense

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 16:03):

I just meant that the statement-if example was confusing because it included answerResult which would not be available (he since removed it)

view this post on Zulip Agus Zubiaga (Aug 29 2024 at 16:03):

I don't think the syntax itself is confusing

view this post on Zulip Sam Mohr (Aug 29 2024 at 16:03):

Okay, good

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 16:05):

:point_up: working with impure functions but failing with pure functions feels really strange. Especially given there is no syntax difference between the two.

Also, I think we can ignore this comment above now that I think about it more.

The rule is anything that returns {} can just be a statement.
If it returns a Result {} ... it can just be used alone with ?
Otherwise, a value needs to be explicitly ignored with _ = ...

That would work with both pure and impure functions and make sense.

view this post on Zulip Richard Feldman (Aug 29 2024 at 16:07):

I like that this design preserves the locality of how ? works today :smiley:

view this post on Zulip Sky Rose (Sep 01 2024 at 12:49):

I know I'm a bit late and still piecing everything together, but will it be easy to predict what happens when reading the code?

Richard Feldman said:

in other words, using ? inside an if statement (not an expression) would add a ? around the entire if

How do you tell whether it's a statement if or an expression if? Does it require knowing the types of the branches to tell how the control flow will work?

What if the example was this instead? I removed a line. The branches are the same type (Result {} _) but now there's no ? inside. Does that change the behavior of the if at all?

foo = \arg ->
    if arg == 0 then
        File.writeUtf8 path
    else
        Ok {}

    ...

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

That does change things

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

Actually I'm pretty sure the first example was accidentally invalid

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

It should have been this for the example:

foo = \arg ->
    if arg == 0 then
        Stdout.line? "arg was zero!"
        File.writeUtf8? path
    else
        Ok? {}

    ...

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

Cause all of the results need to have a ? to propagate. And they are required to propagate due to the ... containing extra code afterwards

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

Without the ... the final ?s in each branch could be dropped.


Last updated: Jun 16 2026 at 16:19 UTC