Recursive records
Closed this issue · 5 comments
Hey, library looks great.
Is possible to create a decoder for the following?
type rec t = { id: string, child: t }
Thanks
Hey there!
Yes, this is possible with a minor change to your type. It would need to be:
type rec t = { id: string, child: option<t> }
It doesn't have to be an option
, but the child t
needs to be wrapped in some type that has an empty state otherwise it'd be infinitely recursive.
With the type above, you can do:
let rec decoder: unit => Json.Decode.t<t> = () => {
open Json.Decode
map2(
field("id", string),
option(field("child", decoder())),
~f=(id, optChild) => { id: id, child: optChild }
)
}
Unfortunately, the way Rescript's recursive syntax works, the recursive value has to be a function so our decoder here takes unit
.
I think there's a possibility for using lazy here too, but that's probably a separate issue/feature.
Closing this for now. If the above doesn't answer your question feel free to reopen.
Hey, sorry for reopening this but when I try to use the above it blows the stack
Yeah, it blows the stack indeed.
Did any of you guys figure out another way to do this?
I just noticed elm json has a lazy decoder. What would be necessary for us to implement it? I can try that
Hey, looks like I made it:
let lazy_ = thunk => Json.Decode.andThen(succeed(), ~f=thunk)
type rec comment = {message: string, responses: array<comment>}
let rec commentDecoder: unit => Json.Decode.t<comment> = () => {
open! Json.Decode
succeed((msg, responses) => {message: msg, responses: responses})
->andMap(field("message", string))
->andMap(field("responses", array(lazy_(_ => commentDecoder()))))
}
// or
let rec commentDecoder: unit => Json.Decode.t<comment> = () => {
open! Json.Decode
map2(field("message", string), field("responses", array(lazy_(_ => commentDecoder()))), ~f=(
message,
responses,
) => {message: message, responses: responses})
}
Yeah, that's working. ✅