Skip to content

wireform-capnproto

wireform-capnproto implements Cap’n Proto, Kenton Varda’s zero-copy serialization framework. Cap’n Proto splits structs into a fixed data section (scalars packed by size) and a pointer section (text, lists, nested structs), making buffers directly mappable for read-heavy workloads. Use this package when you need mmap-friendly serialization with strict schema evolution rules, or when integrating with Cap’n Proto services and .capnp schema files.

  • Typeclass API via ToCapnProto and FromCapnProto with Template Haskell deriving
  • Cap’n Proto IDL parser and codegen from .capnp schema files
  • Segment-based wire layout with separate data and pointer sections
  • Zero-copy-oriented decode that reconstructs values from mapped buffers
  • QuasiQuoter for inline [capnp| ... |] schemas
  • Runtime registry for struct schema lookup

For typed records, derive instances and round-trip through the segment encoder:

{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE TemplateHaskell #-}
import CapnProto.Decode qualified as CPD
import CapnProto.Derive (ToCapnProto (..), FromCapnProto (..), deriveCapnProto)
import CapnProto.Encode qualified as CPE
import Data.Text (Text)
import Data.Word (Word32)
import GHC.Generics (Generic)
data Person = Person
{ personName :: !Text
, personAge :: !Word32
}
deriving stock (Show, Eq, Generic)
$(deriveCapnProto ''Person)
encodePerson :: Person -> ByteString
encodePerson = CPE.encode . toCapnProto
decodePerson :: ByteString -> Either String Person
decodePerson bs = do
val <- CPD.decode bs
fromCapnProto val
bob :: Person
bob = Person "Bob" 42
roundTrip :: Either String Person
roundTrip = decodePerson (encodePerson bob)

You can also work directly with the dynamic Value ADT when exploring wire layout or bridging between schemas:

import qualified Data.Vector as V
import qualified CapnProto.Value as CP
manualStruct :: CP.Value
manualStruct = CP.Struct
(V.fromList [CP.UInt32 42]) -- data section
(V.fromList [CP.Text "hello capnp"]) -- pointer section
manualBytes :: ByteString
manualBytes = CPE.encode manualStruct

Generate types from .capnp files with the quasiquoter or CLI:

{-# LANGUAGE TemplateHaskell #-}
import CapnProto.QQ (capnp)
[capnp|
struct Person {
name @0 :Text;
age @1 :UInt32;
}
|]
Terminal window
wireform-gen capnp -i schema.capnp -o src/Gen/
wireform-capnproto encode + decode (zero-copy decode) wireform-capnproto encode + decode (zero-copy decode) lower is better · ns · ghc-9.8.4 on darwin-aarch64, criterion 1.6.5. Decode is a zero-copy cursor by design: only the outer envelope is resolved at decode time. Per-field reads happen lazily. 0 2500 5000 7500 10000 114 28.4 8610 28.0 Person struct Person[100] encode decode wireform-capnproto encode + decode (zero-copy decode) lower is better · ns · ghc-9.8.4 on darwin-aarch64, criterion 1.6.5. Decode is a zero-copy cursor by design: only the outer envelope is resolved at decode time. Per-field reads happen lazily. 0 2500 5000 7500 10000 114 28.4 8610 28.0 Person struct Person[100] encode decode
Operationencodedecoderatio
Person struct114 ns28.4 ns0.25x
Person[100]8610 ns28.0 ns0.00x

Last run 2026-06-27 11:35:54 UTC. ghc-9.8.4 on darwin-aarch64, criterion 1.6.5. Decode is a zero-copy cursor by design: only the outer envelope is resolved at decode time. Per-field reads happen lazily..

Decode is effectively O(1) regardless of payload size because Cap’n Proto uses zero-copy cursors: only the outer envelope is resolved at decode time, and per-field reads happen lazily on access. Encode is proportional to message size.

The chart and table above are regenerated by wireform-stats from wireform-capnproto/bench-results/summary/capnproto-encode-decode.json — the same source the README chart is built from.

ModulePurpose
CapnProto.DeriveToCapnProto / FromCapnProto and deriveCapnProto
CapnProto.Encode / CapnProto.DecodeSegment encoder and decoder
CapnProto.ValueDynamic untyped Value ADT (data + pointer sections)
CapnProto.Schema / CapnProto.ParserSchema AST and .capnp parser
CapnProto.CodeGen / CapnProto.QQHaskell codegen and quasiquoter
CapnProto.RegistryRuntime struct schema registry