ahrefs / atd

Static types for JSON APIs
Other
308 stars 53 forks source link

atdgen: strange generated JSON for sum type #406

Closed Karmaki closed 2 months ago

Karmaki commented 3 months ago

With this (simplified) example:

type t_kind = [ A of (int * int) | B of string ]<ocaml repr="classic">
type t_elem = { kind: t_kind; info: string; }
type t = t_elem list 

and:

let example =
  let a = Prog_t.{ kind = A (0, 0); info = "info" } in
  let b = Prog_t.{ kind = B "hello"; info = "some info" } in
  let c = Prog_t.{ kind = B "bye"; info = "some more info" } in
   [ a ; b ; c]

  let () =
    let json = Prog_j.string_of_t example in
   print_endline (Yojson.Safe.prettify json) 

I get:

[
  { "kind": <"A": (0, 0)>, "info": "info" },
  { "kind": <"B": "hello">, "info": "some info" },
  { "kind": <"B": "bye">, "info": "some more info" }
]

What are these strange <...>?

I was expecting:

[
  {"kind":{"A":[0,0]},"info":"info"},
  {"kind":{"B":"hello"},"info":"some info"},
  {"kind":{"B":"bye"},"info":"some more info"}
]

Is it a bug? Something that I am doing wrong? How can I fix that?

purefunctor commented 2 months ago

@Karmaki I ran into this as well. Apparently you also need to pass -j-std when invoking atdgen to force it to encode variants into standard JSON types.

Karmaki commented 2 months ago

Great! Thank you for your suggestion @purefunctor. I'll try that ASAP.

Karmaki commented 2 months ago

OK, now I get:

[
  { "kind": [ "A", [ 0, 0 ] ], "info": "info" },
  { "kind": [ "B", "hello" ], "info": "some info" },
  { "kind": [ "B", "bye" ], "info": "some more info" }
]

which is a lot better, but not quite what I was expected... Why is the kind an array and not an object? Is there a way to change that?

purefunctor commented 2 months ago

Why is the kind an array and not an object?

I reckon it's simply an opinionated encoding, maybe there's an optimization associated with it?

I think you can try this adapter if it's the encoding you want: https://ocaml.org/p/atdgen-runtime/latest/doc/Atdgen_runtime/Json_adapter/One_field/index.html

Karmaki commented 2 months ago

Thanks. Just found it myself at the same time.

So with:

type t_pair = (int * int)
type t_kind = [
  A of t_pair
| B of string
]<ocaml repr="classic"><json adapter.ocaml="Atdgen_runtime.Json_adapter.One_field">

type t_elem = { kind: t_kind; info: string; }
type t = t_elem list 

I now get just what I wanted:

[
  { "kind": { "A": [ 0, 0 ] }, "info": "info" },
  { "kind": { "B": "hello" }, "info": "some info" },
  { "kind": { "B": "bye" }, "info": "some more info" }
]

Many thanks for your help. Closing this ticket then.