2013-02-27 28 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