Stream: beginners

Topic: Adding redundant catch-all branch gets a compile error


view this post on Zulip Sylvain Brunerie (Oct 27 2024 at 09:49):

Hi! This one has me really confused. I have a compiling piece of code with a when … is:

loop = \oldState ->
    Stdout.line! "Checking git status…"
    gitStatusOutput =
        gitStatus!
            |> outputToStr
    newState =
        getTimeSinceLastCommit!
            |> (\timeSinceLastCommit -> stateFromGitStatus gitStatusOutput timeSinceLastCommit)

    when (oldState, newState) is
        (DirtySince _, Clean) ->
            Stdout.line! "You are back into a clean state."
            sleepOk newState

        (DirtySince before, DirtySince now) if now - before > thresholdInSeconds ->
            minutes = Num.toFrac thresholdInSeconds / 60 |> Num.round
            Stdout.line! "You have been in a dirty state since more than $(if minutes > 1 then "minutes" else "minute")."
            sleepOk newState

        (DirtySince before, DirtySince now) ->
            Stdout.line! "delta is supposed to be inferior or equal to threshold"
            Stdout.line! (Inspect.toStr before)
            Stdout.line! (Inspect.toStr now)
            sleepOk newState

        (Clean, DirtySince _) ->
            # Just started changing files, all good
            Stdout.line! ""
            sleepOk newState

        (Clean, Clean) ->
            # Still clean, all good
            Stdout.line! ""
            sleepOk newState

        _ ->
            Stdout.line! (stateToStr newState)
            sleepOk newState

But the catch-all branch at the end is useless because redundant: Clean and DirtySince U128 are the only two possible states here. Yet when I try to remove it, I get this error:

This 2nd argument to loop has an unexpected type:

32│      Task.loop Initial loop
                           ^^^^

This loop value is a:

    [
        Clean,
        DirtySince (Int Unsigned128),
    ] -> Task [Step State, …] […]

But loop needs its 2nd argument to be:

    [Initial]* -> Task [Step [Initial], …] […]

Tip: Seems like a tag typo. Maybe DirtySince should be Initial?

I really don’t get it, I don’t see the difference in the last couple branches in terms of type. Also I’m pretty sure of the exhaustiveness because if I remove another branch, I get an additional error about the unsafe pattern matching.

view this post on Zulip Sam Mohr (Oct 27 2024 at 13:17):

So the first tip I'd always recommend in these situations is to add type annotations where you have type issues like this

view this post on Zulip Sam Mohr (Oct 27 2024 at 13:18):

Second off, the type of Task.loop is:

loop : state, (state -> Task [Step state, Done done] err) -> Task done err
loop = \state, step ->
    looper = \current ->
        (@Task next) = step current
        when next {} is
            Err e -> Err e
            Ok (Done newResult) -> Ok newResult
            Ok (Step newState) -> looper (newState)

    @Task \{} -> looper state

view this post on Zulip Sam Mohr (Oct 27 2024 at 13:19):

So two things I can think of:

  1. The type of the state that you're passing is [Initial]*, not [Clean, DirtySince I128]
  2. You're not returning Step newState, you're just returning newState

view this post on Zulip Sam Mohr (Oct 27 2024 at 13:21):

I can't confirm either without more code to look at

view this post on Zulip Sam Mohr (Oct 27 2024 at 13:22):

In the error message you're given, it returns Task [Step State, ...] [...]. What is State?

view this post on Zulip Brendan Hansknecht (Oct 27 2024 at 15:12):

I think this is a bug we have with when ... is and tuples. I have seen it come up in the context of lists and tuples before

view this post on Zulip Brendan Hansknecht (Oct 27 2024 at 15:13):

That said, hard to guarantee without seeing types

view this post on Zulip Dan G Knutson (Oct 27 2024 at 16:19):

A thing I just noticed locally matching on a tuple is that if I accidentally add one redundant case to an already-exhaustive match, then rather than getting an error on the 'extra' case, I get the 'other possibilities include (_, _)' style error

view this post on Zulip Sylvain Brunerie (Oct 28 2024 at 07:45):

Ok it probably has to do with the types at least, I had this type alias State, which is an alias to [Clean, DirtySince U128] but did not include Initial.

view this post on Zulip Sylvain Brunerie (Oct 28 2024 at 08:44):

So I’ve added Initial to State, and now I need more branches, so I can’t quite yet get the exact same potential bug that I had before. Sorry about not being so reactive, I’ll follow up on this later, thanks all for you input!

view this post on Zulip Sam Mohr (Oct 28 2024 at 12:50):

I will say, it's not always great to hear "it'll get better so just wait a bit", but the purity inference PR is looking pretty good, so once we transition to that a lot of the Task-related typing woes can go away by using effectful functions instead of Tasks

view this post on Zulip Sam Mohr (Oct 28 2024 at 12:51):

That should happen sometime in the next couple weeks

view this post on Zulip Sylvain Brunerie (Oct 28 2024 at 16:38):

Right! I fell asleep yesterday while watching the video, I need to watch the whole thing :sweat_smile:


Last updated: Jul 26 2025 at 12:14 UTC