(Generically) Build Parsers from custom data types?

后端 未结 2 902
梦如初夏
梦如初夏 2021-01-19 10:54

I\'m working on a network streaming client that needs to talk to the server. The server encodes the responses in bytestrings, for example, \"1\\NULJohn\\NULTeddy\\NUL501\\NU

2条回答
  •  天涯浪人
    2021-01-19 11:50

    You can write your own parser - but there is already a package that can do the parsing for you: cassava and while SO is usually not a place to search for library recommendations, I want to include this answer for people looking for a solution, but not having the time to implement this themselves and looking for a solution that works out of the box.

    {-# LANGUAGE DeriveGeneric #-}
    {-# LANGUAGE OverloadedStrings #-}
    
    import Data.Csv
    import Data.Vector
    import Data.ByteString.Lazy as B
    import GHC.Generics
    
    data Person = P { personId :: Int
                    , firstName :: String
                    , lastName :: String
                    } deriving (Eq, Generic, Show)
    
     -- the following are provided by friendly neighborhood Generic
    instance FromRecord Person
    instance ToRecord Person
    
    main :: IO ()
    main = do B.writeFile "test" "1\NULThomas\NULof Aquin"
              Right thomas <- decodeWith (DecodeOptions 0) NoHeader <$> 
                                  B.readFile "test"
    
              print (thomas :: Vector Person)
    

    Basically cassava allows you to parse all X-separated structures into a Vector, provided you can write down a FromRecord instance (which needs a parseRecord :: Parser … function to work.

    Side note on Generic until recently I thought - EVERYTHING - in haskell has a Generic instance, or can derive one. Well this is not the case I wanted to serialize some ThreadId to CSV/JSON and happened to find out unboxed types are not so easily "genericked"!

    And before I forget it - when you speak of streaming and server and so on there is cassava-conduit that might be of help.

提交回复
热议问题