wireform-msgpack
wireform-msgpack implements the MessagePack binary serialization format.
MessagePack is widely used for RPC, caching, and inter-service communication
because it is compact, fast to parse, and supported by libraries in most
languages. Use this package when you want a lightweight alternative to JSON
with similar flexibility but smaller payloads.
Key features
Section titled “Key features”- Template Haskell deriving via
deriveMsgPackfor records, enums, and sum types, withwireform-deriveannotations; Generic defaults (empty instances) work for simple cases - Streaming decode for concatenated or length-prefixed MessagePack frames
- msgpack-RPC message encoding for request/response/notification patterns
- JSON bridge for converting between MessagePack and Aeson
Value - Dynamic values via the untyped
ValueADT when schemas are unknown at compile time
Basic usage
Section titled “Basic usage”Define a type and derive codec instances with Template Haskell:
{-# LANGUAGE DeriveGeneric #-}{-# LANGUAGE TemplateHaskell #-}module Person where
import MsgPack.Class (ToMsgPack, FromMsgPack, encodeMsgPack, decodeMsgPack)import MsgPack.Derive (deriveMsgPack)import GHC.Generics (Generic)import Data.Text (Text)
data Person = Person { personName :: !Text , personAge :: !Int } deriving stock (Show, Eq, Generic)
$(deriveMsgPack ''Person)
roundTrip :: Person -> Either String PersonroundTrip p = case decodeMsgPack (encodeMsgPack p) of Left err -> Left err Right val -> Right valFor simple cases with no wire-format customization, Generic defaults also
work: add deriving Generic and declare empty instance ToMsgPack Person and
instance FromMsgPack Person declarations.
For RPC-style messaging, use the msgpack-RPC envelope helpers:
import MsgPack.RPC (RPCMessage(..), encodeRPC, decodeRPC)import Data.Vector (Vector)import qualified Data.Vector as Vimport MsgPack.Value qualified as MV
call :: Text -> Vector MV.Value -> ByteStringcall method params = encodeRPC (RPCRequest 1 method params)
handle :: ByteString -> Either String RPCMessagehandle = decodeRPCWhen processing a buffer that may contain multiple MessagePack values, decode one at a time and advance the cursor:
import MsgPack.Stream (decodeOneWithLeftover)
takeNext :: ByteString -> Either String (MV.Value, ByteString)takeNext = decodeOneWithLeftoverTo inspect or transform values without generated types, round-trip through the dynamic ADT:
import MsgPack.Value qualified as MVimport MsgPack.Encode (encode)import MsgPack.Decode (decode)
dynamicRoundTrip :: MV.Value -> Either String MV.ValuedynamicRoundTrip val = case decode (encode val) of Left err -> Left err Right out -> Right outPerformance
Section titled “Performance”wireform-msgpack is ~4x faster than the Hackage msgpack package on both
encode and decode for a typical record payload.
wireform-msgpack vs Hackage msgpack
Section titled “wireform-msgpack vs Hackage msgpack”| Operation | wireform-msgpack | msgpack | ratio |
|---|---|---|---|
| encode | 297 ns | 1232 ns | 4.14x |
| decode | 388 ns | 1775 ns | 4.58x |
Last run 2026-06-27 11:56:42 UTC. ghc-9.8.4 on darwin-aarch64, criterion 1.6.5.
The chart and table above are regenerated by wireform-stats from wireform-msgpack/bench-results/summary/msgpack-vs-msgpack-haskell.json — the same source the README chart is built from.
Notable modules
Section titled “Notable modules”| Module | Purpose |
|---|---|
MsgPack.Class | ToMsgPack / FromMsgPack, encodeMsgPack, decodeMsgPack |
MsgPack.Encode / MsgPack.Decode | Low-level wire encode and decode |
MsgPack.Value | Dynamic untyped Value ADT |
MsgPack.Stream | Incremental decode for framed input |
MsgPack.RPC | msgpack-RPC request, response, and notification messages |
MsgPack.JSON | MessagePack ↔ JSON conversion |
MsgPack.Derive | Template Haskell deriver with wireform-derive annotations |