0

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

type PhonemeClassMap = Map Char String
type ContextElement = Parser String

phonemeContext :: Parsec String PhonemeClassMap ContextElement
phonemeContext = do
    c <- lower
    return $ char c

可以通过将函数提升charParsec/ ParsecTmonad 来简化。

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 ().

4

2 回答 2

3

在这种情况下,您正在寻找单子绑定>>= :: Monad m => (a -> m b) -> m a -> m b,它允许您应用一个函数,该函数接受一个纯函数a并将单子动作返回给单子a(即“通过”单子应用函数)。这个函数实际上是 monadic 类型类的一个组成部分,并且是<-符号dodesugar 到引擎盖下的内容。

(旁注,与liftM2, liftM3... 不同,为了方便起见,似乎没有预定义bindM2 :: Monad m => (a -> b -> m c) -> m a -> m b -> m c(或bindM3等)。(Hoogle 画了一个空白。))

此外,Parsec 解析器通常(在风格上和其他方面)受益于使用其 Applicative 和 Functor 实例,而不仅仅是它的 Monad 实例,特别是( /的<$>别名)和 monadic & :&的各种(半)等价物。fmapliftMap>><*><**>

phonemeContext = char <$> lower

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

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

于 2012-12-14T21:20:21.413 回答
2
modifyState =<< liftM2 insert upper (char ':' >> spaces >> many1 lower)
于 2012-12-14T17:44:24.937 回答