2014-09-08 17 views
6

Ho un po 'di codice che sembra un po' come questo, ignorando tutto il codice che non è rilevante per la mia domanda:Rifattorizzare una funzione Haskell che utilizza la monade Reader

import qualified Control.Monad.Reader as Reader 

data FooEnv = FooEnv { bar :: Int -> Int } 
type FooReader = Reader.Reader FooEnv 

foo :: Int -> FooReader String 
foo i = Reader.liftM show $ bar' i 
    where 
    bar' i' = do 
     bar'' <- Reader.asks bar 
     return $ bar'' i' 

Esiste un modo di refactoring questo? Nello specifico, la funzione nidificata bar' mi dà più fastidio. Questo può essere condensato su una riga?

risposta

9

Possiamo fare un piccolo ragionamento equo. Diamo prima un'occhiata a bar'. Scriverò in questa forma

asks bar >>= \z -> return (z i) 

Si scopre che liftM è definito per essere liftM f m = m >>= \a -> return (f a) che si inserisce il modello di cui sopra. Quindi cerchiamo di sostituirlo con

liftM ($ i) (asks bar) 

allora abbiamo foo come

liftM show (liftM ($ i) (asks bar)) 

Oppure, scritto fuori un po 'particolare

liftM show . liftM ($ i) $ asks bar 

Se sappiamo che liftM è fmap potremmo riconoscere la Functor legge in gioco qui

fmap show . fmap ($ i) $ asks bar -- equals 
fmap (show . ($ i)) $ asks bar 

Io non sono personalmente un grande fan di utilizzare ($ i) in funzione, quindi cerchiamo di riscrivere come un lambda esplicito

fmap (\f -> show (f i)) (asks bar) 

Ora, potremmo decidere di eliminare la asks utilizzando bar presso il sito di chiamata (vale a dire utilizzare bar in funzione del tipo di bar :: FooEnv -> Int -> Int

fmap (\f -> show (bar f i)) ask 

e come un trucco finale, potremmo usare flip andare inutile nella funzione fmap ped e anche tornare l'uso di asks (grazie Ørjan Johansen)

fmap (show . flip bar i) ask -- or even 
show . flip bar i <$> ask  -- or even 
asks (show . flip bar i) 

Non sto dicendo che questo è il modo più leggibile o più meraviglioso per eseguire questo compito, ma puoi vedere come possiamo ridurre i pezzi usando il ragionamento equo.

+2

Nota che 'chiede f' è equivalente a' f <$> ask', quindi dipende dai tuoi gusti, puoi reintrodurlo alla fine: 'chiede $ show. flip bar i' –

+0

Grazie, @ j-abrahamson! I passaggi dettagliati sono molto utili. Avrei dovuto essere più specifico, però, nel senso che non voglio in linea "bar" in "foo". Lavorerò comunque per capire i passi che hai compiuto, così da poter provare a creare una versione point-free di "bar". Preferisco 'ask' a' ask', quindi grazie anche a @ Ørjan-johansen, per aver aggiunto il passaggio finale. – arussell84

+0

L'ho capito: 'bar 'i' = chiede $ flip bar i'' o' bar '= ask. flip bar' – arussell84

Problemi correlati