bidirectional: Simple bidirectional serialization and deserialization

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

Bidirectional serialization based on

[Skip to Readme]


Change log
Dependencies base (>= && <4.14), profunctors (>=5 && <5.6) [details]
License BSD-3-Clause
Author Mats Rauhala
Category Codec
Bug tracker
Source repo head: git clone
Uploaded by MasseR at 2020-10-07T19:21:18Z


[Index] [Quick Jump]


Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Readme for bidirectional-

[back to package description]

Bidirectional - bidirectional parsing

This library is based on the blog by Lysxia.

In it they define a bidirectional parser that can both generate and consume values.

Imagine that you have a record Person and you want to serialize it into a list and deserialize it back.

data Person = Person { name :: String, age :: Int }

The parser is parameterized over the parsing and generation contexts, these needs to be selected carefully when implementing the parsers. It could be for example a RowParser Writer [SQLData] combination for sqlite-simple, or a simple state and writer for our running example.

So, let's figure out the context for our example. We want to encode into a list and then decode the list back into a value.

type SimpleParser a = IParser (StateT [String] Maybe) (Writer [String]) a a

Then we create the parsers using the parser function.

int :: SimpleParser Int
int =
    (StateT $ \(x:xs) -> (,xs) <$> readMaybe x)
    (\x -> x <$ tell [show x])

string :: SimpleParser String
string =
    (StateT $ \(x:xs) -> Just (x,xs))
    (\x -> x <$ tell [x])

One small detail for creating records, is that the encoder gets the full record structure by default, so you need to focus in on a specific part of the record using the (.=) function.

person :: SimpleParser Person
person =
  Person <$> name .= string
         <*> age .= int

And now you can use your encoders and decoders

> execWriter (encode person (Person "foo" 30))
["foo", "30"]

> evalStateT (decode person ["foo", "30"])
Just (Person { name = "foo", age = 30 })