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!")