Accessible format for structured data serialization.
Serialization of data structures to a Prolog-like Herbrandt-universum-style universal format, useful for type-tagged things and various intermediate representations of program code. The text representation is very simple, allowing interoperability with various minimalistic programming systems.
herb
Herbrandt-universum-style serialization of Haskell values.
- Like Aeson, but all structures have heads!
- Like Show, but no precedence issues or ties to Haskell syntax!
- Like Prolog, but capital case doesn't mean variables!
The grammar is super-simple to allow easy interoperability between programs.
Example
Let's make a small language:
{-# LANGUAGE -XOverloadedStrings #-}
{-# LANGUAGE -XDeriveGeneric #-}
{-# LANGUAGE -XDeriveAnyClass #-}
import Data.Herb
import Data.Herb.Instances
data Expr
= Number Int
| Var Ident
| Apply Expr Expr
deriving (Show, Generic, FromHerb, ToHerb)
(Some more general typeclasses are provided too, mainly the deriving via
wrapper for using Show
, auto-deriving from Generic
as used above, and HKD support with FromHerb1
and ToHerb1
.)
We can convert the data to Herb representation and serialize them:
>>> encodeHerb $ Apply (Var "sqrt") (Number 5)
"Apply(Var(sqrt),Number(5))"
There's a prettyprinter for showing unwieldy values like this symbolic representation of foldr (+) 0 [1..5]
:
>>> encodeHerb $ foldr (Apply . Apply (Var "add") . Number) (Number 0) [1..5]
"Apply(Apply(Var(add),Number(1)),Apply(Apply(Var(add),Number(2)),Apply(Apply(Var(add),Number(3)),Apply(Apply(Var(add),Number(4)),Apply(Apply(Var(add),Number(5)),Number(0))))))"
...piping the above through herb-format
gives a relatively readable output:
Apply(Apply(Var(add), Number(1)),
Apply(Apply(Var(add), Number(2)),
Apply(Apply(Var(add), Number(3)),
Apply(Apply(Var(add), Number(4)),
Apply(Apply(Var(add), Number(5)), Number(0))))))
There's also an Attoparsec-based parser (the parsers and helper functions are in Data.Herb.Parser
):
>>> import Data.Herb.Parser
>>> import Data.Attoparsec.Text
>>> parseOnly herbFile "stuff(this,that) \n stuff(others)"
Right [Struct "stuff" [Atom "this",Atom "that"],Struct "stuff" [Atom "others"]]
...together with a parsing shortcut:
>>> decodeHerb "Apply(Var(sqrt),Number(2))" :: Either String Expr
Right (Apply (Var "sqrt") (Number 2))
And that's it; this isn't supposed to be complex.