Skip to content

Strict versions of record codecs that fail on unknown properties #6

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

Merged
merged 1 commit into from
Sep 6, 2024

Conversation

fsoikin
Copy link
Contributor

@fsoikin fsoikin commented Aug 23, 2024

Turns out there is a use case for not ignoring unknown object keys when parsing records, but failing on them. When the program that parses JSON is not a consumer of upstream API, but takes JSON as input, it's useful to report unknown properties, lest the consumer make a typo without noticing.

This is something I implemented for Spago in purescript/spago#1272, but turns out it's needed in other upstream libraries, so I'm contributing here.

@garyb
Copy link
Owner

garyb commented Aug 23, 2024

I like the idea for sure, I've done something similar in a Scala project but not needed it in PS yet.

I think I'd rather have it entirely separate from the lax object codec though, as doing it this way imposes a performance penalty on all object decoders. I'd also remove the record from the state and just use the Set directly, as that avoids an object allocation for each of the state modifications.

-- | See also `Data.Codec.JSON.Record.object'` for a more commonly useful
-- | version of this function.
object' ∷ ∀ a. { ignoreUnknownProps :: Boolean } -> PropCodec a -> Codec a
object' { ignoreUnknownProps } codec = Codec.codec' dec enc
Copy link
Contributor

@f-f f-f Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed with Gary that the implementation for this should be separate from the lax case, at which point we can avoid the additional parameter (since it would always be false) and name the function in a way that more clearly expresses the intent, e.g. objectStrict

@fsoikin fsoikin force-pushed the track-unknown-props branch from 0a42df8 to cbcb337 Compare August 23, 2024 15:44
@fsoikin fsoikin changed the title An option to fail on unknown properties when parsing a record Strict versions of record codecs that fail on unknown properties Aug 23, 2024
@fsoikin
Copy link
Contributor Author

fsoikin commented Aug 23, 2024

Premature optimization is the root of all evil.


-- | The class used to enable the building of `Record` codecs by providing a
-- | record of codecs.
class RowListCodecStrict (rl ∷ RL.RowList Type) (ri ∷ Row Type) (ro ∷ Row Type) | rl → ri ro where
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at parametrizing this class or its method, but that turned out to be almost as much ceremony as duplicating it. And since we're duplicating everything else anyway...

CJ.recordPropOptional (Proxy ∷ Proxy sym) codec tail
where
codec ∷ CJ.Codec a
codec = coerce (Rec.get (Proxy ∷ Proxy sym) codecs ∷ Optional a)

tail ∷ CJ.PropCodec (Record ro')
tail = rowListCodec (Proxy ∷ Proxy rs) ((unsafeCoerce ∷ Record ri → Record ri') codecs)
tail = rowListCodec @rs ((unsafeCoerce ∷ Record ri → Record ri') codecs)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introducing type applications raises the minimum compiler version to run this to 0.15.x - I'd say we should either not use them at all or do a proper breaking change where all the Proxy are turned into type applications.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be fine converting everything to use type applications, so could accept this as-is and then update the rest in a follow up PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I don't have a problem either way. And since this is your repo @garyb, I'll do whatever you say.

Copy link
Contributor

@f-f f-f left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

Copy link
Owner

@garyb garyb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I've been slow about this! One minor suggestion and then I'm happy to merge.

I'll hold off on releasing it until the few remaining Proxy usages have been updated though. I won't have time to do that until the weekend, so unless you want to do that in a follow up PR too 😉 it'll be a few more days yet before it gets out.

module Data.Codec.JSON.Strict
( PropCodec
, ClaimedProps
, objectStrict
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could just call this object now I suppose?

Copy link
Contributor Author

@fsoikin fsoikin Aug 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then there is a high chance of mixing up the two in consumer code, and there won't be any warning, the code will just silently accept unknown fields.

@fsoikin
Copy link
Contributor Author

fsoikin commented Aug 29, 2024

Yes, I was going to do that in a follow-up PR.

@f-f
Copy link
Contributor

f-f commented Sep 6, 2024

@garyb could we get this merged/released?

@garyb garyb merged commit ecdf0c2 into garyb:main Sep 6, 2024
1 check passed
@garyb
Copy link
Owner

garyb commented Sep 6, 2024

Sorry, I thought I already had merged it. 🤦‍♂️ 😄

@garyb
Copy link
Owner

garyb commented Sep 6, 2024

Published as v2 now, with the switch to VTAs throughout (aside from Variant) applied also.

Thanks @fsoikin for the contribution, and sorry it took so long to get sorted out!

@f-f
Copy link
Contributor

f-f commented Sep 6, 2024

Wonderful, thank you! 🙏

@fsoikin fsoikin deleted the track-unknown-props branch September 6, 2024 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants