简体   繁体   English

从monad转换为applicative

[英]Translate from monad to applicative

OK, so I know what the Applicative type class contains, and why that's useful. 好的,所以我知道Applicative类包含什么,以及为什么它有用。 But I can't quite wrap my brain around how you'd use it in a non-trivial example. 但是我不能完全围绕你如何在一个非平凡的例子中使用它。

Consider, for example, the following fairly simple Parsec parser: 例如,考虑以下相当简单的Parsec解析器:

integer :: Parser Integer
integer = do
  many1 space
  ds <- many1 digit
  return $ read ds

Now how the heck would you write that without using the Monad instance for Parser ? 现在,如果不使用Monad实例进行Parser ,你会怎么写? Lots of people claim that this can be done and is a good idea, but I can't figure out how exactly. 很多人声称这可以做到并且是一个好主意,但我无法弄清楚究竟是怎么回事。

I'd write 我会写的

integer :: Parser Integer
integer = read <$ many1 space <*> many1 digit

There's a bunch of left associative (like application) parser-building operators <$> , <*> , <$ , <* . 有一堆左关联(如应用程序)解析器构建运算符<$><*><$<* The thing in the far left should be the pure function which assembles the result value from the component values. 最左边的东西应该是纯函数,它从组件值组装结果值。 The thing on the right of each operator should be a parser, collectively giving the components of the grammar left-to-right. 每个运算符右侧的东西应该是一个解析器,从左到右共同给出语法的组成部分。 Which operator to use depends on two choices, as follows. 使用哪个运算符取决于两个选项,如下所示。

  the thing to the right is    signal  / noise
  _________________________            
  the thing to the left is \           
                            +-------------------
                    pure /  |   <$>       <$
                  a parser  |   <*>       <*

So, having chosen read :: String -> Integer as the pure function which is going to deliver the semantics of the parser, we can classify the leading space as "noise" and the bunch of digits as "signal", hence 因此,选择read :: String -> Integer作为将传递解析器语义的纯函数,我们可以将前导空格分类为“noise”,将数字串分类为“signal”,因此

 read <$ many1 space <*> many1 digit
 (..)    (.........)     (.........)
 pure    noise parser     |
 (.................)      |
     parser              signal parser
 (.................................)
                    parser

You can combine multiple possibilities with 您可以将多种可能性结合起来

p1 <|> ... <|> pn

and express impossibility with 并且表达不可能性

empty

It's seldom necessary to name components in parsers, and the resulting code looks more like a grammar with added semantics. 在解析器中命名组件很少是必要的,结果代码看起来更像是带有添加语义的语法。

integer :: Parser Integer
integer = read <$> (many1 space *> many1 digit)

Or 要么

integer = const read <$> many1 space <*> many1 digit

Whether you think either of these are more readable is up to you. 您是否认为其中任何一个更具可读性取决于您。

Your example can be progressively rewritten to a form which more clearly resembles an Applicative: 您的示例可以逐步重写为更明显类似于Applicative的表单:

do
  many1 space
  ds <- many1 digit
  return $ read ds
  1. definition of do notation: do定义:

     many1 space >> (many1 digit >>= \\ds -> return $ read ds) 
  2. definition of $ : $定义:

     many1 space >> (many1 digit >>= \\ds -> return (read ds)) 
  3. definition of . 的定义. :

     many1 space >> (many1 digit >>= (return . read)) 
  4. 3rd monad law (associativity): 第3个monad法律(结社):

     (many1 space >> many1 digit) >>= (return . read) 
  5. definition of liftM (in non- do notation): 的定义liftM (在非do记号):

     liftM read (many1 space >> many1 digit) 

This is (or should be, if I haven't messed up :)) identical in behavior to your example. 这是(或者应该是,如果我没有搞砸:))与你的例子相同的行为。

Now, if you replace liftM with fmap with <$> , and >> with *> , you get the Applicative: 现在,如果你更换liftMfmap<$>>>*>你会得到应用型:

read <$> (many1 space *> many1 digit)

This is valid because liftM , fmap , and <$> are generally supposed to be synonyms, as are >> and *> . 这是有效的,因为liftMfmap<$>通常应该是同义词, >>*>

This all works and we can do this because the original example didn't use the result of any parser to build a following parser. 这一切都有效,我们可以这样做,因为原始示例没有使用任何解析器的结果来构建以下解析器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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