2011-11-22 11 views
16

Ho deciso che oggi è il giorno in cui aggiusto alcune delle mie pure funzioni che sono inutilmente in esecuzione in un'azione monadica. Ecco cosa ho.Come organizzo le mie pure funzioni con le mie azioni monodiche idiomaticamente

flagWorkDays :: [C.Day] -> Handler [WorkDay] 
flagWorkDays dayList = 
    flagWeekEnds dayList >>= 
    flagHolidays >>= 
    flagScheduled >>= 
    flagASAP >>= 
    toWorkDays 

Ecco flagWeekEnds, al momento.

flagWeekEnds :: [C.Day] -> Handler [(C.Day,Availability)] 
flagWeekEnds dayList = do 
    let yepNope = Prelude.map isWorkDay dayList 
     availability = Prelude.map flagAvailability yepNope 
    return $ Prelude.zip dayList availability 

flagHolidays segue un andamento simile. toWorkDays cambia semplicemente un tipo in un altro ed è una funzione pura.

flagScheduled e flagASAP sono azioni monodiche. Non sono sicuro di come combinare le azioni monadiche con le pure funzioni idiomaticamente in flagWorkDays. Qualcuno potrebbe aiutarmi a risolvere flagWorkDays, supponendo che flagWeekEnds e flagHolidays siano stati resi puri?

risposta

28

Facciamo un passo indietro per un momento. Esistono due tipi di funzioni, alcuni puri con tipi del modulo a -> b e alcuni monadici di tipo a -> m b.

Per evitare confusione, attenersi anche alla composizione da destra a sinistra. Se si preferisce leggere da sinistra a destra, basta invertire l'ordine delle funzioni e sostituire (<=<) con (>=>) e (.) con (>>>) da Control.Arrow.

Ci sono quindi quattro possibilità per come possono essere composti.

  1. Puro quindi puro. Utilizzare la funzione regolare composizione (.).

    g :: a -> b 
    f :: b -> c 
    f . g :: a -> c 
    
  2. Pure allora monadica. Utilizzare anche (.).

    g :: a -> b 
    f :: b -> m c 
    f . g :: a -> m c 
    
  3. Monadica poi monadica. Usa la composizione kleisli (<=<).

    g :: a -> m b 
    f :: b -> m c 
    f <=< g :: a -> m c 
    
  4. Monadica quindi pura. Utilizzare fmap sulla funzione pura e (.) da comporre.

    g :: a -> m b 
    f :: b -> c 
    fmap f . g :: a -> m c 
    

Ignorando le specifiche dei tipi coinvolti, le funzioni sono:

flagWeekEnds :: a -> b 
flagHolidays :: b -> c 
flagScheduled :: c -> m d 
flagASAP :: d -> m e 
toWorkDays :: e -> f 

Andiamo dall'alto. flagWeekEnds e flagHolidays sono entrambi puri. Caso 1.

flagHolidays . flagWeekEnds 
    :: a -> c 

Questo è puro. Il prossimo è flagScheduled, che è monadico. Caso 2.

flagScheduled . flagHolidays . flagWeekEnds 
    :: a -> m d 

Next è flagASAP, ora abbiamo due funzioni monadici. Caso 3.

flagASAP <=< flagScheduled . flagHolidays . flagWeekEnds 
    :: a -> m e 

E, infine, abbiamo la pura funzione toWorkDays. Caso 4.

fmap toWorkDays . flagASAP <=< flagScheduled . flagHolidays . flagWeekEnds 
    :: a -> m f 

E abbiamo finito.

+2

+1 per una spiegazione generale – fuz

+3

Brillante, come sempre. – Ingo

5

Non è molto difficile. In pratica sostituisci semplicemente (>>=) per (.) e capovolgi l'ordine degli operandi. La sintassi do può aiutare a chiarire. Ho anche fatto l'esempio senza usare il combinatore di Kleisli (pesce) (<=<) :: (b -> m c) -> (a -> m b) -> a -> m c, che è essenzialmente (.) per le monadi.

import Control.Monad 

flagWorkDays :: [C.Day] -> Handler [WorkDay] 
flagWorkDays = 
    fmap toWorkDays . flagASAP <=< flagScheduled . flagHolidays . flagWeekEnds 
+0

E riguardo 'dayList'? – Tarrasch

+0

@Tarrasch era inutile. Ho dimenticato di rimuovere il parametro aggiuntivo – fuz

+0

Esiste un nome comune/funzione predefinita per 'flip (.)'? Sono piuttosto triste che la trasformazione qui abbia dovuto rimettere le cose a posto – hugomg

5

Per compilare la risposta di FUZxxl, diamo pureify flagWeekEnds:

flagWeekEnds :: [C.Day] -> [(C.Day,Availability)] 
flagWeekEnds days = days `zip` map (flagAvailability . isWorkDay) days 

Spesso con una "s" dopo i nomi delle variabili (day ->days) quando la sua una lista (come si fa con plurale in inglese).