roc-lang / roc

A fast, friendly, functional language.
https://roc-lang.org
Universal Permissive License v1.0
3.86k stars 284 forks source link

Pattern matching on tuple (or record) of lists does not recognize exhaustiveness #6802

Open imclerran opened 2 weeks ago

imclerran commented 2 weeks ago

The following should be exhaustive, but roc says that it is not.

main : Task {} _
main =
    list1 = []
    list2 = []
    when (list1, list2) is
        ([], _) -> Task.ok {}
        (_, []) -> Task.ok {}
        ([_, ..], [_,..]) -> Task.ok {}

Error message:

This when does not cover all the possibilities:
11│>      when (list1, list2) is
12│>          ([], _) -> Task.ok {}
13│>          (_, []) -> Task.ok {}
14│>          ([_, ..], [_,..]) -> Task.ok {}

Other possibilities include:
    ( [_, ..], _ )

The only way I have found to avoid this "non exhaustiveness" message is to include a pattern at the end of my when block which matches on all cases, ie:

_ -> #...
# or:
(_, _) -> #...
imclerran commented 2 weeks ago

The following is also "not exhaustive":

when (list1, list2) is
    ([], _) -> Task.ok {}
    (_, []) -> Task.ok {}
    ([_], [_]) -> Task.ok {}
    ([_], [_, ..]) -> Task.ok {}
    ([_, ..], [_]) -> Task.ok {}
    ([_, ..], [_,..]) -> Task.ok {}
imclerran commented 2 weeks ago

Same applies for recods, the following is "not exhaustive":

when { l1: list1, l2: list2 } is
    { l1: [], l2: _ } -> Task.ok {}
    { l1: _, l2: [] } -> Task.ok {}
    { l1: [_], l2: [_] } -> Task.ok {}
    { l1: [_], l2: [_, ..] } -> Task.ok {}
    { l1: [_, ..], l2: [_] } -> Task.ok {}
    { l1: [_, ..], l2: [_, ..] } -> Task.ok {}
imclerran commented 2 weeks ago

Another problem in checking exhaustiveness with record pattern matching:

If I omit the underscore, like follows, it says that patterns are redundant that actually check different fields:

when { l1: list1, l2: list2 } is
    { l1: [] } -> Task.ok {}
    { l2: [] } -> Task.ok {}
    #...

Error message:

The 2nd pattern is redundant:

11│       when { l1: list1, l2: list2 } is
12│           { l1: [] } -> Task.ok {}
13│>          { l2: [] } -> Task.ok {}