繁体   English   中英

如何在不假设Monad的情况下为解析器实现Applicative实例?

[英]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 <*> yy的输入。 这正是Monad mApplicative m之间的区别:

  • Monad您可以使用一个计算的输出作为另一个计算的输入。
  • Applicative ,你不能。

如果你做一个有趣的伎俩是可能的:

Parser x <*> Parser y = Parser $
    \s -> (\(_, xv) (s', yv) -> (s', xv yv)) <$> x s <*> y s

但是,这几乎肯定不是您想要的定义,因为它并行解析xy

修复

  1. 您的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实例。

  2. 您可以在解析器外移动剩余的字符:

     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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM