-
Notifications
You must be signed in to change notification settings - Fork 5
Unify Json and ppx runtime modules #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ppx_deriving_json_runtime
is still reading Json.Decode.error
which seems to be gone?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking great @andreypopp !
I fixed the tests (don't ask how 🙈 ) in #46. For now I use the deprecated APIs (also helpful to see how users will have to adapt their code). Once they are removed we could update the tests to use the new APIs because they're quite exhaustive.
As can be seen in the PR, some of the JSON error messages were more detailed before. We should bring these details back somehow, do you think it'll be too hard with the PPX approach?
src/Json.mli
Outdated
module Of_json : sig | ||
(** Provides a set of low level combinator primitives to decode | ||
Js.Json.t data structures A decoder combinator will return the | ||
decoded value if successful, or raise a [DecodeError of string] if |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not DecodeError
anymore.
(** JSON can be encoded as JSON, trivially. *) | ||
|
||
(** The type of a error which occurs during decoding JSON values. *) | ||
type of_json_error = Json_error of string | Unexpected_variant of string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unexpected_variant
currently always sends "unexpected variant" in the string payload. Should this payload be removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I've wanted to include the tag name there.
* main: update setup-ocaml
I think #46 is ready:
Re: the last point, maybe we should update the examples code to use the new functions, but we can probably do that once they're removed completely? If you think #46 is good to merge here, feel free to do so. The only things missing would be changelog entry and we should probably test some existing package or project that uses this library to make sure everything works as expected (melange-atdgen-codec-runtime is a good candidate). |
@jchavarri thanks! I've merged your PR and then removed |
Cool! I started taking a look at upgrading melange atdgen codec runtime, but it seems that library blended |
here's a branch of melange-atdgen-codec-runtime that builds with latest |
Any blockers for this to go in? 👼 |
Needs a rebase on top of latest changes in main. |
The last commit merged Tried to use @EmileTrotignon's new error system as much as possible. One small change in error msgs can be seen in
I think new format is better as it shows the actual value, but curious what you think. After this, I'll check the melange-atdgen-runtime-codec branch to use the latest commit. A couple more things:
|
I updated ahrefs/melange-atdgen-codec-runtime#53 with this branch and everything seems to be ok. Added a changelog entry and also a workaround to improve the tests, until we get rid of jest. From what I can see the only things missing are:
otherwise, it looks good to mark as ready for review. |
@@ wrap_exn (fun () -> | ||
let (_ : int) = int (Encode.int inf) in | ||
fail "should throw") | ||
|> toEqual "expected an integer but got inf"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@EmileTrotignon do you know how the error handling can print the OCaml expression name, rather than the JavaScript runtime value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The javascript runtime value likely comes from Console.log, you need a custom printer to do something else, but I have a feeling this wasnt your question. Can you clarify ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figured it out. Turns out this code returns the string inf
:
let infinity = [%raw "Infinity"]
let () = Js.log(Stdlib.string_of_float(infinity))
I just merged #55 and now I don't know if that was such a good idea to merge before this one. There are a few merge conflicts because of that 😬 |
Hopefully its just because files were moved around |
I'll try and solve the conflicts |
* main: Add a feature to allow any json in a variant (#55)
got |
One thing i'd like to bring up again before merging is the split of JSON error types in two variants: I don't see a lot of advantages of this split, and the big disadvantage is that it complicates the error type and downstream users code, because they have to either match on both values, or potentially miss errors on either of both. Can we unify them back and remove the inner variant type for simplicity? Isn't "unexpected variant" also a "JSON error"? |
This is because of polymorphic variants scenario where one type extends another type:
How it works now is
then
If we don't have a separate case for this, then |
@andreypopp Thanks! I think the reason you mention makes sense, I hadn't realized there was this optimization in place. Do we need to use the same exn for both "user-originated errors" and "library-originated errors"? I took a stab at using two different exns in #59. It uses a specific exn for the variant case, which I assumed is something that users will never have to care about. It seems to simplify both the implementation and the ergonomics for users, but I'm not sure i'm missing st, please let me know what you think. |
re ergonomic: in case of two exceptions users will have to care about these two when recovering from decoding errors, so it's still a breaking change |
Hm right. I was thinking that the unexpected variant exception would not "escape" the case we were discussing for polyvars extension, but it will do for regular polyvars that are not extended. As this optimization only kicks in when there are decode operations that fail, and we can expect variants to not have crazy depth (reasonable to expect < 10 levels) would you be ok sidestepping it? I.e. use same exception everywhere? If so, I can update the branch with the changes so we can merge this (Ahrefs build is finally passing, using the latest commit). |
To be honest, I'd not make this assumption: this might hold for the code we are writing but in general it might be arbitrary payloads.
But this is what's done in this PR no? Same exception but two cases of error within the exception. Can you remind me what's the downside of the current approach in this PR? The only downsides I'm aware of are:
Just note that there needs to be a special care always to re-raise the original exception (from the |
Let's imagine there are cases out there with 10+ nested levels of types. We still agree that the optimization only kicks in for cases when there is an error in the payload? In other words, for the regular functioning of the library (parsing correct values) the optimization has no impact?
I meant one exception with just a string payload (e.g.
This is the main downside I was considering. For e.g. Ahrefs monorepo, there are ~20 instances of
I hadn't thought of this but I guess it's another downside.
I updated the branch with a couple of changes:
The changes in the tests included in the last commit show how the errors for the extended variant case were improved (unrelated to having going one way or another, but I guess some side benefit of this whole conversation). |
What do you mean forgotten? Compiler should warn if you don't match on all cases, no? Anyway, as I've said, I don't think we should ever match on a specific error in user code — just convert to string and log. |
I was doing |
This was code that existed already. We have to think about migration paths too. |
Yeah, so the migration path is simple:
Anyway, I don't feel strongly about keeping the two error cases, just instinctively feel that such backtracking in deserialisation code could be dangerous, though I can't think of a case where it will blow up at the moment. |
I added back the unexpected variant error in c0e1abf. I also fixed a case with missing errors, when decoding arrays where we were matching only on I had to add helper functions to make sure the unexpected variants carry relevant error information and not just Please take a look, if everything looks good, feel free to merge that PR into this one. |
We will have to remember to always match on |
Tests are actually failing in the branch... Looking. |
Should be fixed. |
Summary of what the PR is doing now:
|
Confirmed with @andreypopp that we can merge this. I will prepare an opam release afterwards. |
Closes #36.