wireform-thrift
wireform-thrift implements Apache Thrift, the
IDL-driven serialization framework used by Cassandra, Parquet footers, and many
high-throughput services. Thrift structs carry numbered field IDs for forward and
backward compatibility, and the package supports both the legacy binary protocol
and the smaller compact protocol. Use this package when you need Thrift wire
compatibility, RPC message framing, or schema codegen from .thrift IDL files.
Key features
Section titled “Key features”- Template Haskell deriving via
deriveThriftfor Haskell record types, withwireform-deriveannotations; Generic defaults (empty instances) work for simple cases - Binary and Compact wire protocols with matching encode/decode entry points
- Thrift IDL parser and codegen from
.thriftschema files - Service definitions for RPC method signatures
- Message framing for request/response envelopes (
Thrift.Message) - JSON bridge for self-describing text rendering
- QuasiQuoter for inline
[thrift| ... |]schemas - Runtime registry for dynamic struct lookup
Basic usage
Section titled “Basic usage”Derive instances with the Template Haskell deriver, then pick a wire protocol. Compact is the recommended choice for new code because it produces smaller payloads:
{-# LANGUAGE DeriveGeneric #-}{-# LANGUAGE TemplateHaskell #-}{-# LANGUAGE DerivingStrategies #-}
import Data.Text (Text)import GHC.Generics (Generic)import Thrift.Class ( ToThrift, FromThrift , encodeThriftBinary, decodeThriftBinary , encodeThriftCompact, decodeThriftCompact )import Thrift.Derive (deriveThrift)
data LogEntry = LogEntry { level :: !Text , message :: !Text , code :: !Int } deriving stock (Show, Eq, Generic)
$(deriveThrift ''LogEntry)
entry :: LogEntryentry = LogEntry "ERROR" "disk full" 507
-- Binary protocol (tag-prefixed fields, larger on the wire)binaryBytes :: ByteStringbinaryBytes = encodeThriftBinary entry
decodeBinary :: Either String LogEntrydecodeBinary = decodeThriftBinary binaryBytes
-- Compact protocol (variable-length encoding, recommended)compactBytes :: ByteStringcompactBytes = encodeThriftCompact entry
decodeCompact :: Either String LogEntrydecodeCompact = decodeThriftCompact compactBytesFor simple cases with no wire-format customization, Generic defaults also
work: add deriving Generic and declare empty instance ToThrift LogEntry
and instance FromThrift LogEntry declarations.
For RPC-style communication, wrap payloads in a message envelope:
import Thrift.Class (toThrift)import Thrift.Message ( ThriftMessage (..), ThriftMessageType (..) , encodeMessageCompact, decodeMessageCompact )
sendRequest :: Text -> LogEntry -> ByteStringsendRequest methodName entry = encodeMessageCompact $ ThriftMessage methodName TMsgCall 1 (toThrift entry)
receiveResponse :: ByteString -> Either String LogEntryreceiveResponse framed = do ThriftMessage _ TMsgReply _ payload <- decodeMessageCompact framed fromThrift payloadGenerate types from IDL with the quasiquoter or CLI:
{-# LANGUAGE TemplateHaskell #-}import Thrift.QQ (thrift)
[thrift| struct Person { 1: string name, 2: i32 age, }|]wireform-gen thrift -i service.thrift -o src/Gen/Performance
Section titled “Performance”Binary vs Compact wire protocol
Section titled “Binary vs Compact wire protocol”| Operation | binary | compact | ratio |
|---|---|---|---|
| encode Person | 248 ns | 259 ns | 1.04x |
| encode [Person] x 100 | 23948 ns | 25963 ns | 1.08x |
Last run 2026-06-27 11:35:55 UTC. ghc-9.8.4 on darwin-aarch64, criterion 1.6.5.
Binary protocol is slightly faster (~8-12%) than Compact across payload sizes. Both are fast enough that the encode cost is negligible relative to network I/O for typical RPC payloads.
The chart and table above are regenerated by wireform-stats from wireform-thrift/bench-results/summary/thrift-binary-vs-compact.json — the same source the README chart is built from.
Notable modules
Section titled “Notable modules”| Module | Purpose |
|---|---|
Thrift.Class | ToThrift / FromThrift plus encodeThriftBinary / encodeThriftCompact |
Thrift.Encode / Thrift.Decode | Low-level binary and compact wire primitives |
Thrift.Wire | Type tags, field IDs, and TType constants |
Thrift.Value | Dynamic untyped Value ADT |
Thrift.Schema / Thrift.Parser | IDL AST and .thrift parser |
Thrift.CodeGen / Thrift.QQ | Haskell codegen and quasiquoter |
Thrift.Message | RPC message envelope and framing |
Thrift.Transport | Length-prefixed transport helpers |
Thrift.Registry | Runtime struct schema registry |
Thrift.JSON | Thrift to JSON bridge |
Thrift.Derive | Template Haskell deriver with annotation modifiers |