在学习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 ()
。
答案 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)