generic-data-functions

Familiar functions lifted to generic data types

https://github.com/raehik/generic-data-functions#readme

LTS Haskell 23.27:0.6.0
Stackage Nightly 2025-07-12:0.6.0
Latest on Hackage:0.6.0

See all snapshotsgeneric-data-functions appears in

MIT licensedbyBen Orchard
Maintained byBen Orchard
This version can be pinned in stack with:generic-data-functions-0.6.0@sha256:2f71cb6b80a1129c72d75d79fb2e8ab545acce501c059a1090c3a82818d9b410,2196

Module documentation for 0.6.0

generic-data-functions

Generic functions that approximate familiar term-level functions. The genericshandle the sum of products representation and have “holes” for the base case,which must be filled by the user via a type class. Perhaps you may also think ofthese as “reusable” or “generic” generics.

Most relevant for simple parsing and printing/serializing/reducing tasks wherethe only “work” to do for the given data type is mechanical field sequencing. Ifyou require more logic than that (and can’t place it in types/newtypes), youwill not be able to use this library.

Emphasis is put on type safety and performance. Notably, for sum type generics,we permit parsing constructor names on the type level (viasymparsec). For cases where youwant to implement your own high-performance sum type handling, we provideGeneric.Data.FOnCstr.

Rationale

There are a number of competing parsing and serialization Haskell libraries.Most have a type class for enumerating permitted types, and some simple genericswhich use that type class for the base case. Other than that base case, everyoneis writing largely the same generic code.

While writing my own libraries, I realized that if another developer wanted usemy serializer, they would probably need to write their own type class for basecases (due to some specific library design). Alas, this would mean they have tocopy-paste my generics and swap the type classes. Very silly. But it turned outmy generics were otherwise highly general, so I spun them out into this library.Now you can swap the type class just by filling in some holes.

Design notes

Sum types are handled by prepending a sum tag

Unless otherwise specified, this is how we disambiguate constructors. How thesum tag is parsed from a constructor is up to the user (and may be done on thetype-level if one wishes).

Functions

foldMap (L->R)

foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m

The user provides thea -> m dictionary via a special type class instance.Constructor fields are mapped and combined left-to-right. Sum representationsare handled by mappending the constructor via a user-suppliedString -> mfirst.

Useful for:

  • simple binary serializers which just concatenate fields together
  • reducing to a numeric value

traverse (L->R)

traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)

The user provides thef a dictionary via a special type class instance.Constructor field actions are run left-to-right.Sum representations are handled by running a constructor action first (thusrequiringMonad f).

Useful for:

  • simple binary parsers which can parse genericfoldMap output

contra

I don’t know what this is exactly. It’s like contramap?

Notes

Orphan instances

This library is designed to work with and around existing libraries and typeclasses. Thus, you will likely be dealing in orphans. Instances, that is. That’slife, Jim.

Funky generics

I have made some weird design choices in this library. Here are some rationales.

Handling badness on type & term levels

I don’t like silently erroring on badly-configured generics usage, e.g. askingfor a function via generics for an empty data type. Originally, I made thosetype error, and that was that. But it meant I would write the same instance overand over again. And that requirement was hidden in a type class implementation.Really, it’d be nice if I could put such requirements directly in the types ofthe functions that have them.

I’ve done that. Now, on certainrepresentation errors, e.g. you tried to usenon-sum generics on a sum type, we runtime error instead. However, there’s aseparate layer for makingassertions about generic representation on the typelevel, the use of which is highly suggested.

Note that if you like to write wrappers over generic functions to fill incertain bits of info, your job just got a lot uglier. Soz. Anything for typesafety my sweet.

License

Provided under the MIT license. SeeLICENSE for license text.

Changes

0.6.0 (2024-06-15)

  • support parsing constructor names on type-level forfoldMap,traverse
    • effectively requires Symparsec– but we need not incur a dependency
    • previous behaviour is still present using*Raw functions
  • addFOnCstr, for targeting a single constructor of a sum type
  • bump minimum compiler to GHC 9.2 (base-4.16) because I can’t easily test GHC9.0
  • big cleanup

0.5.1 (2024-04-10)

  • remove spurious constraint in Traverse.Sum (should be same semantics)
  • bump text upper bound for GHC 9.8 compatibility

0.5.0 (2024-04-05)

  • RemoveSumOpts type used for switching whether or not to permit “singletonsum types” for sum handlers. This was a type-level switch which changedruntime behaviour. generic-data-asserts provides type-level asserts which failat compile time. Now you can’t fail at runtime on a singleton sum – you musteither allow it, or fail at compile time – but I don’t see this as a problem.
  • Add “checked constructor names” to generic traverse fail.
  • Clean up some errors,error usage:
    • V1 errors are handled usingabsurdV1 :: V1 p -> a (likeabsurd :: Void -> a) instead of error messages – since I suppose that’s what you want ifyou’re thrusting void data types at us
    • except for generic traverse, where the user can choose to wrap it into theirfunctor (e.g. as a parse error)

0.4.1 (2024-04-04)

  • Remove generic representation asserts. We can handle them elsewhere withoutcluttering type signatures here. (I strongly recommend them, but that’s not areason to keep them here.)
  • UnwrapD1 in generic type classes rather than handing off to user. This wasnever really clever or useful, as it turns out. Saved a an extra type classper function, but requires the user to write more weird stuff.

0.3.1 (2024-04-03)

  • fix error in traverse types

0.3.0 (2024-04-03)

  • Use type tags, push actual target types into a type family. It means morerequired type annotations, but this is fine by me as I think this libraryshould be implemented using GHC 9.10TypeApplications.
  • SwapNoRec0,EmptyRec0 for uninstantiated data types. Less unwrapping,more consistent to the rest of the type-heavy interface. (They were labelled“via” but never used withDerivingVia.)

0.2.0 (2023-08-04)

  • Redesign interface, pushing certain checks out of type classes into top-levelgeneric function type signature. It means busier top-level types and more codefor wrapping them, but it allows for more flexibility and cleans upimplementation. (And the busyness simply makes explicit the implicit checksthat were being done before.)

0.1.1 (2023-07-20)

  • add work-in-progress store-style genericfoldMap, encoding constructors bytheir index, atGeneric.Data.Function.FoldMap.SumConsByte

0.1.0 (2023-06-23)

Initial release.

  • extracted from binrep