Stream: bugs

Topic: incompatible, but identical, types in list


view this post on Zulip Lukas Juhrich (Apr 12 2026 at 14:30):

Hi,

On main(a1a0be36), I'm trying to add type hints to a fairly abstract data type I have typed up ad-hoc in the top level:

app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/alpha-0/HVZonS7HStbvGwhqvDZE1HreyDfqD6tayzFbi9rAUXYN.tar.zst" }

import pf.Stdin
import pf.Stdout
import pf.Stderr

print! = Stdout.line!
main! = |_args| {
    print!("datalog!")
    print!(Str.inspect(program))
    Ok({})
}

# TODO add parser, for now let's hard-code this in source
example =
    \\ goal :- E(x1, x2), C(x1)
    \\ C(x2) :- E(x1, x2), B(x1)
    \\ B(x2) :- E(x1, x2), A(x1)
    \\ A(x2) :- E(x1, x2), B(x1), A(x1)
    \\ A(x1) :- E(x1, x1)

list = [A({}), B({})]

# arc monadic datalog rule
AmdRuleBody(idb, edb, var_) : {
    edb : (edb, List(var_)),
    idbs : List((idb, var_)),
}

AmdRule(idb, edb, var_) : [
    GoalRule(AmdRuleBody(idb, edb, var_)),
    IdbRule((idb, var_), AmdRuleBody(idb, edb, var_)),
]


program: {rules: List(AmdRule([A, B, C, ..], [E, ..], [X1, X2, ..]))}
program = {
    rules: [
        {
            edb = (E, [X1, X2])
            idbs = [(C, X1)]
            r : AmdRule([A, B, C, ..], [E, ..], [X1, X2, ..])
            r = GoalRule({ edb: edb, idbs: idbs })
            r
        },
        {
            r : AmdRule([A, B, C, ..], [E, ..], [X1, X2, ..])
            r = IdbRule((C, X2), { edb: (E, [X1, X2]), idbs: [(B, X1)] })
            r
        },
        IdbRule((B, X2), { edb: (E, [X1, X2]), idbs: [(A, X1)] }),
        IdbRule((A, X2), { edb: (E, [X1, X2]), idbs: [(B, X1), (A, X1)] }),
        IdbRule((A, X1), { edb: (E, [X1, X1]), idbs: [(B, X1), (A, X1)] }),
    ],
}

it roc checks correctly, but when I execute it as roc datalog.roc, I get a type mismatch and a comptime crash (which vanishes if I move program into main):

❯ roc datalog.roc
-- TYPE MISMATCH ---------------------------------

The first two elements in this list have incompatible types:
   ┌─ datalog.roc:46:3
   │
46 │            {
47 │                    r : AmdRule([A, B, C, ..], [E, ..], [X1, X2, ..])
48 │                    r = IdbRule((C, X2), { edb: (E, [X1, X2]), idbs: [(B, X1)] })
49 │                    r
50 │            },

The first element has this type:

    AmdRule([A, B, C, ..], [E, ..], [X1, X2, ..])

However, the second element has this type:

    AmdRule([A, B, C, ..], [E, ..], [X1, X2, ..])

All elements in a list must have compatible types.
Note: You can wrap each element in a tag to make them compatible.
To learn about tags, see <https://www.roc-lang.org/tutorial#tags>
-- COMPTIME CRASH --------------------------------

This definition crashed during compile-time evaluation:
   ┌─ datalog.roc:37:11
   │
37 │ program = {
38 │    rules: [
39 │            {
40 │                    edb = (E, [X1, X2])
41 │                    idbs = [(C, X1)]
42 │                    r : AmdRule([A, B, C, ..], [E, ..], [X1, X2, ..])
43 │                    r = GoalRule({ edb: edb, idbs: idbs })
44 │                    r
45 │            },
46 │            {
47 │                    r : AmdRule([A, B, C, ..], [E, ..], [X1, X2, ..])
48 │                    r = IdbRule((C, X2), { edb: (E, [X1, X2]), idbs: [(B, X1)] })
49 │                    r
50 │            },
51 │            IdbRule((B, X2), { edb: (E, [X1, X2]), idbs: [(A, X1)] }),
52 │            IdbRule((A, X2), { edb: (E, [X1, X2]), idbs: [(B, X1), (A, X1)] }),
53 │            IdbRule((A, X1), { edb: (E, [X1, X1]), idbs: [(B, X1), (A, X1)] }),
54 │    ],
55 │ }

The crash happened with this message:

    e_tag: expected tag_union but got rt=flex:n/a ct=err has_expected=true for tag `IdbRule`

Found 2 error(s) and 0 warning(s) for datalog.roc.

I added type hints for the first two expressions to debug my (previously incorrect) annotations.
what's going on here? is this a bug in the compiler, interpreter, both, or None? Or is the type check message just rendered incorrectly?

view this post on Zulip Lukas Juhrich (Apr 12 2026 at 14:35):

Oh, this must be related to the { r: type hint; r = blah; r } pattern I used to debug where my type hint goes wrong in detail. if I just say

program : { rules : List(AmdRule([A, B, C, ..], [E, ..], [X1, X2, ..])) }
program = {
    rules: [
        GoalRule({ edb: (E, [X1, X2]), idbs: [(C, X1)] }),
        IdbRule((C, X2), { edb: (E, [X1, X2]), idbs: [(B, X1)] }),
        IdbRule((B, X2), { edb: (E, [X1, X2]), idbs: [(A, X1)] }),
        IdbRule((A, X2), { edb: (E, [X1, X2]), idbs: [(B, X1), (A, X1)] }),
        IdbRule((A, X1), { edb: (E, [X1, X1]), idbs: [(B, X1), (A, X1)] }),
    ],
}

then everything works.

view this post on Zulip Jared Ramirez (Apr 12 2026 at 18:08):

i think that it has to do with the ... these desugar to ..a, ..b, etc. i think when you annotated vars in the body, those get assigned new type variables compared to the top level def, causing the mismatch.

if you change the top level .. to be ..my_var, then us that same variable my_var in the same position in the body def’s annotation, it think it should work

view this post on Zulip Lukas Juhrich (Apr 12 2026 at 18:21):

Oh, okay, that's a bit surprising to me. If the name of the remainder matters, theb my assumption would have been that [A, B, ..] just means the rest is „unspecified“, and if a term types as both [A, B, ..] and [A, B, ..rest], then we deduce [A, B, ..rest] instead of the bottom type; but if it types as [A, B, ..rest] and [A, B, ..tser] then it would deduce bottom and throw a type error.

But the crash is a bug, right?

view this post on Zulip Lukas Juhrich (Apr 12 2026 at 18:23):

Just removing the type hints but leaving the assign-and-return expressions would still cause failure, but I don't recall if it was a crash, type failure, or both.


Last updated: May 01 2026 at 12:45 UTC