Stream: compiler development

Topic: .. vs _


view this post on Zulip Anton (Feb 11 2026 at 17:32):

The unification of the error union is not the same here; the new Try({}, [Exit(I32), ..]) behavior differs from the old Result {} [Exit I32]_. Do we currently have a way to achieve the old unification behavior? This is similar to the situation described in #9010.

view this post on Zulip Anton (Feb 11 2026 at 18:25):

@Jared Ramirez

view this post on Zulip Jared Ramirez (Feb 12 2026 at 15:44):

can you provide a repro?

view this post on Zulip Anton (Feb 17 2026 at 11:29):

Here it is:

app [main!] { pf: platform "./platform/main.roc" }

import pf.Stdout

line : Str -> Try({}, [EmptyStrErr])
line = |str| {
    if str.is_empty() {
        Err(EmptyStrErr)
    } else {
        Ok({})
    }
}

my_func : {} -> Try({}, [Exit(I32), ..])
my_func = |_| {
    line("hey")?
    Err(Exit(1))
}

main! = || {
    match my_func({}) {
        Ok({}) => Stdout.line!("Ok")
        Err(_) => Stdout.line!("Err")
    }
}

output:

❯ ./zig-out/bin/roc test/fx/repro.roc
-- TYPE MISMATCH ---------------------------------

This ? may return early with a type that doesn't match the function body:
   ┌─ test/fx/repro.roc:16:5
   │
16 │     line("hey")?
   │     ^^^^^^^^^^^^

On error, this would return:

    Try({  }, [EmptyStrErr])

But the function body evaluates to:

    Try({  }, [Exit(I32), ..])

Hint: The error types from all ? operators and the function body must be compatible since any of them could be the actual return value.

Found 1 error(s) and 0 warning(s) for test/fx/repro.roc.
app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br" }

import cli.Stdout

line : Str -> Result {} [EmptyStrErr]
line = |str|
    if Str.is_empty(str) then
        Err(EmptyStrErr)
    else
        Ok({})

my_func : {} -> Result {} [Exit(I32)]_
my_func = |_|
    line("hey")?
    Err(Exit(1))

main! = |_args|
    when my_func({}) is
        Ok({}) -> Stdout.line!("Ok")
        Err(_) -> Stdout.line!("Err")

output:

❯ ./roc main.roc
Err

view this post on Zulip Anton (Feb 18 2026 at 09:14):

I would love to hear your (initial) thoughts on this @Jared Ramirez, this is a blocker for the new basic-cli.

view this post on Zulip Jared Ramirez (Feb 19 2026 at 01:11):

Been pretty swamped at work and I have not had the time to look into this yet. Hopefully for Friday/this weekend I should be a little more free to take a look

view this post on Zulip Jared Ramirez (Feb 19 2026 at 16:31):

i believe the issue here is in the rust compiler, this type signature:

line : Str -> Result {} [EmptyStrErr]

is silently upgraded to:

line : Str -> Result {} [EmptyStrErr]_

which makes the Err payloads mergable when you call line("hey")?.

this is called "polarity", and it's not yet implemented in the new compiler. the good new for now though is that by this can be fixed by adding a .. to the type definition of line:

line : Str -> Try({}, [EmptyStrErr, ..])

this is semantically the same as what the rust compiler is doing, so it should unblock!

view this post on Zulip Anton (Feb 20 2026 at 09:58):

Amazing, thanks @Jared Ramirez :heart:


Last updated: Feb 20 2026 at 12:27 UTC