是否可以chainl1
不使用 Parsec 定义的 Monad 实例来表达 Parsec 的组合子?
chainl1 p op =
do x <- p
rest x
where
rest x = do f <- op
y <- p
rest (f x y)
<|> return x
是的:
chainl1 p op = foldl (flip ($)) <$> p <*> many (flip <$> op <*> p)
这个想法是您必须将其解析p (op p)*
和评估为(...(((p) op p) op p)...)
.
稍微扩展一下定义可能会有所帮助:
chainl1 p op = foldl (\x f -> f x) <$> p <*> many ((\f y -> flip f y) <$> op <*> p)
op
由于和的对p
被解析,结果被立即应用,但因为p
是 的右操作数op
,它需要一个flip
。
所以,结果类型many (flip <$> op <*> p)
是f [a -> a]
。然后将这个函数列表从左到右应用于p
by的初始值foldl
。
丑陋但等效的Applicative
定义:
chainl1 p op =
p <**>
rest
where
rest = flip <$> op <*>
p <**>
pure (.) <*> rest
<|> pure id
而不是将左侧参数x
显式传递到右侧op
,这种应用形式“链”通过提升的组合op
器部分应用于其右侧参数(因此) ,然后将最左侧的通孔应用于结果。flip <$> op <*> p
(.)
p
(<**>)
rest :: Alternative f => f (a -> a)