Stream: beginners

Topic: Efficient way of pattern matching on data with shared type


view this post on Zulip Eli Dowling (Feb 05 2024 at 07:50):

I'm wondering if there is some better way to match on these? i feel like it should be possible using an or patttern or somesuch.

LList a := [Node { data : a, next : LList a, prev : LList a }, First { data : a, next : LList a }, Last { data : a, prev : LList a }]
value = \@LList list ->
    when list is
        Node { data } -> data
        Last { data } -> data
        First { data } -> data

view this post on Zulip Luke Boswell (Feb 05 2024 at 08:06):

Looks good to me.

view this post on Zulip Folkert de Vries (Feb 05 2024 at 10:54):

you can do Node { data } | Last { data } | First { data } -> data as a single branch

view this post on Zulip Folkert de Vries (Feb 05 2024 at 10:55):

has no impact on runtime performance but you might like it better in terms of style

view this post on Zulip Eli Dowling (Feb 05 2024 at 10:56):

I tried that because I thought it should work and it actually gave me a compile error complaining about shadowing.

view this post on Zulip Eli Dowling (Feb 05 2024 at 10:56):

And yes, I wasn't looking for fast performance just a cleaner representation

view this post on Zulip Folkert de Vries (Feb 05 2024 at 11:05):

in this case Node r | Last r | First r -> r.data might work?

view this post on Zulip Folkert de Vries (Feb 05 2024 at 11:05):

shadowing is weird here, I would consider that a bug

view this post on Zulip Eli Dowling (Feb 05 2024 at 11:05):

Haha, yup tried that too

view this post on Zulip Eli Dowling (Feb 05 2024 at 11:05):

Also didn't work

view this post on Zulip Folkert de Vries (Feb 05 2024 at 11:06):

what is the error exactly?

view this post on Zulip Eli Dowling (Feb 05 2024 at 11:09):

Jumped back on my computer:
That's your first suggestion

── DUPLICATE NAME in ./LList.roc ───────────────────────────────────────────────

The data name is first defined here:

13│          Node { data }
                  ^^^^^^^^

But then it's defined a second time here:

14│          | Last { data }
                    ^^^^^^^^

Since these variables have the same name, it's easy to use the wrong
one by accident. Give one of them a new name.


── DUPLICATE NAME in ./LList.roc ───────────────────────────────────────────────

The data name is first defined here:

13│          Node { data }
                  ^^^^^^^^

But then it's defined a second time here:

15│          | First { data } -> data
                     ^^^^^^^^

Since these variables have the same name, it's easy to use the wrong
one by accident. Give one of them a new name.


── NAME NOT BOUND IN ALL PATTERNS in ./LList.roc ───────────────────────────────

data is not bound in all patterns of this when branch

13│          Node { data }
                    ^^^^

Identifiers introduced in a when branch must be bound in all patterns
of the branch. Otherwise, the program would crash when it tries to use
an identifier that wasn't bound!

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

view this post on Zulip Eli Dowling (Feb 05 2024 at 11:12):

now the other

value2 = \@LList list ->
    when list is
        Node d | Last d | First d -> d.data
── TYPE MISMATCH in ./LList.roc ────────────────────────────────────────────────

The branches of this when expression don't match the condition:

12│>      when list is
13│           Node d | Last d | First d -> d.data

This list value is a:

    [
        First {
            data : a,
            next : LList a,
        },
        Last {
            data : a,
            prev : LList a,
        },
        Node {
            data : a,
            next : LList a,
            prev : LList a,
        },
    ] as b

But the branch patterns have type:

    [
        First {
            data : a,
            next : LList a,
        },
        Last {
            data : a,
            next : LList a,
        },
        Node {
            data : a,
            next : LList a,
        },
    ]

The branches must be cases of the when condition's type!

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

view this post on Zulip Eli Dowling (Feb 05 2024 at 11:15):

I think my intuition is that because records are "open" in that you can require a subset and not care about the rest of them and unions are open in that same sense. However It seems that slightly unintuitively breaks down when matching union containing a record for which you only want the shared subset

view this post on Zulip Eli Dowling (Feb 05 2024 at 11:21):

Okay, this simpler example does work

fun = \tag ->
    when tag is
        AA t | BB t -> t.data
main =
    b = AA { data: 10, other: 20 }
    fun b

view this post on Zulip Eli Dowling (Feb 05 2024 at 11:26):

It's because opaque unions are closed, right? It all works fine until i get the opaque types involved

view this post on Zulip Eli Dowling (Feb 05 2024 at 11:28):

Okay so this works with a single case

Opaque := [AA { data : U64, other:U64 }]

fun = \tag ->
    when tag is
        AA t -> t.data
funWrap = \@Opaque tag ->
    fun tag
main =
    b = @Opaque (AA { data: 10, other:10 })
    funWrap b

view this post on Zulip Eli Dowling (Feb 05 2024 at 11:34):

This also works when i remove other from AA

Opaque := [AA { data : U64 }, BB { data : U64 }]

fun : [AA { data : U64 }, BB { data : U64 }]* -> U64
fun = \tag ->
    when tag is
        AA t | BB t -> t.data
        _ -> crash ""
funWrap = \@Opaque tag ->
    fun tag

main =
    @Opaque (AA { data: 10 }) |> funWrap

view this post on Zulip Eli Dowling (Feb 05 2024 at 11:40):

It's like we can't have both an open union and an open record at the same time.
When i try to force everything to be open with annotation things get really weird:

Opaque a b := [AA { data : U64, other : U64 } b, BB { data : U64 }a]

fun : [AA { data : U64 }*, BB { data : U64 }*]* -> U64
fun = \tag ->
    when tag is
        AA t | BB t -> t.data
        _ -> crash ""
funWrap = \@Opaque tag ->
    fun tag

main =
    @Opaque (AA { data: 10 }) |> funWrap

error:

── TYPE MISMATCH in ./LList.roc ────────────────────────────────────────────────

The branches of this when expression don't match the condition:

43│>      when tag is
44│           AA t | BB t -> t.data
45│           _ -> crash ""

This tag value is a:

    […]*

But the branch patterns have type:

    […]*

The branches must be cases of the when condition's type!

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

view this post on Zulip Brendan Hansknecht (Feb 05 2024 at 15:52):

My guess is that patterns just don't have enough flexibility when being nested.


Last updated: Jul 06 2025 at 12:14 UTC