這可以使用例如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