Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

49 Commits

Repository files navigation

Codec Build StatusHackage

Codec makes it simple to write composable bidirectional serializers with a consistent interface.

Just define your data type normally:

dataRecordB=RecordB{recordBString::String
, recordBDouble::Double}deriving (Eq, Ord, Show)

and then associate each field with a codec using the =. operator:

recordBObjCodec::JSONCodecRecordB
recordBObjCodec = asObject "RecordB"$RecordB<$> recordBString =. field "string"<*> recordBDouble =. field "double"

That's it! If you want, you can now define ToJSON and FromJSON instances, or just use it directly:

instanceToJSONRecordBwhere
toJSON = toJSONCodec recordBObjCodec
toEncoding = toEncodingCodec recordBObjCodec
instanceFromJSONRecordBwhere
parseJSON = parseJSONCodec recordBObjCodec

Support can be added for almost any serialization library, but aeson and binary support are included.

JSON example:

dataRecordA=RecordA{recordAInt::Int
, recordANestedObj::RecordB
, recordANestedArr::RecordB
, recordANestedObjs:: [ RecordB ]
}deriving (Eq, Ord, Show)
dataRecordB=RecordB{recordBString::String
, recordBDouble::Double}deriving (Eq, Ord, Show)
recordACodec::JSONCodecRecordA
recordACodec = asObject "RecordA"$RecordA<$> recordAInt =. field "int"<*> recordANestedObj =. field' "nestedObj" recordBObjCodec
<*> recordANestedArr =. field' "nestedArr" recordBArrCodec
<*> recordANestedObjs =. field' "nestedObjs" (arrayOf' idid recordBObjCodec)
recordBObjCodec::JSONCodecRecordB
recordBObjCodec = asObject "RecordB"$RecordB<$> recordBString =. field "string"<*> recordBDouble =. field "double"-- serialize to array elementsrecordBArrCodec::JSONCodecRecordB
recordBArrCodec = asArray "RecordB"$RecordB<$> recordBString =. element
<*> recordBDouble =. element

Binary example:

dataRecordA=RecordA{recordAInt64::Int64
, recordAWord8::Word8
, recordANestedB::RecordB}deriving (Eq, Ord, Show)
dataRecordB=RecordB{recordBWord16::Word16
, recordBByteString64::BS.ByteString}deriving (Eq, Ord, Show)
recordACodec::BinaryCodecRecordA
recordACodec =RecordA<$> recordAInt64 =. int64le
<*> recordAWord8 =. word8
<*> recordANestedB =. recordBCodec
recordBCodec::BinaryCodecRecordB
recordBCodec =RecordB<$> recordBWord16 =. word16host
<*> recordBByteString64 =. byteString 64

A Codec is just a combination of a deserializer r a, and a serializer c -> w a.

dataCodecForrwca=Codec{codecIn::ra
, codecOut::c->wa}typeCodecrwa=CodecForrwaa

With binary for example, r is Get and w is PutM. The reason we have an extra parameter c is so that we can associate a Codec with a particular field using the =. operator:

(=.) :: (c' -> c) -> CodecFor r w c a -> CodecFor r w c' a

Codec is an instance of Functor, Applicative, Monad and Profunctor. You can serialize in any order you like, regardless of field order in the data type:

recordBCodecFlipped::BinaryCodecRecordB
recordBCodecFlipped =do
bs64 <- recordBByteString64 =. byteString 64RecordB<$> recordBWord16 =. word16host
<*>pure bs64

Contributors

=. operator and Profunctor approach thanks to Xia Li-yao

About

Easy bidirectional serialization in Haskell

Resources

Stars

49 stars

Watchers

6 watching

Forks

Releases

Packages

Contributors

Languages