Stream: beginners

Topic: Unexpected type mismatch with parameterised function


view this post on Zulip Jonathan (Mar 04 2026 at 00:18):

I have a function insert, that has the type

    insert : RBTree(k, v), k, v -> RBTree(k, v) where [
        k.is_eq: k, k -> Bool,
        k.is_lt: k, k -> Bool,
        k.is_gt: k, k -> Bool
    ]

And a function empty, that has the type

    empty : () -> RBTree(k, v) where [
        k.is_eq: k, k -> Bool,
        k.is_lt: k, k -> Bool,
        k.is_gt: k, k -> Bool
    ]

I'm not sure why this snippet

test_insert = || {
    set : RBTree(I32,Str)
    set = RBTree.empty()

    set2: RBTree(I32, Str)
    set2 = set.insert(0.I32, "")
    True
}

receives the following error

-- TYPE MISMATCH ---------------------------------

The insert method on RBTree has an incompatible type:
    ┌─ /Users/jrp2018/Fun/roc/RBTree.roc:209:12
    │
209 │     set2 = set.insert(0.I32, "")
    │            ^^^

The method insert has the type:

    RBTree(k, v), k, v -> RBTree(k, v)
      where [k.is_eq : k, k -> Bool
         , k.is_gt : k, k -> Bool
         , k.is_lt : k, k -> Bool]

But I need it to have the type:

    RBTree(I32, Str), I32, Str -> RBTree(I32, Str)

As far as I can see, the concrete types match the pattern and the constraints?

view this post on Zulip Jonathan (Mar 04 2026 at 11:33):

Hmm. I tried to produce a minimal example with exactly the same type restrictions, and the compiler had no issue with the below. I would have thought that manually specifying the function types would have isolated the problem to be at the call site

main! = |_args| {
    x : MyType(I32, Str)
    x = MyType.empty()

    x2 : MyType(I32, Str)
    x2 = x.insert(0.I32, "")
    Ok({})
}

MyType(a, b) :: [A].{
    insert : MyType(a, b), a, b -> MyType(a, b)
      where [a.is_eq : a, a -> Bool
         , a.is_gt : a, a -> Bool
         , a.is_lt : a, a -> Bool]
    insert = |cl, x, y| cl

    empty : () -> MyType(a, b) where [
        a.is_eq: a, a -> Bool,
        a.is_lt: a, a -> Bool,
        a.is_gt: a, a -> Bool
    ]
    empty = || {
        A
    }
}

view this post on Zulip Anton (Mar 04 2026 at 11:42):

Can you share the full RBTree code?

view this post on Zulip Jonathan (Mar 04 2026 at 11:48):

Thanks, here you go :) https://gist.github.com/jrrrp/ae6fe1e91a1932593e13f790451a8471

view this post on Zulip Jonathan (Mar 04 2026 at 11:50):

But to add to the story. If I remove that test at the bottom that is getting the type mismatch error, and write the exact same thing in main.roc, I don't get any errors. It only seemed to be when it was in the same file. I am running roc test RBTree.roc --no-cache for one and just roc --no-cache to run the same in main

view this post on Zulip Jonathan (Mar 04 2026 at 11:52):

Though when I write a very simple 'MyType' in its own file, mirroring with the same function signatures as RBTree, with the 'same' test in the bottom, that doesn't have an error.

view this post on Zulip Anton (Mar 04 2026 at 11:54):

I get thread 231107 panic: reached unreachable code with your original RBTree.roc and the latest Roc from main branch. I will take a look...

view this post on Zulip Jonathan (Mar 04 2026 at 11:57):

If it does type check for you, I was getting a malloc heap corruption during roc build that I think I tracked down to the spread operator (line 83 works vs 93 doesn't) but couldn't reproduce outside of that context. (This was before I wrote the test at the bottom, which I suppose triggers the error earlier in the compiler?)

view this post on Zulip Jonathan (Mar 04 2026 at 11:58):

Anton said:

I get thread 231107 panic: reached unreachable code with your original RBTree.roc and the latest Roc from main branch. I will take a look...

I'm on a couple days old roc so will give it a try

view this post on Zulip Anton (Mar 04 2026 at 11:58):

For me the panic triggers during canonicalization

view this post on Zulip Jonathan (Mar 04 2026 at 12:02):

I don't get that same error with the newest roc nightly. Still the same type mismatch. Is RBTree defined in main.roc for you or RBTree.roc?

view this post on Zulip Jonathan (Mar 04 2026 at 12:02):

(I also had this error before, but figured that it was due to the type mismatch:

-- COMPTIME EVAL ERROR ---------------------------

This definition could not be evaluated at compile time:
    ┌─ /Users/jrp2018/Fun/roc/RBTree.roc:208:19
    │
208 │     x2 = x.insert(0.I32, "")
    │                   ^^^^^

The evaluation failed with error:

    Numeric literal cannot be used as this type (type doesn't support from_numeral)

)

view this post on Zulip Anton (Mar 04 2026 at 12:02):

I am doing ./zig-out/bin/roc check RBTree.roc --no-cache

view this post on Zulip Jonathan (Mar 04 2026 at 12:04):

That gives the same error as before, but I am using nightlies (macos-apple silicon). I can try from source and see

view this post on Zulip Jonathan (Mar 04 2026 at 12:22):

Building from source I get the same error as you now (interestingly I couldn't build with the downloaded zig-aarch64-macos-0.15.2 but nix -> buildcmd worked fine)

view this post on Zulip Jonathan (Mar 04 2026 at 13:10):

I think the unreachable case (trace said it was from canonicalizeExpr for the arguments inside parseMethodCall?) is triggered by this apply but I couldn't reduce it further from here.

 b = a.apply(|new_right| RBTree.Node({colour: before.colour, key: before.key, value: before.value, right: new_right, left: before.left}))

view this post on Zulip Jonathan (Mar 04 2026 at 14:08):

Furthermore, this gives the same crash and stack trace
b = a.apply(|x| before) (before is a tree node introduced earlier)

but this doesn't, and it proceeds to the type mismatch error: b = a.apply(|x| x). I have no idea if this is useful at this point ^^

view this post on Zulip Anton (Mar 04 2026 at 14:13):

I have a different minimal repro, I will keep you posted on the fix.

view this post on Zulip Anton (Mar 04 2026 at 15:00):

PR#9236 fixes one unreachable panic. I will take a look at the remaining errors now.

view this post on Zulip Jonathan (Mar 04 2026 at 15:45):

Thank you Anton!

view this post on Zulip Anton (Mar 04 2026 at 16:45):

I'm now looking at #9237

view this post on Zulip Jonathan (Mar 04 2026 at 19:29):

#9238

The compiler serializes recursive self-references (e.g., Tree inside Node(Tree)) as .err content to break cycles

I suppose this is why sometimes the errors are less informative? I've had cases where type checker expects X but got "Error". I don't have a snippet right now but I think it was with a recursive type.

view this post on Zulip Anton (Mar 06 2026 at 09:41):

Uhu, could be

view this post on Zulip Jonathan (Mar 06 2026 at 11:51):

Actually wait a sec (Thought I had made a mistake with order of fold_rev args but nope)

Anton I'm not sure if one of the PRs you had on the go was targeting this, but if it helps I hit a very similar but more simple problem without any extra crashes when testing some of the missing list builtins, and it seems to be sensitive to the content of the function not just the signature :thinking: (repro below). For now I can just not put expects in the bottom and all is ok :)

ListFuns :: [].{

    # No problem
    noop : a -> a
    noop = |it| it

    # No problem
    noopList : List(a) -> List(a)
    noopList = |it| it

    # No problem (e.g. something that isn't a noop)
    doubleList : List(a) -> List(a)
    doubleList = |it| it.concat(it)

    # Problem (type mismatch: I32 != a), but only when tested in this file
    reverse : List(a) -> List(a)
    reverse = |list| {
        destination = List.with_capacity(list.len())
        list.fold_rev(destination, |item, dest| dest.append(item))
    }
}

# ===========
# Running expects inside ListFuns.roc
#
# expect ListFuns.noop(2) == 2            # All good
# expect ListFuns.noopList([2]) == [2]    # All good
# expect ListFuns.doubleList([1]) == [1, 1] # All good
# expect ListFuns.reverse([2]) == [2]     # Type mismatch - expected 'a'.
#                                         # Also missing method, comptime eval
#                                         # error etc.
#
# ===========
# Import into main.roc, and `roc test main.roc`
#
# expect ListFuns.noop(2) == 2            # All good
# expect ListFuns.noopList([2]) == [2]    # All good
# expect ListFuns.doubleList([1]) == [1, 1] # All good
# expect ListFuns.reverse([2]) == [2]     # All good

view this post on Zulip Anton (Mar 06 2026 at 12:26):

Thanks @Jonathan, that's a useful repo :)
I hit a panic: integer overflow when running ./zig-out/bin/roc test main.roc --no-cache. I am going to start by looking at that first.

view this post on Zulip Anton (Mar 06 2026 at 14:36):

Fixed in PR#9241

view this post on Zulip Steve Howell (Mar 06 2026 at 14:40):

Just to be completely nit-picky here (and to help my drive-by understanding of zig), shouldn't the test here say that it was an "underflow" issue?

view this post on Zulip Anton (Mar 06 2026 at 15:04):

Yeah, zig panics with integer overflow when there is an underflow :p I think I am going to leave it as is, it seems the least confusing.

view this post on Zulip Anton (Mar 06 2026 at 17:15):

I'm looking at the actual compile errors from the ListFuns file now

view this post on Zulip Jonathan (Mar 06 2026 at 17:19):

I'd like to try and help, but I can't figure out why all my traces are stripped despite being a debug build :sweat_smile:

view this post on Zulip Anton (Mar 06 2026 at 17:29):

Are you using a headerless main app like here: https://github.com/roc-lang/roc/blob/main/test/echo/hello.roc

view this post on Zulip Jonathan (Mar 06 2026 at 17:31):

No I was using Luke's simple-cli

view this post on Zulip Jonathan (Mar 06 2026 at 17:32):

I'll check what happens without

view this post on Zulip Jonathan (Mar 06 2026 at 17:35):

Same issue there. ListFuns when tested/called in main -> no problem. Tested in the same module -> type mismatches

view this post on Zulip Anton (Mar 06 2026 at 17:37):

Oh yes, that is expected, but I meant the headerless main app helps with the traces.

view this post on Zulip Jonathan (Mar 06 2026 at 17:37):

Ahh! Thank you haha. Yeh I was trying to atos my way back but it wasn't looking right at all.

view this post on Zulip Anton (Mar 06 2026 at 18:40):

Fix in PR#9243


Last updated: Mar 20 2026 at 12:28 UTC