Stream: beginners

Topic: Type mismatch: Do I need explicit typing here?


view this post on Zulip Ashley Davis (Jul 08 2024 at 21:31):

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

view this post on Zulip Ian McLerran (Jul 08 2024 at 21:46):

Well the first thing I'm seeing is that height isn't included in the return value from getNextRow, so that is a mismatch...

view this post on Zulip Ian McLerran (Jul 08 2024 at 21:48):

Same goes for offsetY

view this post on Zulip Ashley Davis (Jul 08 2024 at 21:50):

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.

view this post on Zulip Ian McLerran (Jul 08 2024 at 21:55):

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

view this post on Zulip Ashley Davis (Jul 08 2024 at 21:56):

Thanks so much.

view this post on Zulip Ian McLerran (Jul 08 2024 at 22:06):

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.

view this post on Zulip Richard Feldman (Jul 08 2024 at 22:08):

@Ashley Davis would you be able to post a reproduction of this error message? I have some ideas for how to improve it!

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:12):

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,
        },
    }

view this post on Zulip Richard Feldman (Jul 08 2024 at 22:18):

I'm surprised it's not doing that on the second one though; do you have a repro of that one?

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:18):

I've actually fixed it now.

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:22):

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

view this post on Zulip Richard Feldman (Jul 08 2024 at 22:23):

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!

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:28):

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

view this post on Zulip Richard Feldman (Jul 08 2024 at 22:28):

sure!

view this post on Zulip Richard Feldman (Jul 08 2024 at 22:29):

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

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:34):

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 *,
    }

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:36):

I could probably still recover the original error by hitting the undo key, so let me know if you need that.

view this post on Zulip Richard Feldman (Jul 08 2024 at 22:38):

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!

view this post on Zulip Richard Feldman (Jul 08 2024 at 22:38):

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:

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:39):

Ha you should see how many errors I've gone through!

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:39):

I'm a complete newbie again.

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:40):

This would be easy if I could undo to a specific time ;)

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:45):

Ok I've been back through my undo buffer and extracted 3 sets of code + error.

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:46):

1: https://gist.github.com/ashleydavis/487284e01796ac5751bfd580f181f027

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:46):

2: https://gist.github.com/ashleydavis/7b130aa61e3056254bfbbf33f35aa9f8

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:47):

3: https://gist.github.com/ashleydavis/eb2d39dd07e8f9c37d9bb6e24eed0ee6

view this post on Zulip Ashley Davis (Jul 08 2024 at 22:47):

At the end of each one is the error.

view this post on Zulip Richard Feldman (Jul 08 2024 at 22:49):

sweet, thank you so much! :heart_eyes:

view this post on Zulip Richard Feldman (Jul 10 2024 at 11:45):

so for the first one, I'm not sure what the best error message would be :thinking:

view this post on Zulip Richard Feldman (Jul 10 2024 at 11:45):

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,
    }, … }

────────────────────────────────────────────────────────────────────────────────

view this post on Zulip Richard Feldman (Jul 10 2024 at 11:46):

the thing that makes this tricky is that one of them is row: { ... } and the other is row : [LayoutRow { ... }]

view this post on Zulip Richard Feldman (Jul 10 2024 at 11:46):

but also the record types are different

view this post on Zulip Richard Feldman (Jul 10 2024 at 11:47):

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

view this post on Zulip Richard Feldman (Jul 10 2024 at 11:48):

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?

view this post on Zulip Richard Feldman (Jul 10 2024 at 11:49):

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:

view this post on Zulip Kiryl Dziamura (Jul 10 2024 at 12:38):

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

view this post on Zulip Richard Feldman (Jul 10 2024 at 16:02):

yeah I agree

view this post on Zulip Richard Feldman (Jul 10 2024 at 16:03):

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