stdlib icon indicating copy to clipboard operation
stdlib copied to clipboard

decode function for keyed varients

Open CrowdHailer opened this issue 4 months ago • 2 comments

I think with the current API the expected way to solve varients is using one_of this doesn't give very good errors. I have been using the following function in several projects. It decodes first a discriminator then picks a subsequent decoder based on it. This allows it to show a specific error for the discriminator, and then good errors if the discriminator is known but it still fails to decode.

Here is the function https://github.com/CrowdHailer/oas/blob/main/src/oas/decodex.gleam#L3

Check the tests for using it and the errors it gives https://github.com/CrowdHailer/oas/blob/main/test/oas/decodex_test.gleam

I'm sure there's an argument for not extending the standard library, in which case I'd be interested in the best way to define a decoder (just a decoder without an essentially similar discriminate function extracted) that gives useful errors.

The proposal in this issue is to add a discriminate function the same or similar to the one I have linked here

CrowdHailer avatar Jul 29 '25 07:07 CrowdHailer

What advantage would this have over the approach the documentation details for decoding these?

lpil avatar Jul 29 '25 17:07 lpil

The example in the doc's is too simplistic, because everything after the switch is a success.

I'll try an use the approach as I understand it from the docs.

pub type Thing {
  Foo(String)
  Bar(Int)
}
pub fn demo_test() {
  let decoder = {
    use decoded_string <- decode.field("type", decode.string)
    case decoded_string {
      // Return succeeding decoders for valid strings
      "foo" -> {
        use foo <- decode.field("foo", decode.string)
        decode.success(Foo(foo))
      }
      "bar" -> {
        use bar <- decode.field("bar", decode.int)
        decode.success(Bar(bar))
      }
      // Return a failing decoder for any other strings
      _ -> decode.failure(Bar(0), "Thing")
    }
  }
  decode.run(
    dynamic.properties([#(dynamic.string("type"), dynamic.int(1))]),
    decoder,
  )
  |> echo
  // Error([DecodeError(expected: "String", found: "Int", path: ["type"]), DecodeError(expected: "Thing", found: "Dict", path: [])])
}

Running this returns two errors. the first is valid. The second in my mind is erroneous.

The error for this decode is also not great

  decode.run(
    dynamic.properties([#(dynamic.string("type"), dynamic.string(""))]),
    decoder,
  )
  |> echo
// Error([DecodeError(expected: "Thing", found: "Dict", path: [])])

Thing is a Dict so this error is also a bit misleading.

When trying to look at 1k plus lines of open api spec I've lost time to both of these errors.

CrowdHailer avatar Jul 29 '25 18:07 CrowdHailer