![](/img/trans.png)
[英]Proving equivalence of sequence definitions from Applicative and Monad
[英]Translate from monad to applicative
好的,所以我知道Applicative
类包含什么,以及为什么它有用。 但是我不能完全围绕你如何在一个非平凡的例子中使用它。
例如,考虑以下相当简单的Parsec解析器:
integer :: Parser Integer
integer = do
many1 space
ds <- many1 digit
return $ read ds
现在,如果不使用Monad
实例进行Parser
,你会怎么写? 很多人声称这可以做到并且是一个好主意,但我无法弄清楚究竟是怎么回事。
我会写的
integer :: Parser Integer
integer = read <$ many1 space <*> many1 digit
有一堆左关联(如应用程序)解析器构建运算符<$>
, <*>
, <$
, <*
。 最左边的东西应该是纯函数,它从组件值组装结果值。 每个运算符右侧的东西应该是一个解析器,从左到右共同给出语法的组成部分。 使用哪个运算符取决于两个选项,如下所示。
the thing to the right is signal / noise
_________________________
the thing to the left is \
+-------------------
pure / | <$> <$
a parser | <*> <*
因此,选择read :: String -> Integer
作为将传递解析器语义的纯函数,我们可以将前导空格分类为“noise”,将数字串分类为“signal”,因此
read <$ many1 space <*> many1 digit
(..) (.........) (.........)
pure noise parser |
(.................) |
parser signal parser
(.................................)
parser
您可以将多种可能性结合起来
p1 <|> ... <|> pn
并且表达不可能性
empty
在解析器中命名组件很少是必要的,结果代码看起来更像是带有添加语义的语法。
integer :: Parser Integer
integer = read <$> (many1 space *> many1 digit)
要么
integer = const read <$> many1 space <*> many1 digit
您是否认为其中任何一个更具可读性取决于您。
您的示例可以逐步重写为更明显类似于Applicative的表单:
do
many1 space
ds <- many1 digit
return $ read ds
do
定义:
many1 space >> (many1 digit >>= \\ds -> return $ read ds)
$
定义:
many1 space >> (many1 digit >>= \\ds -> return (read ds))
的定义.
:
many1 space >> (many1 digit >>= (return . read))
第3个monad法律(结社):
(many1 space >> many1 digit) >>= (return . read)
的定义liftM
(在非do
记号):
liftM read (many1 space >> many1 digit)
这是(或者应该是,如果我没有搞砸:))与你的例子相同的行为。
现在,如果你更换liftM
与fmap
与<$>
和>>
与*>
你会得到应用型:
read <$> (many1 space *> many1 digit)
这是有效的,因为liftM
, fmap
和<$>
通常应该是同义词, >>
和*>
。
这一切都有效,我们可以这样做,因为原始示例没有使用任何解析器的结果来构建以下解析器。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.