logo
首页技术栈工具库讨论
winery
winery
Please see the README on GitHub at https://github.com/fumieval/winery#readme [Index] [Quick Jump] For package maintainers and hackage trustees winery is a serialisation library focusing on performance, compactness and compatibility. The primary feature is that metadata (types, field names, etc) are packed into one schema. A number of formats, like JSON and CBOR, attach metadata for each value: [{"id": 0, "name": "Alice"}, {"id": 1, "name": "Bob"}] In contrast, winery stores them separately, eliminating redundancy while guaranteeing well-typedness: Unlike other libraries that don't preserve metadata (e.g.binary, cereal, store) at all, winery also allows readers to decode values regardless of the current implementation. The interface is simple; serialise encodes a value with its schema, and deserialise decodes a ByteString using the schema in it. It's also possible to serialise schemata and data separately. serialiseSchema encodes a schema and its version number into a ByteString, and serialiseOnly serialises a value without a schema. In order to decode data generated this way, pass the result of deserialiseSchema to getDecoder. Finally run evalDecoder to deserialise them. The recommended way to create an instance of Serialise is to use DerivingVia. for single-constructor records, or just for any ADT. The former explicitly describes field names in the schema, and the latter does constructor names. If you want to customise one of the methods, you can use bundleVia to supply the rest of definitions. If the representation is not the same as the current version (i.e. the schema is different), the data cannot be decoded directly. This is where extractors come in. Extractor parses a schema and returns a function which gives a value back from a Term. You can build an extractor using combinators such as extractField, extractConstructor, etc. If you want to customise the extractor, the pair of gvariantExtractors and buildVariantExtractors is handy. Extractor is Alternative, meaning that multiple extractors (such as a default generic implementation and fallback plans) can be combined into one. Altering an instance for a record type is a little bit tricky. HKD can represent a record where each field is Subextractor instead of the orignal type. The barbies-th allows us to derive it from a plain declaration. Obtain a record of extractors using bextractors :: forall b. (AllB Serialise b, ...) => b Subextractor, update it as necessary, then build an extractor for an entire record by buildRecordExtractor. More generic instance (for covered types) can be defined as below: Term can be deserialised from any winery data. It can be pretty-printed using the Pretty instance: You can use the winery command-line tool to inspect values. At the moment, the following queries are supported: A useful library should also be fast. Benchmarking encoding/decoding of the following datatype. Here's the result: | | encode 1 | encode 1000 | decode | length | |-----------|----------|-------------|---------| ------- | | winery | 0.28 μs | 0.26 ms | 0.81 ms | 58662 | | cereal | 0.82 μs | 0.78 ms | 0.90 ms | 91709 | | binary | 1.7 μs | 1.7 ms | 2.0 ms | 125709 | | serialise | 0.61 μs | 0.50 ms | 1.4 ms | 65437 | | store | 54 ns | 56 μs | 0.13 ms | 126410 | | aeson | 9.9 μs | 9.7 ms | 17 ms | 160558 |