2013-02-27 58 views
18

好了,我知道是什么Applicative类型类包含的,为什么这是有用的。但我不能完全围绕如何在一个不平凡的例子中使用它。从单子转换到应用性

考虑,例如,下列相当简单秒差距解析器:

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

现在如何赫克你会写,而无需使用Monad实例Parser?很多人声称这是可以做到的,并且是一个好主意,但我无法弄清楚究竟如何。

回答

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

或者

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

不管你认为其中任一更易读是你。

+0

为什么'const'? – MathematicalOrchid 2013-02-28 19:30:29

+1

我们希望忽略'many1 space'的值(但不是效果),并将'read'应用于'many1 digit'的值。 (对不起,我刚刚进来,已经很晚了,我很疲倦:我用术语玩得很快而且松散。)如果你想象's'和'd'代表'many1 space'和' many1 digit',那么'const读取的值(忽略效果)<$> many1 space <*> many1 digit'是'const read sd' ='read d'。 – dave4420 2013-02-28 22:33:11

38

我会写

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作为将要递送解析器的语义的纯函数,我们可以前导间隔作为“噪声”和数字的一串为“信号”进行分类,因此

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

您可以

p1 <|> ... <|> pn 

和快速不可能结合的多种可能性与

empty 

很少有必要在解析器中命名组件,并且生成的代码看起来更像是带有添加语义的语法。

+8

哇,我知道'<$',但我只用过它,如果它左边的东西是一个常量,右边是一个简单的值...我从来没有想过如果我把一个函数左边:P好手戏 – 2013-02-27 23:10:40

7

你的例子可以逐步改写为其中更清楚地类似于一个应用型的一种形式:

do 
    many1 space 
    ds <- many1 digit 
    return $ read ds 
定义的
  1. do符号:

    定义的$
    many1 space >> (many1 digit >>= \ds -> return $ read ds) 
    
  2. many1 space >> (many1 digit >>= \ds -> return (read ds)) 
    
  3. 定义的.

    many1 space >> (many1 digit >>= (return . read)) 
    
  4. 第三单子法(结合性):

    定义的liftM
    (many1 space >> many1 digit) >>= (return . read) 
    
  5. (在非do表示法):

    liftM read (many1 space >> many1 digit) 
    

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

,如果更换liftMfmap<$>,并>>*>,你会得到应用型:

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

这是有效的,因为liftMfmap<$>一般应该是同义词,如是>>*>

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

+0

酷!另一种方式来编写'读取<$ many1 space <*> many1 digit'。 :)最后一句话非常重要。这是否意味着这种风格与上下文无关的语法相对应,而更一般的语法必须用单体风格进行分析? – 2013-03-05 14:52:35

+0

@WillNess我不是这方面的专家,但我确实相信是这样。 – 2013-03-05 19:38:31