(Generically) Build Parsers from custom data types?

I am working on a network streaming client that needs to talk to the server. The server encodes the responses in bytes, for example, "1 \ NULJohn \ NULTeddy \ NUL501 \ NUL", where "\ nUL" is the delimiter. The above response translates to "This is a type 1 message (hard-coded by the server) that tells the client what the user ID is (here the user ID is" John Teddy "is" 501 ").

So naively I define a custom data type

data User
  { firstName :: String
  , lastName :: String
  , id :: Int
  }

and analyzer for this data type

parseID :: Parser User
parseID = ...

Then, a handler is simply written to perform some task (for example, writing to the database) after the parser successfully matches such a response. It is very simple.

, 100 , , . , , 100 , , , , haksell . , - , , ?

+4
2

generics-sop . generics-sop Generics .

ReadP, , Applicative . :

{-# language DeriveGeneric #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language TypeFamilies #-}
{-# language DataKinds #-}
{-# language TypeApplications #-} -- for the Proxy

import Text.ParserCombinators.ReadP (ReadP,readP_to_S)
import Text.ParserCombinators.ReadPrec (readPrec_to_P)
import Text.Read (readPrec)
import Data.Proxy
import qualified GHC.Generics as GHC
import Generics.SOP

, Applicative . Int Bool:

class HasSimpleParser c where
    getSimpleParser :: ReadP c

instance HasSimpleParser Int where
    getSimpleParser = readPrec_to_P readPrec 0

instance HasSimpleParser Bool where
    getSimpleParser = readPrec_to_P readPrec 0

, HasSimpleParser:

recParser :: (Generic r, Code r ~ '[xs], All HasSimpleParser xs) => ReadP r
recParser = to . SOP . Z <$> hsequence (hcpure (Proxy @HasSimpleParser) getSimpleParser)

Code r ~ '[xs], All HasSimpleParser xs , " , xs, HasSimpleParser ".

hcpure n- (NP), r. (NP , ReadP).

hsequence, n- n- .

, n- r, to, Z SOP n- , to.


, Generics.SOP.Generic:

data Foo = Foo { x :: Int, y :: Bool } deriving (Show, GHC.Generic)

instance Generic Foo -- Generic from generics-sop

Foo recParser:

main :: IO ()
main = do
    print $ readP_to_S (recParser @Foo) "55False"

[(Foo {x = 55, y = False},"")]
+5

, , : cassava SO , , , , .

{-# 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)

, X, Vector, FromRecord ( parseRecord :: Parser … .

Generic - - haskell . , , ThreadId CSV/JSON , ""!

- .., cassava-conduit, .

+4

Source: https://habr.com/ru/post/1671379/


All Articles