这可以使用例如regular库来完成。与此库的工作通常需要一些语言扩展:
{-# 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
类型类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},"")]
注:这只是一个如何得到使用常规库通用解析器非常简单的例子。图书馆本身带有一个generic list-of-successes parser,记录特别好。你可能想先检查一下。此外,该库还提供了模板Haskell支持,以便可以自动导出Regular
的实例。这些实例包括记录标签的特殊结构类型,以便您可以让您的泛型函数将记录类型处理为真正的幻想。查看文档。
只是为了澄清:你想为'可解析Record'一个实例为您生成? – kosmikus 2012-07-10 15:35:08