Haskell EDN
An EDN parsing and encoding library inspired by aeson
.
Based on spec and hints published on GitHub.
Hackage: https://hackage.haskell.org/package/hedn
Stackage: https://www.stackage.org/package/hedn
Examples
AST
(#haskell/edn example/kitchensink ; tagged symbol
nil ; ugh..
\space ; character
"dynamic \"typing\"" ; string
{:numbers ; keyword
[42 ; integer
42.0 ; floating
] ; Vector
:bools
#{true, false} ; Set (with optional commas)
} ; Map
) ; List
import qualified Data.EDN as EDN
import qualified Data.Text.IO as Text
main = do
edn <- Text.readFile "example.edn"
value <- EDN.parseText "example.edn" edn
print value
NoTag
(List
[ Tagged "haskell" "edn" (Symbol "example" "kitchensink")
, NoTag Nil
, NoTag (Character ' ')
, NoTag (String "dynamic \"typing\"")
, NoTag
(Map
(fromList
[ ( NoTag (Keyword "bools")
, NoTag
(Set (fromList [ NoTag (Boolean False) , NoTag (Boolean True) ]))
)
, ( NoTag (Keyword "numbers")
, NoTag (Vec [ NoTag (Integer 42) , NoTag (Floating 42.0) ])
)
]))
])
Conversion
More examples in tests/Data/EDN/Class/Test.hs
.
data Person = Person
{ personFirst :: Text
, personLast :: Text
} deriving (Eq, Show)
instance ToEDN Person where
toEDN Person{..} =
toEDNtagged "myapp" "Person" $ Map.fromList
[ (EDN.Keyword "first", toEDN personFirst)
, (EDN.Keyword "last", toEDN personLast)
]
instance FromEDN Person where
parseEDN = \case
EDN.Tagged "myapp" "Person" v -> Person
<$> EDN.mapGetKeyword "first" v
<*> EDN.mapGetKeyword "last" v
_ ->
fail "myapp/Person expected"
import qualified Data.EDN as EDN
import qualified Data.Text.IO as Text
main = Text.putStrLn $
encodeText (Person "Fred" "Mertz")
#myapp/Person {:first "Fred" :last "Mertz"}
Quasiquoter
Embed EDN AST literals with edn
:
fredEDN = [edn|
#myapp/Person {:first "Fred" :last "Mertz"}
|]
Additionally there are QQs for untagged collections.
They simply wrap the block in appropriate symbols.
- Lists,
()
EDN.List (items :: [TaggedValue]) =
[ednList|
this is a list of symbols, commas are whitespace
|]
- Vectors,
[]
EDN.Vec (items :: Vector TaggedValue) =
[ednVec|
42 is #the/answer true
|]
- Sets,
#{}
EDN.Set (items :: Set TaggedValue) =
[ednSet|
badger badger badger badger
badger badger badger badger
mushroom mushroom
|]
- Maps,
{}
EDN.Map (dict :: Map TaggedValue TaggedValue) =
[ednMap|
:keywords "as you like it"
or/symbols "I won't judge"
#uuid "d748ab62-9cb1-41fb-b8dc-e23f3ffc5f9b"
(tagged values, collections, anything goes)
|]
EDN as DSL syntax
You can add quasiquoters for your values by specializing fromEDN
QuasiQuoter.
Usual TH shenanigans apply.
Define things in a separate module.
Either Data.Data.Data
or Language.Haskell.TH.Syntax.Lift
instance required to convert values to Haskell AST.
module My.Data.QQ (myData) where
import Data.EDN.QQ (fromEDN)
import My.Data.Types (MyData)
myData :: QuasiQuoter
myData = fromEDN @MyData
module Main where
import My.Data.QQ (myData)
main :: IO ()
main = cobc
[myData|
{:identification-division
[#program/id hello]
:procedure-division
((display "Hello, world!"
end-display)
stop-run
)
}
|]