Parsing 如何在不使用Monad的情况下实现解析器的应用程序实例?

Parsing 如何在不使用Monad的情况下实现解析器的应用程序实例?,parsing,haskell,applicative,Parsing,Haskell,Applicative,我不知道如何为这个解析器实现一个Applicative实例: newtype Parser m s a = Parser { getParser :: [s] -> m ([s], a) } 不假设单子m。我希望只需要假设Applicative m,因为Functor实例只需要假设Functor m。我最终得到了: instance Functor m => Functor (Parser m s) where fmap f (Parser g) = Parser (fmap

我不知道如何为这个解析器实现一个
Applicative
实例:

newtype Parser m s a = Parser { getParser :: [s] -> m ([s], a) }
不假设
单子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')

我问这个问题的原因纯粹是自我教育。

这是不可能的。查看
newtype的内部:

getParser :: [s] -> m ([s], a)
您可能希望将
[s]
传递到
xy
中的
y
的输入。这正是
Monad m
Applicative m
之间的区别:

  • Monad
    中,您可以使用一个计算的输出作为另一个计算的输入
  • Applicative
    中,您不能
如果你做一个有趣的把戏,这是可能的:

Parser x <*> Parser y = Parser $
    \s -> (\(_, xv) (s', yv) -> (s', xv yv)) <$> x s <*> y s
请注意,
ParserT ms
不是
Monad
的实例,只要不定义
Monad
实例

  • 您可以将剩余字符移到解析器之外:

    newtype ParserT m s a = ParserT { runParser :: [s] -> ([s], m a) }
    
    instance Applicative m => Applicative (ParserT m s) where
        ParserT x <*> ParserT y = ParserT $ \s ->
            let (s', x') = x s
                (s'', y') = y s'
            in x' <*> y'
        ...
    
    newtype ParserT m s a=ParserT{runParser::[s]>([s],m a)}
    实例Applicative m=>Applicative(ParserT m s),其中
    ParserT x ParserT y=ParserT$\s->
    设(s',x')=xs
    (s'',y')=y's'
    在x'y'
    ...
    

  • 以尽可能多地使用Applicative为目标的满分-它更干净

    标题:您的解析器可以保持应用性,但可能的解析集合需要存储在Monad中。内部结构:使用Monad。外部结构:适用性强

    您正在使用
    m([s],a)
    来表示一系列可能的解析。当您解析下一个输入时,您希望它取决于已经解析的内容,但您使用的是
    m
    ,因为可能存在少于或多于一个的解析;您需要执行
    \([s],a)->…
    ,并使用它创建一个新的
    m([s],a)
    。这个过程称为绑定,并使用
    >=
    或等效程序,因此您的容器肯定是单子,没有转义

    在你的容器中使用monad并不是那么糟糕,毕竟它只是一个你保存了一些东西的容器。在内部使用monad和作为monad是有区别的。您的解析器可以在内部使用monad时应用

    如果你的解析器是实用的,那么它们会更简单,所以理论上你可以在组合它们的时候做一些优化,方法是保留关于它们做什么的静态信息,而不是保留它们的实现。比如说,

    string "Hello World!" <|> string "Hello Mum!"
    == (++) <$> string "Hello " <*> (string "World" <|> string "Mum!")
    
    string“你好,世界!”string“你好,妈妈!”
    ==(++)字符串“你好”(字符串“世界”字符串“妈妈!”)
    
    第二个版本比第一个版本好,因为它没有回溯


    如果您经常这样做,就像在运行正则表达式之前编译正则表达式一样,创建一个图(有限状态自动机)并尽可能地简化它,消除整个低效回溯负载。

    所以完全不可能编写任何应用程序解析器?(当然,除非它也是一个单子)也就是说,这不仅仅是因为我选择了一个糟糕的类型而不可能,是吗?@MattFenwick:完全可以编写一个应用程序解析器。解析器是否适用的问题与
    m
    是否适用的问题不同。请记住,
    m
    不是您的解析器,
    m
    的类可能与
    ParserT
    的类关系不大。我想我很困惑,b/c第一个示例仍然依赖于单子,这是我试图避免的,而第二个示例有不同的类型???@MattFenwick:您要求了一些不可能的东西,所以“修复”我们必须改变问题的参数,直到有可能回答为止。记住:如果你想使用一个
    ma
    计算的输出作为另一个
    a->mb
    计算的输入,你需要一个Monad,因为这基本上就是Monad的定义。因此,修复方法是允许
    Monad m
    上下文,或者更改解析器的定义,这样您就不需要从一元计算中提取值。@MattFenwick第二种方法假设所有可能的解析器使用的令牌数量完全相同。第一种方法必须使用monad来避免这种假设。DeitrichEpp是正确的——您在内部需要monad,这并不使解析器成为monadic,而只是其实现的一部分。内部数据是一元的,但更高级的结构是可应用的。
    newtype ParserT m s a = ParserT { runParser :: [s] -> ([s], m a) }
    
    instance Applicative m => Applicative (ParserT m s) where
        ParserT x <*> ParserT y = ParserT $ \s ->
            let (s', x') = x s
                (s'', y') = y s'
            in x' <*> y'
        ...
    
    string "Hello World!" <|> string "Hello Mum!"
    == (++) <$> string "Hello " <*> (string "World" <|> string "Mum!")