Applicative constructor for records

I want to generally create applicative constructors for haskell records in order to create a parser for writing.

Consider the entry:

data Record = Record {i :: Int, f :: Float}

the constructor I want:

Record <$> pInt <*> pFloat

Parsers for basic types are given:

class Parseable a where
  getParser :: Parser a

instance Parseable Int where
  getParser = pInt

instance Parseable Float where
  getParser = pFloat

Are there libraries that can already do this? Is it possible to define getParser for a record? Thanks in advance.

+5
source share
2 answers

This can be done using, for example, the regular library . To work with this library, some language extensions are usually required:

{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE TypeFamilies         #-}
{-# LANGUAGE TypeOperators        #-}
{-# LANGUAGE UndecidableInstances #-}

import Control.Applicative
import Generics.Regular

, -- -: ., , uu-parsinglib parsec, , .

newtype Parser a = Parser {runParser :: ReadS a}

instance Functor Parser where
  fmap f p = Parser $ \s -> [(f x, s') | (x, s') <- runParser p s]

instance Applicative Parser where
  pure x  = Parser $ \s -> [(x, s)]
  p <*> q = Parser $ \s ->
    [(f x, s'') | (f, s') <- runParser p s, (x, s'') <- runParser q s']

instance Alternative Parser where
  empty   = Parser $ \_ -> []
  p <|> q = Parser $ \s -> runParser p s ++ runParser q s

( , type ReadS a = String -> [(a, String)].)

pSym :: Char -> Parser Char
pSym c = Parser $ \s -> case s of
  (c' : s') | c == c' -> [(c', s')]
  _                   -> []

pInt :: Parser Int
pInt = Parser reads

pFloat :: Parser Float
pFloat = Parser reads

, :

class Parseable a where
  getParser :: Parser a

instance Parseable Int where
  getParser = pInt

instance Parseable Float where
  getParser = pFloat

, :

data Record = Record {i :: Int, f :: Float}

instance Parseable Record where
  getParser = Record <$> pInt <* pSym ' ' <*> pFloat

, ?

Record ( . regular):

type instance PF Record = K Int :*: K Float

Record type Regular:

instance Regular Record where
  from (Record n r) = K n :*: K r
  to (K n :*: K r)  = Record n r

:

class ParseableF f where
  getParserF :: Parser a -> Parser (f a)

instance ParseableF (K Int) where
  getParserF _ = K <$> pInt

instance ParseableF (K Float) where
  getParserF _ = K <$> pFloat

instance (ParseableF f, ParseableF g) => ParseableF (f :*: g) where
  getParserF p = (:*:) <$> getParserF p <* pSym ' ' <*> getParserF p

( , , .)

, Regular ( ParseableF ) :

instance (Regular a, ParseableF (PF a)) => Parseable a where
  getParser = to <$> getParserF getParser

. Parseable (.. , Int, Float , , Record), . :

> runParser (getParser :: Parser Record) "42 3.14"
[(Record {i = 42, f = 3.14},"")]

. , , . , . . , Template Haskell, Regular . , . .

+9

regular, , ghc-7.2 GHC , Template Haskell, .

, dblhelix, . :

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeOperators #-}

import Control.Applicative
import GHC.Generics

Parser , . Generic Record:

data Record = Record { i :: Int, f :: Float }
  deriving (Generic, Show)

Generic regular. PF regular.

ParseableF Parseable', , :

class Parseable' f where
  getParser' :: Parser (f a)

-- covers base types such as Int and Float:
instance Parseable a => Parseable' (K1 m a) where
  getParser' = K1 <$> getParser

-- covers types with a sequence of fields (record types):
instance (Parseable' f, Parseable' g) => Parseable' (f :*: g) where
  getParser' = (:*:) <$> getParser' <* pSym ' ' <*> getParser'

-- ignores meta-information such as constructor names or field labels:
instance Parseable' f => Parseable' (M1 m l f) where
  getParser' = M1 <$> getParser'

, Parseable :

class Parseable a where
  getParser :: Parser a
  default getParser :: (Generic a, Parseable' (Rep a)) => Parser a
  getParser = to <$> getParser'

instance Parseable Int where
  getParser = pInt

instance Parseable Float where
  getParser = pFloat

Record , :

instance Parseable Record

:

> runParser (getParser :: Parser Record) "42 3.14"
[(Record {i = 42, f = 3.14},"")]
+3

All Articles