This repository contains an implementation of Google's Protocol Buffers in Haskell. It provides a pure Haskell implementation that uses GHC.Generics and does not require .proto files to function.
- Pure Haskell implementation of Protocol Buffers
- Uses GHC.Generics for automatic encoding/decoding
- No preprocessor or additional build steps required
- Supports Required, Optional, Repeated, and Packed fields
- Type-safe field tags using DataKinds
This project uses the Cabal build system for Haskell.
cabal update
cabal build --enable-tests --enable-benchmarkscabal test --enable-testscabal benchcabal checkThis project commonly uses the following GHC language extensions:
DeriveGeneric- For automatic Generic instance derivationDataKinds- For type-level numeric field tagsOverloadedStrings- For Text literalsFlexibleContextsandFlexibleInstances- For generic programmingTypeOperators- For generic representation types
- Use camelCase for function and variable names
- Use PascalCase for type names and constructors
- Field selectors in protobuf messages should be descriptive
- Type parameters typically use single letters (a, b, c) or meaningful names
- Main library code is in
src/Data/ProtocolBuffers/ - Test suite is in
tests/Main.hs - Benchmarks are in
bench/ - Plugin code (currently commented out) is in
plugin/
- Use Haddock-style comments for public APIs
- Include examples in documentation where appropriate
- Document complex type-level programming constructs
Data.ProtocolBuffers- Main public APIData.ProtocolBuffers.Internal- Internal implementation detailsData.ProtocolBuffers.Encode- Encoding logicData.ProtocolBuffers.Decode- Decoding logicData.ProtocolBuffers.Wire- Wire format handlingData.ProtocolBuffers.Types- Core type definitions
Required n a- Required field with tag nOptional n a- Optional field with tag n (uses Last monoid)Repeated n a- Repeated field with tag n (list of values)Packed n a- Packed repeated field with tag nValue a- Wraps primitive typesMessage a- Wraps nested message types
Encode- For encoding messages to binary formatDecode- For decoding messages from binary formatHasField- For getting and setting field valuesEncodeWire/DecodeWire- For primitive wire type encoding/decoding
- Uses Tasty test framework (tasty, tasty-hunit, tasty-quickcheck)
- QuickCheck properties for roundtrip testing
- HUnit tests for specific encoding/decoding cases
- Google reference test cases for compatibility
The project targets multiple GHC versions as specified in CI:
- GHC 8.10.7
- GHC 9.0.2
- GHC 9.2.8
- GHC 9.4.8
- GHC 9.6.6
- GHC 9.8.4
Ensure changes are compatible with the oldest supported GHC version (8.10.7).
- Understand the Generic programming approach used (GHC.Generics)
- Review existing encoding/decoding implementations
- Check for similar patterns in the codebase
- Ensure changes maintain wire format compatibility
- Run the full test suite:
cabal test - Run cabal check:
cabal check - Build with benchmarks enabled:
cabal build --enable-benchmarks - Consider adding QuickCheck properties for new functionality
- Update documentation if public APIs are modified
- Protocol Buffers has specific wire format requirements - test thoroughly
- Field tags must be unique within a message
- Type-level programming can be tricky - use
:kindin GHCi to debug - Changes to encoding must maintain backward compatibility
- Be careful with orphan instances
Key dependencies include:
base- Base Haskell librarybytestring- Efficient byte stringsbinary- Binary serializationtext- Text handlingunordered-containers- Hash maps and setsmtl- Monad transformer librarydeepseq- For benchmarking
Avoid adding new dependencies unless absolutely necessary.
The following Protocol Buffers features are not currently implemented:
- Default values for Optional fields
- Extension fields
- Storing unknown fields (fields without a mapped tag)
When working on these areas, document any intentional limitations or design decisions.