packages feed

winery-1: README.md

# winery

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:

```
0402 0402 0269 6410 046e 616d 6514  [{ id :: Integer, name :: Text }]
0200 0541 6c69 6365 0103 426f 62    [(0, "Alice"), (1, "Bob")]
```

Unlike `binary` or `cereal` which doesn't preserve metadata at all, winery also
allows readers to decode values regardless of the current implementation.

## Interface

The interface is simple; `serialise` encodes a value with its schema, and
`deserialise` decodes a ByteString using the schema in it.

```haskell
class Serialise a where
  schema :: Serialise a => proxy a -> Schema

serialise :: Serialise a => a -> B.ByteString
deserialise :: Serialise a => B.ByteString -> Either WineryException a
```

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.

```haskell
serialiseSchema :: Schema -> B.ByteString
serialiseOnly :: Serialise a => a -> B.ByteString
```

In order to decode data generated this way, pass the result of `deserialiseSchema`
to `getDecoder`. Finally run `evalDecoder` to deserialise them.

```haskell
deserialiseSchema :: B.ByteString -> Either WineryException Schema
getDecoder :: Serialise a => Schema -> Either WineryException (Decoder a)
evalDecoder :: Decoder a -> B.ByteString -> a
```

## Deriving an instance

The recommended way to create an instance of `Serialise` is to use `DerivingVia`.

```haskell
  deriving Generic
  deriving Serialise via WineryRecord Foo
```

for single-constructor records, or just

```haskell
  deriving Generic
  deriving Serialise via WineryVariant Foo
```

for any ADT. The former explicitly describes field names in the schema, and the
latter does constructor names.

## Backward compatibility

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`.

If having default values for missing fields is sufficient, you can pass a
default value to `gextractorRecord`:

```haskell
  extractor = gextractorRecord $ Just $ Foo "" 42 0
```

You can also build an extractor using combinators such as `extractField`, `extractConstructor`, etc.

```haskell
buildExtractor
  $ ("None", \() -> Nothing)
  `extractConstructor` ("Some", Just)
  `extractConstructor` extractVoid
  :: Extractor (Maybe a)
```

`Extractor` is Alternative, meaning that multiple extractors (such as a default
generic implementation and fallback plans) can be combined into one.

## Pretty-printing

`Term` can be deserialised from any winery data. It can be pretty-printed using the `Pretty` instance:

```
{ bar: "hello"
, baz: 3.141592653589793
, foo: Just 42
}
```

You can use the `winery` command-line tool to inspect values.

```
$ winery '.[:10] | .first_name .last_name' benchmarks/data.winery
Shane Plett
Mata Snead
Levon Sammes
Irina Gourlay
Brooks Titlow
Antons Culleton
Regine Emerton
Starlin Laying
Orv Kempshall
Elizabeth Joseff
Cathee Eberz
```

At the moment, the following queries are supported:

* `.` return itself
* `.[]` enumerate all the elements in a list
* `.[i]` get the i-th element
* `.[i:j]` enumerate i-th to j-th items
* `.foo` Get a field named `foo`
* `F | G` compose queries (left to right)

## Performance

A useful library should also be fast. Benchmarking encoding/decoding of the
following datatype.

```haskell
data Gender = Male | Female

data TestRec = TestRec
  { id_ :: !Int
  , first_name :: !Text
  , last_name :: !Text
  , email :: !Text
  , gender :: !Gender
  , num :: !Int
  , latitude :: !Double
  , longitude :: !Double
  }
```

Here's the result:

|           | encode 1 | encode 1000 | decode  |
|-----------|----------|-------------|---------|
| winery    | __0.28 μs__  | __0.26 ms__ | __0.81 ms__ |
| cereal    | 0.82 μs  | 0.78 ms     | 0.90 ms |
| binary    | 1.7 μs   | 1.7 ms      | 2.0 ms  |
| serialise | 0.61 μs  | 0.50 ms     | 1.4 ms  |
| aeson     | 9.9 μs   | 9.7 ms      | 17 ms   |