Proposal: add decoder for nested optionals, handling Nils in the path #4202
Replies: 2 comments 4 replies
-
Hello! This would be written today like so: use value <- decode.then(decode.optionally_at(
["a", "b", "c"],
None,
decode.optional(decode.string),
)) We could possibly add this new function, but it's best to wait some time to get an understanding of how the current API is going. The stdlib is very conservative so we delaying any additions is good. |
Beta Was this translation helpful? Give feedback.
-
My proposed fix in gleam-lang/stdlib#809 ended up raising a concern about preventing callers from intentionally failing on Nil when they desire that behavior. That's not likely to be a concern for JSON decoding, but certainly could be an issue for other uses of dynamic. Instead of changing I expect we will need more community interest to drive this forward, so please leave a comment if you have questions or a use case for this. I've made a number of edits to my original proposal to correct mistakes and focus on the Nil issue. |
Beta Was this translation helpful? Give feedback.
-
Problem Summary
The new
use
decoder API lacks an easy way to cherry-pick a nested value when the path to that value may have missing or null elements. It is possible with the current API by mixingoptional_field
,optional
, andat
– but the method is not simple or easily discoverable.Cherry-picking optional nested values is very helpful when the Gleam type you wish to decode into does not closely match the schema you are decoding from, or if you don’t have a schema and are iterating on the design of your type & decoder.
Proposed Solution Summary
Add an
nullable_subfield
function to thedynamic/decode
module, similar to the existingsubfield
function, but allowing for a default value, signature:In addition, we can consider modifying the existing
optionally_at
function to handle “null” and not just missing values. Its current behavior might be considered a bug, but altering it might also be considered an API change.Problem Detail
Writing a decoder to optionally extract a value for a.bc from the below JSON samples is more difficult than one would expect.
{"id": 1}
{"id": 1, "a": null}
{"id": 1, "a": {"b": null}}
{"id": 1, "a": {"b": {"c": null}}}
{"id": 1, "a": {"b": {"c": “target value”}}}
There are several difficulties:
There is no optional subfield function for the newLouis pointed out thatuse
decoder API, so one must start withoptional_field
and then pass in regular Decoder to navigate deeper into the treeoptional_at
can be combined withthen
to make it ause
decoder.optionally_at
does not handle null values along the path, only missing keys (see failure example below)one_of
+at
can be a concise way to decode a nested value, it does not allow the caller to differentiate a missing value from an invalid one, masking errors (see failure example below)optional
andat
results in a nested set of Options that need to be flattenedProblem Example Code
This example illustrates the existing way to decode this, along with some assertions you may use to test alternatives if you feel I’ve missed something. It should be usable in a fresh gleam project after running
gleam add gleam_json
.The example below succeeds, but the combination of
optional_field
,optional
, andat
is difficult to read, non-obvious, and requires a matching chain ofoption.flatten
calls to unwrap the result.Failing Examples
Trying to use optionally_at instead:
Will fail to decode when
a
isnull
:Trying to use
one_of
instead:Will decode a None, instead of failing when given a number instead of a string:
Solution Detail
My ideal solution would allow the decoder block above to be written as:
Beta Was this translation helpful? Give feedback.
All reactions