[英]How do I implement an Applicative instance for a parser without assuming Monad?
我无法弄清楚如何为此解析器实现Applicative
实例:
newtype Parser m s a = Parser { getParser :: [s] -> m ([s], a) }
没有假设Monad m
。 我希望只需要假设Applicative m
,因为Functor
实例只需要假设Functor m
。 我终于结束了:
instance Functor m => Functor (Parser m s) where
fmap f (Parser g) = Parser (fmap (fmap f) . g)
instance Monad m => Applicative (Parser m s) where
pure a = Parser (\xs -> pure (xs, a))
Parser f <*> Parser x = Parser h
where
h xs = f xs >>= \(ys, f') ->
x ys >>= \(zs, x') ->
pure (zs, f' x')
我该怎么做呢? 我尝试用手代替in >>=
,但总是因为尝试减少join
而陷入困境 - 这也需要Monad
。
我也咨询了Parsec ,但即使这样也没多大帮助:
instance Applicative.Applicative (ParsecT s u m) where
pure = return
(<*>) = ap
我提出这个问题的原因纯粹是自我教育。
这是不可能的。 看看你的内部newtype
:
getParser :: [s] -> m ([s], a)
据推测,您希望将[s]
传递给x <*> y
的y
的输入。 这正是Monad m
和Applicative m
之间的区别:
Monad
您可以使用一个计算的输出作为另一个计算的输入。 Applicative
,你不能。 如果你做一个有趣的伎俩是可能的:
Parser x <*> Parser y = Parser $
\s -> (\(_, xv) (s', yv) -> (s', xv yv)) <$> x s <*> y s
但是,这几乎肯定不是您想要的定义,因为它并行解析x
和y
。
您的ParserT
可以很容易地Applicative
:
newtype ParserT msa = ParserT { runParser :: [s] -> m ([s], a) } -- or, equvalently newtype ParserT msa = ParserT (StateT [s] ma) instance Monad m => Applicative (ParserT ms) where ...
需要注意的是ParserT ms
不是一个实例Monad
,只要你不定义Monad
实例。
您可以在解析器外移动剩余的字符:
newtype ParserT msa = ParserT { runParser :: [s] -> ([s], ma) } instance Applicative m => Applicative (ParserT ms) where ParserT x <*> ParserT y = ParserT $ \\s -> let (s', x') = xs (s'', y') = ys' in x' <*> y' ...
旨在尽可能使用Applicative的满分 - 它更清洁。
标题: 您的解析器可以保持适用,但您可能的解析集合需要存储在Monad中。 内部结构:使用monad。 外部结构:适用。
你使用m ([s],a)
来表示一堆可能的解析。 解析下一个输入时,您希望它依赖于已经解析的内容,但是您使用的是m
因为可能有少于或多于一个可能的解析; 你想做\\([s],a) -> ...
并使用它来制作一个新的m ([s],a)
。 该过程称为绑定并使用>>=
或等效,因此您的容器绝对是Monad,无法逃脱。
使用monad作为你的容器并不是那么糟糕 - 它只是一个容器,你毕竟保留了一些东西。 内部使用monad和monad之间有区别。 在使用monad时,你的解析器可以适用。
请参阅应用解析比monadic解析有什么好处? 。
如果你的解析器是适用的,它们就更简单了,所以理论上你可以在组合它们时做一些优化,保留静态信息,而不是保持它们的实现 。 例如,
string "Hello World!" <|> string "Hello Mum!"
== (++) <$> string "Hello " <*> (string "World" <|> string "Mum!")
第二个版本比第一个版本更好,因为它没有回溯。
如果你做了很多这样的事情,就像在正常表达式运行之前编译它一样,创建一个图形(有限状态自动机)并尽可能地简化它并消除一大堆低效的回溯。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.