从monad转换为applicative

Mat*_*hid 19 monads haskell applicative

好的,所以我知道Applicative类型类包含什么,以及为什么它有用.但是我不能完全围绕你如何在一个非平凡的例子中使用它.

例如,考虑以下相当简单的Parsec解析器:

integer :: Parser Integer
integer = do
  many1 space
  ds <- many1 digit
  return $ read ds
Run Code Online (Sandbox Code Playgroud)

现在,如果不使用Monad实例,你会怎么写Parser?很多人声称这可以做到并且是个好主意,但我无法弄清楚究竟是怎么回事.

pig*_*ker 39

我会写的

integer :: Parser Integer
integer = read <$ many1 space <*> many1 digit
Run Code Online (Sandbox Code Playgroud)

有一堆左结合(如应用程序)语法分析器建设运营的<$>,<*>,<$,<*.最左边的东西应该是纯函数,它从组件值组装结果值.每个运算符右侧的东西应该是一个解析器,从左到右共同给出语法的组成部分.使用哪个运算符取决于两个选项,如下所示.

  the thing to the right is    signal  / noise
  _________________________            
  the thing to the left is \           
                            +-------------------
                    pure /  |   <$>       <$
                  a parser  |   <*>       <*
Run Code Online (Sandbox Code Playgroud)

因此,选择read :: String -> Integer作为将传递解析器语义的纯函数,我们可以将前导空格分类为"噪声",将数字串分类为"信号",因此

 read <$ many1 space <*> many1 digit
 (..)    (.........)     (.........)
 pure    noise parser     |
 (.................)      |
     parser              signal parser
 (.................................)
                    parser
Run Code Online (Sandbox Code Playgroud)

您可以将多种可能性结合起来

p1 <|> ... <|> pn
Run Code Online (Sandbox Code Playgroud)

并且表达不可能性

empty
Run Code Online (Sandbox Code Playgroud)

在解析器中命名组件很少是必要的,结果代码看起来更像是带有添加语义的语法.

  • 哇,我知道`<$`,但我只使用它,如果它左边的东西是一个常数而右边是一个简单的值...我从未想过如果我把一个函数放到哪里会发生什么左边:P好戏 (8认同)

dav*_*420 11

integer :: Parser Integer
integer = read <$> (many1 space *> many1 digit)
Run Code Online (Sandbox Code Playgroud)

要么

integer = const read <$> many1 space <*> many1 digit
Run Code Online (Sandbox Code Playgroud)

您是否认为其中任何一个更具可读性取决于您.


Mat*_*ick 8

您的示例可以逐步重写为更明显类似于Applicative的表单:

do
  many1 space
  ds <- many1 digit
  return $ read ds
Run Code Online (Sandbox Code Playgroud)
  1. do符号的定义:

    many1 space >> (many1 digit >>= \ds -> return $ read ds)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 定义$:

    many1 space >> (many1 digit >>= \ds -> return (read ds))
    
    Run Code Online (Sandbox Code Playgroud)
  3. 定义.:

    many1 space >> (many1 digit >>= (return . read))
    
    Run Code Online (Sandbox Code Playgroud)
  4. 第3个monad法(结社):

    (many1 space >> many1 digit) >>= (return . read)
    
    Run Code Online (Sandbox Code Playgroud)
  5. liftM(非do表示法)的定义:

    liftM read (many1 space >> many1 digit)
    
    Run Code Online (Sandbox Code Playgroud)

这是(或者应该是,如果我没有搞砸:))与你的例子相同的行为.

现在,如果你更换liftMfmap使用<$>,以及>>*>,你会得到应用型:

read <$> (many1 space *> many1 digit)
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为liftM,fmap<$>,通常应该是同义词,因为>>*>.

这一切都有效,我们可以这样做,因为原始示例没有使用任何解析器的结果来构建以下解析器.