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?
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.
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
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?
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