Stream: beginners

Topic: ✔ Encoding Dict Str a


view this post on Zulip Karakatiza (May 07 2024 at 00:19):

I have trouble writing a simple dictionary encoder, would appreciate any help!
To avoid X Y problem: I am looking for the most straightforward way to work with dynamic JSON records, e.g. as a Dict Str a. I cannot use JSON list of "pairs" instead etc.
What I came up with:

main.roc

app "simple-dictionary"
    packages {
        pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br"
    }
    imports [
        pf.Stdout,
        json.Json,
    ]
    provides [main] to pf

Dictionary a := Dict Str a implements [
        Encoding { toEncoder: toDictionaryEncoder },
        Inspect, # auto derive
        Eq, # auto derive
    ]

toDictionaryEncoder : Dictionary val -> Encoder fmt where val implements Encoding, fmt implements EncoderFormatting
toDictionaryEncoder = \@Dictionary val ->
    bytes, fmt <- Encode.custom
    Encode.appendWith
        bytes
        (fmt.record (
            (key, value) <- Dict.toList val |> List.map
            {
                key,
                value : Encode.toEncoder value
            }
        ))
        fmt

main = @Dictionary (Dict.single "a" 0) |> Inspect.toStr |> Stdout.line

roc check error:

thread '<unnamed>' panicked at crates/compiler/solve/src/specialize.rs:866:25:
lambda set region not resolved: (`17.IdentId(2)`, MemberSpecializationInfo { _phase: PhantomData<roc_can::abilities::Resolved>, symbol: `17.IdentId(2)`, specialization_lambda_sets: VecMap { keys: [1], values: [144] } })
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

view this post on Zulip Brendan Hansknecht (May 07 2024 at 01:05):

Not at all sure it will help, but you could try putting a where on Dictionary a to specify that a has Encoding.

view this post on Zulip Karakatiza (May 07 2024 at 01:21):

Not sure what syntax are you referring to?

view this post on Zulip Richard Feldman (May 07 2024 at 03:09):

I think he meant a implements Decoding (the original keyword was has and we later changed it to implements)

view this post on Zulip Karakatiza (May 07 2024 at 03:50):

For

Dictionary a where a implements Encoding := Dict Str a implements [
        Encoding { toEncoder: toDictionaryEncoder },
        Inspect, # auto derive
        Eq, # auto derive
    ]

I am getting

I was partway through parsing an ability definition, but I got stuck
here:

63│  Dictionary a where a implements Encoding := Dict Str a implements [
                                     ^

I was expecting to see a value signature next.

view this post on Zulip Brendan Hansknecht (May 07 2024 at 08:27):

Should be:
Dictionary a := Dict Str a where a implements Encoding implements [...]

view this post on Zulip Karakatiza (May 07 2024 at 15:47):

Got it. Yeah, this doesn't make a difference to the original error

view this post on Zulip Brendan Hansknecht (May 07 2024 at 20:05):

One other thought but still a shot in the dark (don't have access to test rn). What happens if you type the number?

Try typing it in the final line Dict.single "a" 0u64

view this post on Zulip Karakatiza (May 07 2024 at 20:26):

Did not help. I tried to minimize to the following:

test : fmt -> List U8 where fmt implements EncoderFormatting
test = \fmt -> Encode.appendWith
    []
    (fmt.record [{key: "a", value: Encode.toEncoder ""}])
    fmt

main = Stdout.line "Ok"

And now I am getting a more meaningful error

This expression is used in an unexpected way:

89│      (fmt.record [{key: "a", value: Encode.toEncoder ""}])
          ^^^^^^^^^^

This fmt value is a:

    fmt where fmt implements EncoderFormatting

But you are trying to use it as:

    { record : * }b

Note: The type variable fmt says it can take on any value that
implements the ability EncoderFormatting.

But, I see that the type is only ever used as a a record value. Can
you replace fmt with a more specific type?

Am I calling the function of the ability wrong?

https://www.roc-lang.org/builtins/Encode#EncoderFormatting

view this post on Zulip Karakatiza (May 07 2024 at 20:35):

I realized my mistake. The right question is half the answer :)

Being member of EncoderFormatting, record function is not a member of fmt value, but is a function luckily exposed in the built-in Encode module. So the call should be Encode.record, and the full implementation looks like

toDictionaryEncoder : Dictionary val -> Encoder fmt where val implements Encoding, fmt implements EncoderFormatting
toDictionaryEncoder = \@Dictionary val ->
    bytes, fmt <- Encode.custom
    Encode.appendWith
        bytes
        (Encode.record (
            (key, value) <- Dict.toList val |> List.map
            {
                key,
                value : Encode.toEncoder value
            }
        ))
        fmt

view this post on Zulip Notification Bot (May 07 2024 at 21:02):

Karakatiza has marked this topic as resolved.

view this post on Zulip Karakatiza (May 07 2024 at 21:04):

The error message

This expression is used in an unexpected way:

89│      (fmt.record [{key: "a", value: Encode.toEncoder ""}])
          ^^^^^^^^^^

This fmt value is a:

    fmt where fmt implements EncoderFormatting

But you are trying to use it as:

    { record : * }b

was much more useful than the original compiler panic message


Last updated: Jul 06 2025 at 12:14 UTC