Skip to content

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.

  • Template Haskell deriving via deriveThrift for Haskell record types, with wireform-derive annotations; 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 .thrift schema 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

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 :: LogEntry
entry = LogEntry "ERROR" "disk full" 507
-- Binary protocol (tag-prefixed fields, larger on the wire)
binaryBytes :: ByteString
binaryBytes = encodeThriftBinary entry
decodeBinary :: Either String LogEntry
decodeBinary = decodeThriftBinary binaryBytes
-- Compact protocol (variable-length encoding, recommended)
compactBytes :: ByteString
compactBytes = encodeThriftCompact entry
decodeCompact :: Either String LogEntry
decodeCompact = decodeThriftCompact compactBytes

For 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 -> ByteString
sendRequest methodName entry =
encodeMessageCompact $
ThriftMessage methodName TMsgCall 1 (toThrift entry)
receiveResponse :: ByteString -> Either String LogEntry
receiveResponse framed = do
ThriftMessage _ TMsgReply _ payload <- decodeMessageCompact framed
fromThrift payload

Generate types from IDL with the quasiquoter or CLI:

{-# LANGUAGE TemplateHaskell #-}
import Thrift.QQ (thrift)
[thrift|
struct Person {
1: string name,
2: i32 age,
}
|]
Terminal window
wireform-gen thrift -i service.thrift -o src/Gen/
wireform-thrift binary vs compact wire protocols wireform-thrift binary vs compact wire protocols lower is better · ns · ghc-9.8.4 on darwin-aarch64, criterion 1.6.5 0 12500 25000 37500 50000 248 259 23948 25963 encode Person encode [Person] x 100 binary compact wireform-thrift binary vs compact wire protocols lower is better · ns · ghc-9.8.4 on darwin-aarch64, criterion 1.6.5 0 12500 25000 37500 50000 248 259 23948 25963 encode Person encode [Person] x 100 binary compact
Operationbinarycompactratio
encode Person248 ns259 ns1.04x
encode [Person] x 10023948 ns25963 ns1.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.

ModulePurpose
Thrift.ClassToThrift / FromThrift plus encodeThriftBinary / encodeThriftCompact
Thrift.Encode / Thrift.DecodeLow-level binary and compact wire primitives
Thrift.WireType tags, field IDs, and TType constants
Thrift.ValueDynamic untyped Value ADT
Thrift.Schema / Thrift.ParserIDL AST and .thrift parser
Thrift.CodeGen / Thrift.QQHaskell codegen and quasiquoter
Thrift.MessageRPC message envelope and framing
Thrift.TransportLength-prefixed transport helpers
Thrift.RegistryRuntime struct schema registry
Thrift.JSONThrift to JSON bridge
Thrift.DeriveTemplate Haskell deriver with annotation modifiers