I'm getting a type mismatch been literal records. I feel like I need to add explicit types to my literal records to force them into the right shape. How do I do that? Or is there a better way to get this code to compile?
Here is the code:
interface Layout
exposes [getNextRow, GalleryItem, LayoutItem, LayoutRow]
imports []
GalleryItem : {
}
LayoutItem : {
}
LayoutRow : {
items : List LayoutItem,
galleryWidth : Int U32,
targetRowHeight : Int U32,
currentRowItems : List GalleryItem,
width : Int U32,
removedItems : List GalleryItem,
headings : List Str
}
getNextRow : List GalleryItem, Int U32, Int U32, List GalleryItem, Int U32, List GalleryItem, List Str -> { row : LayoutRow, removedItems: List GalleryItem, remainingItems : List GalleryItem }
getNextRow = \items, galleryWidth, targetRowHeight, currentRowItems, width, removedItems, headings ->
if List.len items == 0 then {
row: {
items: [],
galleryWidth,
targetRowHeight,
currentRowItems,
width,
removedItems,
headings
},
removedItems: [],
remainingItems: []
}
else {
row: {
items: [],
galleryWidth,
targetRowHeight,
currentRowItems,
width,
removedItems,
headings
},
removedItems: [],
remainingItems: []
}
# Empty gallery returns empty layout.
expect getNextRow [] 10 20 [] 0 [] [] == {
row: {
items: [],
galleryWidth: 10,
targetRowHeight: 20,
currentRowItems: [],
width: 0,
removedItems: [],
headings: []
},
removedItems: [],
remainingItems: []
}
# No items left returns current row.
expect
currentRowItems = [{}, {}, {}]
removedItems = [{}, {}]
headings = ["a", "b"]
out = getNextRow [] 10 21 currentRowItems 12 removedItems headings
out == { # <-- This is where it says the problem is.
row: {
items: currentRowItems,
offsetY: 0,
height: 21,
currentRowItems: [],
width: 12,
headings
},
removedItems,
remainingItems: []
}
Here is the error:
── TYPE MISMATCH in Layout.roc ─────────────────────────────────────────────────
This 2nd argument to == has an unexpected type:
71│> out == {
72│> row: {
73│> items: currentRowItems,
74│> offsetY: 0,
75│> height: 21,
76│> currentRowItems: [],
77│> width: 12,
78│> headings
79│> },
80│> removedItems,
81│> remainingItems: []
82│> }
The argument is a record of type:
{ row : {
height : Num *,
offsetY : Num *,
width : Num *,
…
}, … }
But == needs its 2nd argument to be:
{ row : {
galleryWidth : Int U32,
removedItems : List {},
targetRowHeight : Int U32,
width : Int U32,
…
}, … }
Tip: Seems like a record field typo. Maybe height should be
galleryWidth?
Tip: Can more type annotations be added? Type annotations always help
me give more specific messages, and I think they could help a lot in
this case
Well the first thing I'm seeing is that height
isn't included in the return value from getNextRow, so that is a mismatch...
Same goes for offsetY
Oh yeah I'm seeing the problems. Thanks for the pointer in the right direction.
It would be cool if the Roc compiler could diff the expected and actual types side by side.
The error message is pretty much giving you the diff here. It doesn't list the full record, but only the diff:
The argument is a record of type:
{ row : {
height : Num *, # this is present but should not be
offsetY : Num *, # this is present but should not be
width : Num *,
…
}, … }
But == needs its 2nd argument to be:
{ row : {
galleryWidth : Int U32, # this is missing
removedItems : List {}, # this is missing
targetRowHeight : Int U32, # this is missing
width : Int U32,
…
}, … }
I'm not sure why it included width
(maybe the compiler chooses to include 1 overlap?), but every other record field listed in the error message is problematic. The fields that are okay are omitted as part of the ...
.
Thanks so much.
But you're right, the error message could definitely be clearer in specifying the specific issues, ie extra/missing fields, type mismatch, etc. With that said, it does typically do a pretty good job of surfacing which fields are problematic.
@Ashley Davis would you be able to post a reproduction of this error message? I have some ideas for how to improve it!
I posted one earlier in this chat, but I have a different one now as I'm trying to work through the differences in my types:
── TYPE MISMATCH in Layout.roc ─────────────────────────────────────────────────
Something is off with the then branch of this if expression:
21│ getNextRow : List GalleryItem, Int U32, Int U32, List GalleryItem, Int U32, List GalleryItem, List Str -> { row : LayoutRow, removedItems: List GalleryItem, remainingItems : List GalleryItem }
22│ getNextRow = \items, galleryWidth, targetRowHeight, currentRowItems, width, removedItems, headings ->
23│> if List.len items == 0 then {
24│> row: {
25│> items: [],
26│> galleryWidth,
27│> targetRowHeight,
28│> currentRowItems,
29│> width,
30│> removedItems,
31│> headings
32│> },
33│> removedItems: [],
34│> remainingItems: []
35│> }
This branch is a record of type:
{
remainingItems : List GalleryItem,
removedItems : List GalleryItem,
row : {
currentRowItems : List GalleryItem,
galleryWidth : Int U32,
headings : List Str,
items : List *,
removedItems : List GalleryItem,
targetRowHeight : Int U32,
width : Int U32,
},
}
But the type annotation on getNextRow says it should be:
{
remainingItems : List GalleryItem,
removedItems : List GalleryItem,
row : {
currentRowItems : List GalleryItem,
galleryWidth : Int U32,
headings : List Str,
items : List {},
targetRowHeight : Int U32,
width : Int U32,
},
}
I'm surprised it's not doing that on the second one though; do you have a repro of that one?
I've actually fixed it now.
I'm getting better anyway (thanks to help here) at reading the error messages and working out what the problem is.
I'm trying to spend 30 minutes each morning to move this project forward and learn Roc, but currently it's moving really slowly as I'm struggling a bit with basic syntax (and it doesn't help when I shoot myself in the foot either).
Ashley Davis said:
I've actually fixed it now.
great! :smiley:
if there’s any way to get back to the state where the error reproduced, that would be very helpful!
@Richard Feldman if it helps I can produce a cut down sample that produces a similar error message? I'm not sure I can get the exact same one as before though.
sure!
just the one where no diffs appear - I don't know why the …
s aren't happening in that one like they are in the original message you posted, and I'd like to investigate
Here's a cutdown example, but it might be too simple!
Let's say I made a typo smed
instead of smeg
:
interface Layout
exposes [getNextRow, GalleryItem]
imports []
getNextRow = \_ -> {
smeg: 1
}
expect getNextRow == {
smed: 2
}
When the problem is this simple it's easy to decipher the error message:
── TYPE MISMATCH in Layout.roc ─────────────────────────────────────────────────
This 2nd argument to == has an unexpected type:
9│> expect getNextRow == {
10│> smed: 2
11│> }
The argument is a record of type:
{ smed : Num * }
But == needs its 2nd argument to be:
* -> { smeg : Num * }
Here's a slight more complicated version:
interface Layout
exposes [getNextRow, GalleryItem]
imports []
getNextRow = \_ -> {
a: 1,
b: 2,
smeg: 1,
c: 3
}
expect getNextRow == {
a: 1,
b: 2,
smed: 2,
c: 3
}
In the more complicated case it's more difficult to see what you got wrong:
── TYPE MISMATCH in Layout.roc ─────────────────────────────────────────────────
This 2nd argument to == has an unexpected type:
12│> expect getNextRow == {
13│> a: 1,
14│> b: 2,
15│> smed: 2,
16│> c: 3
17│> }
The argument is a record of type:
{
a : Num *,
b : Num *,
c : Num *,
smed : Num *,
}
But == needs its 2nd argument to be:
* -> {
a : Num *,
b : Num *,
c : Num *,
smeg : Num *,
}
I could probably still recover the original error by hitting the undo key, so let me know if you need that.
Ashley Davis said:
I could probably still recover the original error by hitting the undo key, so let me know if you need that.
if you could, that would be great!
the ones you just showed are separately interesting - the * ->
at the beginning means one of them was a function and the other one wasn't, and I think we should also have a separate special-case for that :big_smile:
Ha you should see how many errors I've gone through!
I'm a complete newbie again.
This would be easy if I could undo to a specific time ;)
Ok I've been back through my undo buffer and extracted 3 sets of code + error.
1: https://gist.github.com/ashleydavis/487284e01796ac5751bfd580f181f027
2: https://gist.github.com/ashleydavis/7b130aa61e3056254bfbbf33f35aa9f8
3: https://gist.github.com/ashleydavis/eb2d39dd07e8f9c37d9bb6e24eed0ee6
At the end of each one is the error.
sweet, thank you so much! :heart_eyes:
so for the first one, I'm not sure what the best error message would be :thinking:
the current error is:
── TYPE MISMATCH in repro.roc ──────────────────────────────────────────────────
This 2nd argument to == has an unexpected type:
74│> == {
75│> row: LayoutRow {
76│> items: currentRowItems,
77│> offsetY: 0,
78│> height: 21,
79│> currentRowItems: [],
80│> width: 12,
81│> headings,
82│> },
83│> removedItems,
84│> remainingItems: [],
85│> }
The argument is a record of type:
{ row : [LayoutRow {
currentRowItems : List *,
headings : List Str,
height : Num *,
items : List {},
offsetY : Num *,
width : Num *,
}], … }
But == needs its 2nd argument to be:
{ row : {
currentRowItems : List {},
galleryWidth : Int U32,
headings : List Str,
items : List LayoutItem,
removedItems : List {},
targetRowHeight : Int U32,
width : Int U32,
}, … }
────────────────────────────────────────────────────────────────────────────────
the thing that makes this tricky is that one of them is row: { ... }
and the other is row : [LayoutRow { ... }]
but also the record types are different
so if we just show the diff as being between row : { ... }
and row : [LayoutRow { ... }]
then it won't be showing that the record types are different too
but if we show the record types, would it really make sense to diff them, considering one of them is a tag payload and the other isn't?
but if we don't diff them, it seems easy to miss the [LayoutRow ...]
tag around the record - I missed it the first few times I looked at this error :sweat_smile:
I would avoid deep diff for simplicity and have only shallow one. I think this is a good way to show the difference of the fields, as in your message: row : { ... }
vs row : [LayoutRow { ... }]
(literally use three dots). imo it's even enough to have row : ...
vs row : [ ... ]
to emphasize the highest level incompatibility. After the user fixed it, they will get the next type error and it's fine. Small clear steps are better than a big chunk of diff one should comprehend
yeah I agree
just focusing on "before you can fix anything else that might be wrong here, you have to fix this"
Last updated: Jul 06 2025 at 12:14 UTC