Stream: beginners

Topic: ✔ Confusing error messages relating to Results


view this post on Zulip Ian McLerran (Apr 21 2024 at 16:22):

Am I the only one who frequently gets confused by the error messages produced by the compiler when Results are involved? Seems like the mismatched use of Result and Ok/Err contributes to the confusion. But I've pretty much never seen an error message involving a Result that wasn't difficult to decipher. For example the below:

This when expression produces:
    Result [
        Err [InvalidTimeFormat],
        Ok Time.Time,
    ] […]

But the type annotation on parseFractionalTime says it should be:
    Result {
        hour : Int Unsigned8,
        minute : Int Unsigned8,
        nanosecond : Int Unsigned32,
        second : Int Unsigned8,
    } […]

This makes no sense to me. The type annotation for parseFractionalTime is:
parseFractionalTime : List U8, List U8 -> Result Time [InvalidTimeFormat]

So what is the difference between:

    Result [
        Err [InvalidTimeFormat],
        Ok Time.Time,
    ] […]

and

Result Time [InvalidTimeFormat]

I see the error message shows a Result [Ok .., Result .. ] as the unexpected return, where as the expected return is a Result {..} where {..} matches the type signature for my Date.Date type. What is this error message telling me?

view this post on Zulip Brendan Hansknecht (Apr 21 2024 at 16:34):

So

{
    hour : Int Unsigned8,
    minute : Int Unsigned8,
    nanosecond : Int Unsigned32,
    second : Int Unsigned8,
}

Looks to just be an accidental inlining of Time.Time

view this post on Zulip Brendan Hansknecht (Apr 21 2024 at 16:35):

So the error is that you have a:

Result [
        Err [InvalidTimeFormat],
        Ok Time.Time,
    ] […]

Which rewritten would be a:

Result (Result Time.Time [InvalidTimeFormat]) […]

view this post on Zulip Brendan Hansknecht (Apr 21 2024 at 16:36):

In other words, you have nested results

view this post on Zulip Brendan Hansknecht (Apr 21 2024 at 16:36):

Aside:
Results probably have poor errors cause they are just tags and not some sort of unique type. As such, they are actually really flexible cause they can be used as any generic tag.

view this post on Zulip Brendan Hansknecht (Apr 21 2024 at 16:39):

Any errors specific to result would need to be careful cause someone technically could make valid tags in many case.
For example, this is valid (so we have our work cut out to make error nice while also dealing with that):

MyResult ok err : [OK ok, Err err]

x = OK 27
when x is
    OK v -> v
    Err _ -> crash "..."

view this post on Zulip Ian McLerran (Apr 21 2024 at 16:40):

Okay, I thought it looked like it was telling me I had a nested result. But I'm tearing my hair out trying to figure out where I could be nesting results, without success. :expressionless:

view this post on Zulip Brendan Hansknecht (Apr 21 2024 at 16:40):

Can you share the code?

view this post on Zulip Ian McLerran (Apr 21 2024 at 16:41):

Sure! One moment...

view this post on Zulip Ian McLerran (Apr 21 2024 at 16:42):

parseFractionalTime : List U8, List U8 -> Result Time [InvalidTimeFormat]
parseFractionalTime = \wholeBytes, fractionalBytes ->
    combineDurationResAndTime = \durationRes, time ->
        when durationRes is
            Ok duration -> Duration.addTimeAndDuration time duration |> Ok
            Err _ -> Err InvalidTimeFormat
    when (wholeBytes, utf8ToFrac fractionalBytes) is
        ([_,_], Ok frac) -> # hh
            time <- parseLocalTimeHour wholeBytes |> Result.map
            frac * nanosPerHour |> Num.round |> Duration.fromNanoseconds |> combineDurationResAndTime time
        ([_,_,_,_], Ok frac) -> # hhmm
            time <- parseLocalTimeMinuteBasic wholeBytes |> Result.map
            frac * nanosPerMinute |> Num.round |> Duration.fromNanoseconds |> combineDurationResAndTime time
        ([_,_,':',_,_], Ok frac) -> # hh:mm
            time <- parseLocalTimeMinuteExtended wholeBytes |> Result.map
            frac * nanosPerMinute |> Num.round |> Duration.fromNanoseconds |> combineDurationResAndTime time
        ([_,_,_,_,_,_], Ok frac) -> # hhmmss
            time <- parseLocalTimeBasic wholeBytes |> Result.map
            frac * nanosPerSecond |> Num.round |> Duration.fromNanoseconds |> combineDurationResAndTime time
        ([_,_,':',_,_,':',_,_], Ok frac) -> # hh:mm:ss
            time <- parseLocalTimeExtended wholeBytes |> Result.map
            frac * nanosPerSecond |> Num.round |> Duration.fromNanoseconds |> combineDurationResAndTime time
        _ -> Err InvalidTimeFormat

and the external call here that returns the final value, Duration.addTimeAndDuration:

addTimeAndDuration : Time, Duration -> Time
addTimeAndDuration = \time, duration ->
    t1nanos = toNanoseconds duration
    dNanos = Time.toNanosSinceMidnight time |> Num.toI128
    t2Nanos = (dNanos + t1nanos) % Const.nanosPerDay
    Time.fromNanosSinceMidnight t2Nanos

view this post on Zulip Brendan Hansknecht (Apr 21 2024 at 16:44):

Result.map to Result.try?

view this post on Zulip Brendan Hansknecht (Apr 21 2024 at 16:45):

Cause Result.map Just maps the success case. So you are mapping the success case to a Result Time.Time [InvalidTimeFormat]

view this post on Zulip Ian McLerran (Apr 21 2024 at 16:46):

Ahhh... that would make sense..

view this post on Zulip Ian McLerran (Apr 21 2024 at 16:47):

Of course 🤦🏻‍♂️

view this post on Zulip Ian McLerran (Apr 21 2024 at 16:52):

I do feel like Result error messages tend to be pretty confusing. They often mismatch, like you say, since they are just a Tag union, so there is intermingled use of Result and Ok Err, combined with mismatched inlining of other types can get pretty messy.

view this post on Zulip Brendan Hansknecht (Apr 21 2024 at 16:59):

Yeah, lots of room for improvement.

view this post on Zulip Notification Bot (Apr 21 2024 at 17:40):

Ian McLerran has marked this topic as resolved.


Last updated: Jul 06 2025 at 12:14 UTC