Maa*_*ten 5 haskell applicative
我想一般为haskell记录创建应用程序构造函数,以便为记录创建解析器.
考虑记录:
data Record = Record {i :: Int, f :: Float}
Run Code Online (Sandbox Code Playgroud)
我想要的构造函数:
Record <$> pInt <*> pFloat
Run Code Online (Sandbox Code Playgroud)
给出了基本类型的解析器:
class Parseable a where
getParser :: Parser a
instance Parseable Int where
getParser = pInt
instance Parseable Float where
getParser = pFloat
Run Code Online (Sandbox Code Playgroud)
有没有可以做到这一点的图书馆?是否可以为记录定义getParser?提前致谢.
这可以使用例如常规库来完成.使用此库通常需要一些语言扩展:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
import Control.Applicative
import Generics.Regular
Run Code Online (Sandbox Code Playgroud)
至少有两个最流行的解析器 - 组合器库带有一个applicative-functor接口:例如,参见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
Run Code Online (Sandbox Code Playgroud)
(注意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
Run Code Online (Sandbox Code Playgroud)
直截了当,我们有:
class Parseable a where
getParser :: Parser a
instance Parseable Int where
getParser = pInt
instance Parseable Float where
getParser = pFloat
Run Code Online (Sandbox Code Playgroud)
并且,对于您的记录类型,根据需要:
data Record = Record {i :: Int, f :: Float}
instance Parseable Record where
getParser = Record <$> pInt <* pSym ' ' <*> pFloat
Run Code Online (Sandbox Code Playgroud)
现在,我们如何通常生成这样的解析器?
首先,我们定义所谓的模式函子Record(详见常规文档):
type instance PF Record = K Int :*: K Float
Run Code Online (Sandbox Code Playgroud)
然后,我们创建Record一个类型类的实例Regular:
instance Regular Record where
from (Record n r) = K n :*: K r
to (K n :*: K r) = Record n r
Run Code Online (Sandbox Code Playgroud)
接下来,我们定义一个通用解析器:
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
Run Code Online (Sandbox Code Playgroud)
(要涵盖所有常规类型,您必须提供更多实例,但这些将适用于您的示例.)
现在,我们可以证明类中的每个类型Regular(给定ParseableF其模式函子的实例)都带有一个解析器:
instance (Regular a, ParseableF (PF a)) => Parseable a where
getParser = to <$> getParserF getParser
Run Code Online (Sandbox Code Playgroud)
让我们来看看吧.删除原始实例Parseable(即,for Int,Float当然Record),并保留单个通用实例.开始了:
> runParser (getParser :: Parser Record) "42 3.14"
[(Record {i = 42, f = 3.14},"")]
Run Code Online (Sandbox Code Playgroud)
注意:这只是如何使用常规库派生通用解析器的一个非常基本的示例.该库本身带有一个通用的成功列表解析器,它对记录做了特别好的事情.你可能想先检查一下.此外,该库附带了Template Haskell支持,因此Regular可以自动派生实例.这些实例包括记录标签的特殊结构类型,因此您可以使用通用函数来处理记录类型.查看文档.
| 归档时间: |
|
| 查看次数: |
1189 次 |
| 最近记录: |