2013-02-27 18 views
18

OK, quindi so cosa contiene la classe di tipo Applicative e perché è utile. Ma non riesco a capire il modo in cui lo useresti in un esempio non banale.Traduci da monad a applicativo

Si consideri, per esempio, il seguente abbastanza semplice parser Parsec:

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

Ora come diavolo avrebbe si scrive che senza usare l'istanza Monad per Parser? Molte persone affermano che ciò può essere fatto ed è una buona idea, ma non riesco a capire come esattamente.

risposta

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

O

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

Sia che si pensi uno di questi sono più leggibile sta a voi.

+0

Perché il 'const'? – MathematicalOrchid

+1

Vogliamo ignorare il valore (ma non l'effetto) di 'many1 space', e applicare' read' al valore di 'many1 digit'. (Scusa, sono appena entrato, è tardi, sono stanco: sto giocando veloce e sciolto con la terminologia.) Se immagini 's' e' d' rappresentano i valori di 'many1 space' e' many1 digit' rispettivamente, quindi il valore (ignorando gli effetti) di 'const read <$> many1 space <*> many1 digit' è' const read sd' = 'read d'. – dave4420

38

mi piacerebbe scrivere

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

C'è un gruppo di sinistra associativa (come l'applicazione) gli operatori parser-building <$>, <*>, <$, <*. La cosa nell'estrema sinistra dovrebbe essere la pura funzione che assembla il valore del risultato dai valori del componente. La cosa a destra di ogni operatore dovrebbe essere un parser, collettivamente dando i componenti della grammatica da sinistra a destra. Quale operatore utilizzare dipende da due scelte, come segue.

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

Quindi, avendo scelto read :: String -> Integer come pura funzione che sta per consegnare la semantica del parser, possiamo classificare lo spazio iniziale come "rumore" e il mazzo di cifre come "segnale", quindi

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

È possibile combinare più possibilità con

p1 <|> ... <|> pn 

e impossibilità express con

empty 

È raramente necessario assegnare un nome ai componenti nei parser e il codice risultante assomiglia più a una grammatica con semantica aggiunta.

+8

Wow, sapevo di '<$', ma l'ho sempre usato solo se la cosa alla sua sinistra era una costante e il diritto era un valore semplice ... Non ho mai pensato a cosa sarebbe successo se avessi messo una funzione a sinistra: P Trucco bello –

7

vostro esempio può essere progressivamente riscritto per una forma che assomiglia più chiaramente un applicativo:

do 
    many1 space 
    ds <- many1 digit 
    return $ read ds 
  1. definizione di do notazione:

    many1 space >> (many1 digit >>= \ds -> return $ read ds) 
    
  2. definizione di $:

    many1 space >> (many1 digit >>= \ds -> return (read ds)) 
    
  3. definizione di .:

    many1 space >> (many1 digit >>= (return . read)) 
    
  4. legge terza monade (associatività):

    (many1 space >> many1 digit) >>= (return . read) 
    
  5. definizione di liftM (in non do notazione):

    liftM read (many1 space >> many1 digit) 
    

Questo è (o dovrebbe essere, se non ho incasinato :)) comportamento identico al tuo esempio.

Ora, se si sostituisce liftM con fmap con <$>, e >> con *>, si ottiene l'applicativo:

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

Questo è valido in quanto liftM, fmap, e <$> sono generalmente doveva essere sinonimi, come sono >> e *>.

Questo funziona e possiamo farlo perché l'esempio originale non ha utilizzato il risultato di alcun parser per creare un parser successivo.

+0

Fantastico! Un altro modo per scrivere 'read <$ many1 space <*> many1 digit'. :) L'ultima frase è molto importante. Ciò significa che questo stile corrisponde a grammatiche senza contesto e grammatiche più generali devono essere analizzate con stile monadico? –

+0

@WillNess Non sono un esperto in questo, ma credo che sia così. –