This is to work around the lack of expressivity of the alternative instance of `Aeson.Parser` in terms of error reporting. The fundamental problem is that there is no way to express "unrecoverable" failures, i.e. parsing failure that should always result in a failure for the whole parser, regardless of the existence of alternatives.
For example, let `p` be a Aeson parser that expects a certain object with fields `a` and `b`, `q` an arbitrary Aeson parser, and consider the parser `r = p <|> q`. How should `r` behave exactly in the case where `p` fails to parse its input?
For example, if the input is not an object, or an object that does not contain the fields `a` and `b`, then it makes sense that the parser `q` is considered. However, if the input is indeed an object that looks like something that `p` accepts, but nevertheless parsing fails because of some error deep down in the structure of one of its fields, normally we wouldn't want `q` to be tried at all, and the parse error for `p` should result in a global failure.
Since this sort of distinction between recoverable and unrecoverable errors is not present at all in Aeson, that means that using the Alternative instance can produce unexpected error messages (as the final error always comes from the last parser tried), and worse than that, result in too lenient behaviour when optional fields are involved.
In this patch, we do not try to solve this problem, but instead work around the leniency issue for optional fields by introducing a new combinator specifically for this case. Specifically, the schema `optField name (Just v) sch` results in an optional field containing a value corresponding to `sch`, and which uses the JSON default value `v` when the haskell value is `Nothing`.
The corresponding parser does not suffer from the issue above, so it results in a failure whenever the field is present, but its value cannot be parsed by the parser corresponding to `sch`. Note that specifying `Nothing` as the default value makes the serialiser simply omit the field altogether when the haskell value is `Nothing`.
Co-authored-by: Akshay Mankar <akshay@wire.com>