packages feed

winery-0.2.1: README.md

# winery

winery is a serialisation library for Haskell.

* __Fast encoding__: can create a bytestring or write to a handle efficiently
* __Compact representation__: uses VLQ by default. Separates schemata and contents
* __Stateless decoding__: you can decode a value without reading all the leading bytes
* __Inspectable__: data can be read without the original instance

## 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

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

It's also possible to serialise schemata and data separately.

```haskell
-- Note that 'Schema' is an instance of 'Serialise'
schema :: Serialise a => proxy a -> Schema
serialiseOnly :: Serialise a => a -> B.ByteString
```

`getDecoder` gives you a deserialiser.

```haskell
getDecoder :: Serialise a => Schema -> Either StrategyError (ByteString -> a)
```

For user-defined datatypes, you can either define instances

```haskell
instance Serialise Foo where
  schemaVia = gschemaViaRecord
  toEncoding = gtoEncodingRecord
  deserialiser = gdeserialiserRecord Nothing
```

for single-constructor records, or just

```haskell
instance Serialise Foo
```

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

## Streaming output

You can write data to a handle without allocating a ByteString. You can see the
length before serialisation.

```haskell
toEncoding :: Serialise a => a -> Encoding
hPutEncoding :: Handle -> Encoding -> IO ()
getSize :: Encoding -> Int
```

## The schema

The definition of `Schema` is as follows:

```haskell
data Schema = SSchema !Word8
  | SUnit
  | SBool
  | SChar
  | SWord8
  | SWord16
  | SWord32
  | SWord64
  | SInt8
  | SInt16
  | SInt32
  | SInt64
  | SInteger
  | SFloat
  | SDouble
  | SBytes
  | SText
  | SList !Schema
  | SArray !(VarInt Int) !Schema -- fixed size
  | SProduct [Schema]
  | SProductFixed [(VarInt Int, Schema)] -- fixed size
  | SRecord [(T.Text, Schema)]
  | SVariant [(T.Text, [Schema])]
  | SFix Schema -- ^ binds a fixpoint
  | SSelf !Word8 -- ^ @SSelf n@ refers to the n-th innermost fixpoint
  deriving (Show, Read, Eq, Generic)
```

The `Serialise` instance is derived by generics.

There are some special schemata:

* `SSchema n` is a schema of schema. The winery library stores the concrete schema of `Schema` for each version, so it can deserialise data even if the schema changes.
* `SFix` binds a fixpoint.
* `SSelf n` refers to the n-th innermost fixpoint bound by `SFix`. This allows it to provide schemata for inductive datatypes.

## Backward compatibility

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

```haskell
  deserialiser = gdeserialiserRecord $ Just $ Foo "" 42 0
```

You can also build a custom deserialiser using the Alternative instance and combinators such as `extractField`, `extractConstructor`, etc.

## 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)

## Benchmark

```haskell
data TestRec = TestRec
  { id_ :: !Int
  , first_name :: !Text
  , last_name :: !Text
  , email :: !Text
  , gender :: !Gender
  , num :: !Int
  , latitude :: !Double
  , longitude :: !Double
  } deriving (Show, Generic)
```

(De)serialisation of the datatype above using generic instances:

```
serialise/list/winery                    mean 847.4 μs  ( +- 122.7 μs  )
serialise/list/binary                    mean 1.221 ms  ( +- 169.0 μs  )
serialise/list/serialise                 mean 290.4 μs  ( +- 34.98 μs  )
serialise/item/winery                    mean 243.1 ns  ( +- 27.50 ns  )
serialise/item/binary                    mean 1.080 μs  ( +- 75.82 ns  )
serialise/item/serialise                 mean 322.4 ns  ( +- 21.09 ns  )
serialise/file/winery                    mean 681.9 μs  ( +- 247.0 μs  )
serialise/file/binary                    mean 1.731 ms  ( +- 611.6 μs  )
serialise/file/serialise                 mean 652.9 μs  ( +- 185.8 μs  )
deserialise/winery                       mean 733.2 μs  ( +- 11.70 μs  )
deserialise/binary                       mean 1.582 ms  ( +- 122.3 μs  )
deserialise/serialise                    mean 823.3 μs  ( +- 38.08 μs  )

```

Not bad, considering that binary and serialise don't encode field names.