如何简化修改用户状态的Parsec规则

时间:2012-12-14 17:39:19

标签: haskell monads parsec

在学习Parsec时,我发现了一些有些冗长的规则,比如

type PhonemeClassMap = Map Char String
type ContextElement = Parser String

phonemeContext :: Parsec String PhonemeClassMap ContextElement
phonemeContext = do
    c <- lower
    return $ char c
通过将char等函数提升到Parsec / ParsecT monad中,可以简化

phonemeContext :: Parsec String PhonemeClassMap ContextElement
phonemeContext = liftM char lower

现在我正在尝试简化修改用户状态的规则:

import Data.Map (insert)

phonemeClassDefinition :: Parsec String PhonemeClassMap ()
phonemeClassDefinition = do
    upperChar <- upper
    lowerChars <- char ':' >> spaces >> many1 lower
    modifyState (insert upperChar lowerChars)

我可以轻松举起insert :: Char -> String -> PhonemeClassMap -> PhonemeClassMap进行以下改进:

phonemeClassDefinition = do
    f <- liftM2 insert upper (char ':' >> spaces >> many1 lower)
    modifyState f

有没有办法将这两个表达式说成一个?相同的提升技术不适用于modifyState :: Monad m -> (u -> u) -> ParsecT s u m ()

2 个答案:

答案 0 :(得分:3)

在这种情况下,您正在寻找monadic bind >>= :: Monad m => (a -> m b) -> m a -> m b,它允许您应用一个带有纯a的函数并将monadic动作返回到monadic a(即apply功能“通过”monad)。这个函数实际上是monadic类型类的一个组成部分,它是<-符号中do符号的内容。

(旁注,与liftM2不同,liftM3 ...,为方便起见,似乎没有预定义的bindM2 :: Monad m => (a -> b -> m c) -> m a -> m b -> m c(或bindM3等)。({ {3}}))

此外,Parsec解析器通常(使用其风格和其他方式)使用其Applicative和Functor实例,而不仅仅是其Monad实例,特别是<$>fmap / liftM的别名)和各种(半)等价的monadic ap&amp; >>Hoogle draws a blank

phonemeContext = char <$> lower

phonemeClassDefinition = (insert <$> upper <*> (char ':' *> spaces *> many1 lower)) >>= modifyState

(请注意,@ melpomene的=<<只是flip (>>=),即交换了参数。)

答案 1 :(得分:2)

modifyState =<< liftM2 insert upper (char ':' >> spaces >> many1 lower)