Skip to content

Add untagged and generics derivations#6

Merged
marinelli merged 6 commits into
mainfrom
add-untagged-and-generics
May 22, 2026
Merged

Add untagged and generics derivations#6
marinelli merged 6 commits into
mainfrom
add-untagged-and-generics

Conversation

@marinelli
Copy link
Copy Markdown
Contributor

@marinelli marinelli commented Apr 23, 2026

Simplify API: replace GTaggedJSON with GTagged/GUntagged + add Generic1 support

Motivation

The previous API exposed a single newtype GTaggedJSON parameterised by an options type that had to embed a Maybe Symbol to control whether tagging was enabled. This meant users needed two separate HasTaggedOptions instances (one for 'Just tagKey and one for 'Nothing) and the options type had to carry the tag key at the type level. Internally, this relied on the singleton library to reflect the Maybe Symbol at runtime, adding an unwanted dependency. An initial implementation by @crtschin first explored moving the tag key to the type level and adding Generic1 support, using singleton to bridge the type- and value-level representations; this PR builds on that work while removing the singleton dependency entirely.

Changes

New newtypes replacing GTaggedJSON:

  • GTagged (key :: Symbol) opts a — derive tagged (discriminated union) JSON instances; the tag field name is now a type-level Symbol on the newtype itself, not baked into the options type
  • GUntagged opts a — derive untagged (plain product/sum) JSON instances
  • GTagged1 (key :: Symbol) opts f a / GUntagged1 opts f aGeneric1-based variants for parameterised types (f a), enabling derivation on types with a type parameter

API simplification:

  • Users no longer need to encode Maybe Symbol in their options type; HasTaggedOptions now only needs a single instance per options type
  • The tag key is specified directly in the deriving … via clause: via GTagged "my_tag" MyOpts MyType
  • The tagKey field has been removed from TaggedOptions; tagging vs. non-tagging is now expressed entirely through which newtype you pick
  • unwrap replaces the old .unGTaggedJSON accessor

New Generic1 support (genericLift* functions):

  • genericLiftToJSON, genericLiftToEncoding, genericLiftToPairs, genericLiftToSeries, genericLiftParseJSON, genericLiftDeclareNamedSchema — counterparts to the existing generic* functions, operating via Generic1

Other:

  • Dropped the singleton library dependency
  • CPP compatibility shim for insert-ordered-containers 0.3.0 breaking change (affects openapi3 users on newer versions)
  • Updated tested GHC versions to include 9.12.4
  • Expanded test suite: dual-prefix sum type tests, full Generic1 round-trip and schema tests

@marinelli marinelli force-pushed the add-untagged-and-generics branch from d722c61 to 8564f0c Compare April 23, 2026 20:53
@marinelli marinelli requested a review from potocpav April 23, 2026 21:42
@marinelli marinelli marked this pull request as ready for review April 23, 2026 21:43
@marinelli marinelli force-pushed the add-untagged-and-generics branch from 8564f0c to 051ffc1 Compare April 24, 2026 11:57
@marinelli marinelli removed the request for review from potocpav April 25, 2026 17:25
Copy link
Copy Markdown
Collaborator

@potocpav potocpav left a comment

Choose a reason for hiding this comment

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

Great stuff!

Comment on lines +50 to +59
-- This is due to a breaking change introduced in insert-ordered-containers-0.3.0
-- More info are available here:
-- https://github.com/biocad/openapi3/pull/119
-- https://github.com/erikd/insert-ordered-containers/pull/8
--
#if !MIN_VERSION_openapi3(3,2,5)
import Data.HashMap.Strict.InsOrd qualified as HMSI
#else
import Data.HashMap.Strict.InsOrd.Compat qualified as HMSI
#endif
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does this fix our ordering issues in OpenAPI? 🧐 @svobot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I do not think this is going to fix the ordering. The conditional here is just for building the library with the new version of the openapi3 library.

Here is the background: there was an issue in insert-ordered-containers with the FromJSON and ToJSON instances (see erikd/insert-ordered-containers#7). This problem should have been resolved here: erikd/insert-ordered-containers#8. However, this change made the swagger2 and openapi3 packages incompatible with the latest release of insert-ordered-containers.

One of the contributors to the swagger2 library decided to fix the problem by copying the old version of one of the insert-ordered-containers modules and using it when building the library with insert-ordered-containers > v0.3.0 (see GetShopTV/swagger2#262). The author of openapi3 did the exact same thing (see biocad/openapi3#119).

I think the strategy was to preserve the legacy behavior, leaving no option to use the new version of the insert-ordered-containers library.

This also causes issues in other packages:

  • servant-swagger now strictly depends on insert-ordered-containers > v0.3.0, meaning it will use the internal Compat module (servant-swagger.cabal#L78).
  • servant-openapi3 currently does not build with insert-ordered-containers > v0.3.0 (servant-openapi3.cabal#L88).

As a result, if your project depends on both of those packages, you cannot actually bump either of them, and you are stuck with insert-ordered-containers < v0.3.0.

What we really need to do is propose that the authors of swagger2 and openapi3 add a build flag to use either the new version of insert-ordered-containers (>= v0.3.0) or the legacy version of insert-ordered-containers (< v0.3.0) and the Compat modules.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Does this fix our ordering issues in OpenAPI? 🧐 @svobot

We should try to build openapi3 with the new version of insert-ordered-containers without the Compat module.

@marinelli marinelli merged commit b5f9015 into main May 22, 2026
8 checks passed
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.

2 participants