{"id":16431483,"url":"https://github.com/dcastro/haskell-flatbuffers","last_synced_at":"2026-03-06T02:06:01.934Z","repository":{"id":56694388,"uuid":"153953802","full_name":"dcastro/haskell-flatbuffers","owner":"dcastro","description":"An implementation of the flatbuffers protocol in Haskell.","archived":false,"fork":false,"pushed_at":"2024-08-25T22:40:37.000Z","size":1312,"stargazers_count":32,"open_issues_count":4,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-12-28T03:14:13.230Z","etag":null,"topics":["flatbuffers","haskell","haskell-library"],"latest_commit_sha":null,"homepage":"https://hackage.haskell.org/package/flatbuffers","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dcastro.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-10-20T22:28:09.000Z","updated_at":"2024-10-26T10:47:26.000Z","dependencies_parsed_at":"2024-02-11T19:55:41.481Z","dependency_job_id":"2fb32b89-0146-43c5-aa34-6939e3c6e46d","html_url":"https://github.com/dcastro/haskell-flatbuffers","commit_stats":{"total_commits":494,"total_committers":3,"mean_commits":"164.66666666666666","dds":0.0080971659919028,"last_synced_commit":"cea6a75109de109ae906741ee73cbb0f356a8e0d"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/dcastro/haskell-flatbuffers","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcastro%2Fhaskell-flatbuffers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcastro%2Fhaskell-flatbuffers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcastro%2Fhaskell-flatbuffers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcastro%2Fhaskell-flatbuffers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dcastro","download_url":"https://codeload.github.com/dcastro/haskell-flatbuffers/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcastro%2Fhaskell-flatbuffers/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30158926,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T22:39:40.138Z","status":"online","status_checked_at":"2026-03-06T02:00:08.268Z","response_time":250,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flatbuffers","haskell","haskell-library"],"created_at":"2024-10-11T08:30:29.605Z","updated_at":"2026-03-06T02:06:01.910Z","avatar_url":"https://github.com/dcastro.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Haskell Flatbuffers\n\nAn implementation of the [flatbuffers protocol][flatbuffers] in Haskell.\n\n[![Build Status](https://travis-ci.com/dcastro/haskell-flatbuffers.svg?branch=master)](https://travis-ci.com/dcastro/haskell-flatbuffers)\n[![Hackage](https://img.shields.io/hackage/v/flatbuffers)](http://hackage.haskell.org/package/flatbuffers)\n\n- [Getting started](#getting-started)\n  - [Codegen](#codegen)\n  - [Enums](#enums)\n  - [Bit flags / Bitmasks](#bit-flags--bitmasks)\n  - [Structs](#structs)\n  - [Unions](#unions)\n  - [File Identifiers](#file-identifiers)\n- [TODO](#todo)\n\n\n## Getting started\n\n1. Start off by writing a [flatbuffers schema][schema] with the data structures you want to serialize/deserialize.\n    ```\n    namespace Data.Game;\n\n    table Monster {\n      name: string;\n      hp: int;\n      locations: [string] (required);\n    }\n    ```\n2. Create a Haskell module named after the namespace in the schema.\n    ```haskell\n    module Data.Game where\n    ```\n3. Use `mkFlatBuffers` to generate constructors and accessors for the data types in your schema.\n    ```haskell\n    {-# LANGUAGE TemplateHaskell #-}\n\n    module Data.Game where\n    import FlatBuffers\n\n    $(mkFlatBuffers \"schemas/game.fbs\" defaultOptions)\n    ```\n4. The following declarations will be generated for you.\n    ```haskell\n    data Monster\n\n    -- Constructor\n    monster :: Maybe Text -\u003e Maybe Int32 -\u003e WriteVector Text -\u003e WriteTable Monster\n\n    -- Accessors\n    monsterName      :: Table Monster -\u003e Either ReadError (Maybe Text)\n    monsterHp        :: Table Monster -\u003e Either ReadError Int32\n    monsterLocations :: Table Monster -\u003e Either ReadError (Vector Text)\n    ```\n\nWe can now construct a flatbuffer using `encode` and read it using `decode`:\n\n```haskell\n{-# LANGUAGE OverloadedStrings #-}\n\nimport           Data.ByteString.Lazy (ByteString)\nimport           FlatBuffers\nimport qualified FlatBuffers.Vector as Vector\n\n-- Writing\nbyteString = encode $\n      monster\n        (Just \"Poring\")\n        (Just 50)\n        (Vector.fromList 2 [\"Prontera Field\", \"Payon Forest\"])\n\n-- Reading\nreadMonster :: ByteString -\u003e Either ReadError String\nreadMonster byteString = do\n  someMonster \u003c- decode byteString\n  name        \u003c- monsterName someMonster\n  hp          \u003c- monsterHp someMonster\n  locations   \u003c- monsterLocations someMonster \u003e\u003e= Vector.toList\n  Right (\"Monster: \" \u003c\u003e show name \u003c\u003e \" (\" \u003c\u003e show hp \u003c\u003e \" HP) can be found in \" \u003c\u003e show locations)\n```\n\nFor the rest of this document, we'll assume these imports/extensions are enabled:\n\n```haskell\n{-# LANGUAGE OverloadedStrings #-}\n\nimport           Data.ByteString.Lazy (ByteString)\nimport           Data.Text (Text)\nimport qualified Data.Text as Text\nimport           FlatBuffers\nimport qualified FlatBuffers.Vector as Vector\n```\n\n### Codegen\n\nYou can check exactly which declarations were generated by browsing your module in ghci:\n\n```plain\nλ\u003e :m Data.Game FlatBuffers FlatBuffers.Vector\nλ\u003e :browse Data.Game\ndata Monster\nmonster :: Maybe Int32 -\u003e WriteTable Monster\nmonsterHp :: Table Monster -\u003e Either ReadError Int32\n```\n\nOr by launching a local hoogle server with Stack:\n\n```plain\n\u003e stack hoogle --rebuild --server\n```\n\nThere are lots of examples in the [test/Examples][examples] folder and the [`THSpec`][thspec] module.\n\nIn particular, `test/Examples/schema.fbs` contains a variety of data structures and `Examples.HandWritten` demonstrates what the code generated by `mkFlatBuffers` would look like.\n\n### Enums\n\n```\nenum Color: short {\n  Red, Green, Blue\n}\n```\n\nGiven the enum declaration above, the following code will be generated:\n\n```haskell\ndata Color\n  = ColorRed\n  | ColorGreen\n  | ColorBlue\n  deriving (Eq, Show, Read, Ord, Bounded)\n\ntoColor   :: Int16 -\u003e Maybe Color\nfromColor :: Color -\u003e Int16\n\ncolorName :: Color -\u003e Text\n```\n\nUsage:\n\n```\ntable Monster {\n  color: Color;\n}\n```\n\n```haskell\ndata Monster\n\nmonster      :: Maybe Int16 -\u003e WriteTable Monster\nmonsterColor :: Table Monster -\u003e Either ReadError Int16\n```\n\n```haskell\n-- Writing\nbyteString = encode $\n      monster (Just (fromColor ColorBlue))\n\n-- Reading\nreadMonster :: ByteString -\u003e Either ReadError Text\nreadMonster byteString = do\n  someMonster \u003c- decode byteString\n  i           \u003c- monsterColor someMonster\n  case toColor i of\n    Just color -\u003e Right (\"This monster is \" \u003c\u003e colorName color)\n    Nothing    -\u003e Left (\"Unknown color: \" \u003c\u003e show i) -- Forwards compatibility\n```\n\n### Bit flags / Bitmasks\n\n```\nenum Colors: uint16 (bit_flags) {\n  Red, Green, Blue\n}\n```\n\nGiven the enum declaration above, the following code will be generated:\n\n```haskell\ncolorsRed, colorsGreen, colorsBlue :: Word16\ncolorsRed = 1\ncolorsGreen = 2\ncolorsBlue = 4\n\nallColors :: [Word16]\n\ncolorsNames :: Word16 -\u003e [Text]\n```\n\nUsage:\n\n```\ntable Monster {\n  colors: Colors = \"Red Blue\";\n}\n```\n\n```haskell\ndata Monster\n\nmonster       :: Maybe Word16 -\u003e WriteTable Monster\nmonsterColors :: Table Monster -\u003e Either ReadError Word16\n```\n\n```haskell\nimport Control.Monad.Except (MonadError, MonadIO, liftEither, liftIO)\nimport Data.Bits ((.|.), (.\u0026.))\nimport qualified Data.Text.IO as Text\n\n-- Writing\nbyteString = encode $\n      monster (Just (colorsBlue .|. colorsGreen))\n\n-- Reading\nreadMonster :: (MonadIO m, MonadError ReadError m) =\u003e ByteString -\u003e m ()\nreadMonster byteString = do\n  someMonster \u003c- liftEither $ decode byteString\n  colors      \u003c- liftEither $ monsterColors someMonster\n\n  let isRed = colors .\u0026. colorsRed /= 0\n  liftIO $ putStrLn $ \"Is this monster red? \" \u003c\u003e if isRed then \"Yes\" else \"No\"\n\n  liftIO $ Text.putStrLn $ \"Monster colors: \" \u003c\u003e Text.intercalate \", \" (colorsNames colors)\n```\n\n\n### Structs\n\n```\nstruct Coord {\n  x: long;\n  y: long;\n}\n```\n\nGiven the struct declaration above, the following code will be generated:\n\n```haskell\ndata Coord\ninstance IsStruct Coord\n\n--  Constructor\ncoord :: Int64 -\u003e Int64 -\u003e WriteStruct Coord\n\n-- Accessors\ncoordX :: Struct Coord -\u003e Either ReadError Int64\ncoordY :: Struct Coord -\u003e Either ReadError Int64\n```\n\nUsage:\n\n```\ntable Monster {\n  position: Coord (required);\n}\n```\n\n```haskell\ndata Monster\n\nmonster         :: WriteStruct Coord -\u003e WriteTable Monster\nmonsterPosition :: Table Monster -\u003e Either ReadError (Struct Coord)\n```\n\n```haskell\n-- Writing\nbyteString = encode $\n      monster (coord 123 456)\n\n-- Reading\nreadMonster :: ByteString -\u003e Either ReadError String\nreadMonster byteString = do\n  someMonster \u003c- decode byteString\n  pos         \u003c- monsterPosition someMonster\n  x           \u003c- coordX pos\n  y           \u003c- coordY pos\n  Right (\"Monster is located at \" \u003c\u003e show x \u003c\u003e \", \" \u003c\u003e show y)\n```\n\n### Unions\n\n```\ntable Sword { power: int; }\ntable Axe { power: int; }\nunion Weapon { Sword, Axe }\n```\n\nGiven the union declaration above, the following code will be generated:\n\n```haskell\n-- Accessors\ndata Weapon\n  = WeaponSword !(Table Sword)\n  | WeaponAxe   !(Table Axe)\n\n-- Constructors\nweaponSword :: WriteTable Sword -\u003e WriteUnion Weapon\nweaponAxe   :: WriteTable Axe   -\u003e WriteUnion Weapon\n```\n\nUsage:\n\n```\ntable Character {\n  weapon: Weapon;\n}\n```\n\n```haskell\ndata Character\n\ncharacter       :: Maybe (WriteUnion Weapon) -\u003e WriteTable Character\ncharacterWeapon :: Table Character -\u003e Either ReadError (Maybe (Union Weapon))\n```\n\n```haskell\n-- Writing\nbyteString = encode $\n      character\n        (Just (weaponSword (sword (Just 1000))))\n\n-- Reading\nreadCharacter :: ByteString -\u003e Either ReadError String\nreadCharacter byteString = do\n  someCharacter \u003c- decode byteString\n  weapon        \u003c- characterWeapon someCharacter\n  case weapon of\n    Just (Union (WeaponSword sword)) -\u003e do\n      power \u003c- swordPower sword\n      Right (\"Weilding a sword with \" \u003c\u003e show power \u003c\u003e \" Power.\")\n    Just (Union (WeaponAxe axe)) -\u003e do\n      power \u003c- axePower axe\n      Right (\"Weilding an axe with \" \u003c\u003e show power \u003c\u003e \" Power.\")\n    Just (UnionUnknown byte) -\u003e Left \"Unknown weapon\" -- Forward compatibility\n    Nothing -\u003e Right \"Character has no weapon\"\n```\n\n### File Identifiers\n\nFrom [\"File identification and extension\"][schema]:\n\n\u003e Typically, a FlatBuffer binary buffer is not self-describing, i.e. it needs you to know its schema to parse it correctly. But if you want to use a FlatBuffer as a file format, it would be convenient to be able to have a \"magic number\" in there, like most file formats have, to be able to do a sanity check to see if you're reading the kind of file you're expecting.\n\u003e\n\u003e Now, you can always prefix a FlatBuffer with your own file header, but FlatBuffers has a built-in way to add an identifier to a FlatBuffer that takes up minimal space, and keeps the buffer compatible with buffers that don't have such an identifier.\n\n```\ntable Monster { name: string; }\n\nroot_type Monster;\nfile_identifier \"MONS\";\n```\n\n```haskell\ndata Monster\ninstance HasFileIdentifier Monster\n\n-- Usual constructor and accessors...\n```\n\nWe can now construct a flatbuffer using `encodeWithFileIdentifier` and use `checkFileIdentifier` to check if it's safe to decode it to a specific type:\n\n```haskell\n{-# LANGUAGE TypeApplications #-}\n\n-- Writing\nbyteString = encodeWithFileIdentifier $\n      monster (Just \"Poring\")\n\n-- Reading\nreadName :: ByteString -\u003e Either ReadError (Maybe Text)\nreadName byteString = do\n  if checkFileIdentifier @Monster byteString then do\n    someMonster \u003c- decode byteString\n    monsterName someMonster\n  else if checkFileIdentifier @Character byteString then do\n    someCharacter \u003c- decode byteString\n    characterName someCharacter\n  else\n    Left \"Unexpected flatbuffer identifier\"\n```\n\n## TODO\n\n### Features\n\n- [ ] gRPC support\n- [ ] Size-prefixed buffers (needed for streaming multiple messages)\n    - [flatbuffers/3898](https://github.com/google/flatbuffers/issues/3898)\n    - [FlatCC](https://github.com/dvidelabs/flatcc/blob/master/doc/binary-format.md#nested-flatbuffers)\n- [ ] Fixed length arrays in structs\n    - [flatbuffers/63](https://github.com/google/flatbuffers/issues/63)\n    - [flatbuffers/3987](https://github.com/google/flatbuffers/pull/3987)\n    - [flatbuffers/5313](https://github.com/google/flatbuffers/pull/5313)\n    - [FlatCC](https://github.com/dvidelabs/flatcc/blob/master/doc/binary-format.md#fixed-length-arrays)\n- [ ] unions of strings / structs\n    - [FlatCC](https://github.com/dvidelabs/flatcc/blob/master/doc/binary-format.md#unions)\n- [ ] `key` attribute (See [\"Storing dictionaries in a FlatBuffer\" section](https://google.github.io/flatbuffers/flatbuffers_guide_use_java_c-sharp.html))\n- [ ] `nested_flatbuffer` attribute\n- [ ] `hash` attribute\n    - [Docs](https://google.github.io/flatbuffers/flatbuffers_guide_writing_schema.html)\n    - [Docs](https://google.github.io/flatbuffers/flatbuffers_guide_use_cpp.html#flatbuffers_cpp_object_based_api)\n- [ ] DSL that allows sharing of data (e.g. reuse an offset to a string/table)\n- [ ] `shared` attribute\n    - [Docs](https://google.github.io/flatbuffers/flatbuffers_guide_use_cpp.html#flatbuffers_cpp_object_based_api)\n- [ ] Attach [Haddock documentation to the generated code](https://hackage.haskell.org/package/template-haskell-2.21.0.0/docs/Language-Haskell-TH-Lib.html#g:32).\n\n### Other\n\n- [ ] TH: sort table fields by size + support `original_order` attribute\n- [ ] Enrich `Vector` API: drop, take, null, folds, sum, elem, for_, traverse_, ideally support most of operations in `Data.Foldable`\n- [ ] Improve error messages during `SemanticAnalysis` stage, provide source code location\n- [ ] Try alternative bytestring builders: `fast-builder`, `blaze-builder`\n- [ ] Try alternative bytestring parsers: `cereal`\n\n [flatbuffers]: https://google.github.io/flatbuffers/\n [schema]: https://google.github.io/flatbuffers/flatbuffers_guide_writing_schema.html\n [examples]: https://github.com/dcastro/haskell-flatbuffers/tree/master/test/Examples\n [thspec]: https://github.com/dcastro/haskell-flatbuffers/blob/master/test/FlatBuffers/Internal/Compiler/THSpec.hs\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcastro%2Fhaskell-flatbuffers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdcastro%2Fhaskell-flatbuffers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcastro%2Fhaskell-flatbuffers/lists"}