Skip to content

Getting started

Working code before theory. The wireform repo is the fastest playground; your own Cabal package is only a few lines more.

You needNotes
GHC9.6+ recommended; GHC2021.
cabal-install3.x
Optional: Nixnix develop gives pinned GHC + pre-built deps.

No Docker, no protoc, and no language-specific runtimes needed.

Terminal window
cd wireform-
cabal update
cabal run example-msgpack

Output: a Person record round-tripped through MessagePack via Generic defaults. No schema, no codegen. This is the lowest-ceremony path.

Try a few more to see the range:

Terminal window
cabal run example-protobuf # protobuf message instances
cabal run example-xml # Generic XML encode/decode
cabal run example-avro # Avro schema + value API
cabal run example-cbor # CBOR encode/decode
cabal run example-parquet # Parquet footer metadata

For MessagePack, CBOR, BSON, YAML, TOML, EDN, Ion, Bencode, CSV, XML, HTML.

  1. Define records with Generic.
  2. Derive the format’s classes.
  3. Call the typed encode/decode.
import MsgPack.Class (ToMsgPack, FromMsgPack, encodeMsgPack, decodeMsgPack)
data Person = Person { name :: !Text, age :: !Int }
deriving stock (Show, Eq, Generic)
deriving anyclass (ToMsgPack, FromMsgPack)
let bytes = encodeMsgPack (Person "Ada" 36)

Same pattern for ToCBOR/FromCBOR, ToBSON/FromBSON, ToXML/FromXML, ToYAML/FromYAML, ToTOML/FromTOML, etc. Derive multiple at once:

deriving anyclass (ToMsgPack, FromMsgPack, ToCBOR, FromCBOR, ToYAML, FromYAML)

Best when: you control both ends of the wire and want little ceremony.

Step 3: Use wireform from your own package

Section titled “Step 3: Use wireform from your own package”

Path dependency (typical while hacking or before Hackage)

Section titled “Path dependency (typical while hacking or before Hackage)”
~/Code/
wireform-/ # this repo
my-app/
cabal.project
my-app.cabal
app/Main.hs

cabal.project:

packages:
.
../wireform-

my-app.cabal:

cabal-version: 3.0
name: my-app
version: 0.1.0.0
build-type: Simple
executable my-app
main-is: Main.hs
hs-source-dirs: app
default-language: GHC2021
build-depends:
base ^>=4.18
, text ^>=2.0
, bytestring ^>=0.11
, wireform
default-extensions:
OverloadedStrings
DeriveGeneric
DerivingStrategies
DeriveAnyClass

app/Main.hs:

module Main where
import Data.Text (Text)
import qualified Data.ByteString as BS
import GHC.Generics (Generic)
import MsgPack.Class (ToMsgPack, FromMsgPack, encodeMsgPack, decodeMsgPack)
data Person = Person
{ name :: !Text
, age :: !Int
} deriving stock (Show, Eq, Generic)
deriving anyclass (ToMsgPack, FromMsgPack)
main :: IO ()
main = do
let bytes = encodeMsgPack (Person "Ada" 36)
putStrLn $ "Encoded to " <> show (BS.length bytes) <> " bytes"
case decodeMsgPack bytes of
Right p -> print (p :: Person)
Left e -> putStrLn $ "decode failed: " <> e
Terminal window
cd ~/Code/my-app && cabal run my-app

Put .proto files under proto/ and splice them in:

{-# LANGUAGE TemplateHaskell #-}
import Proto.TH (loadProto)
$(loadProto "proto/person.proto")

For imports across files, pass include dirs:

import Proto.TH (loadProtoWith, defaultLoadOpts, LoadOpts(..))
$(loadProtoWith defaultLoadOpts { loIncludeDirs = ["proto", "."] } "proto/api.proto")

Reference: cabal run example-th in the wireform repo.

No global install needed:

Terminal window
cabal exec wireform-gen -- proto -i proto/person.proto -o gen/
cabal exec wireform-gen -- avro -i schemas/user.avsc -o gen/
cabal exec wireform-gen -- thrift -i service.thrift -o gen/

Then add gen to hs-source-dirs and list modules in your .cabal.

Supported schema languages: proto, avro, thrift, bond, capnp, fbs, asn1, xsd.

Once basic encode/decode works, these are the modules you reach for:

GoalModule
Stream protobuf messages over a socketProto.Decode.Stream
Stream MessagePack valuesMsgPack.Stream
Read Avro container filesAvro.Container
Avro schema evolutionAvro.Resolution
gRPC framing (without full server)Proto.GRPC
Full gRPC client/serverwireform-grpc package
Thrift RPC headersThrift.Message
MsgPack RPCMsgPack.RPC
Dynamic protobuf (no generated types)Proto.Dynamic
Protobuf text formatProto.TextFormat
CBOR diagnostic dumpsCBOR.Diagnostic
CSS selectors on HTMLHTML.Selector
XPath queries on XMLXML.Path
XSLT transformsXML.XSLT
SAX events from XMLXML.SAX
Concurrent XML parsingXML.Incremental
Parquet read/writeParquet.Read / Parquet.Write
Iceberg table metadataIceberg.JSON / Iceberg.Read
ORC columnsORC.Read
Arrow IPCArrow.IPC
Kafka produce/consumeKafka.Client.Producer / Kafka.Client.Consumer
Kafka Streams DSLKafka.Streams
SymptomLikely causeFix
loadProto cannot find fileWrong cwd or pathPaths are relative to package root; cabal run from there.
Proto import not foundMissing include dirloIncludeDirs in loadProtoWith, or -I for wireform-gen proto.
Could not find module 'Proto.…'Generated code not wiredAdd gen to hs-source-dirs; list modules in .cabal.
Link errors / missing C symbolsNo C compilerInstall gcc/clang; on macOS, Xcode CLI tools.
Bounds / solver failuresVersion mismatchMatch base/text to your GHC; or use the path dependency.