2014-09-08 7 views
6

Mam trochę kodu, który wygląda trochę jak ten, ignorując cały kod, który nie jest właściwy do mojego pytania:Refaktoryzacja funkcji Haskell, który używa monady 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' 

Czy istnieje sposób na refaktoryzację tego? Szczególnie zagraża mi najbardziej zagnieżdżona funkcja bar'. Czy można to skondensować do jednej linii?

Odpowiedz

9

Możemy zrobić trochę rozumowania równania. Najpierw spójrzmy na bar'. Napiszę go w tej formie

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

Okazuje się, że liftM określa się liftM f m = m >>= \a -> return (f a) który pasuje do wzorca wyżej. Warto więc zastąpić go

liftM ($ i) (asks bar) 

Następnie mamy foo jako

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

Albo rozpisane trochę szczególnie

liftM show . liftM ($ i) $ asks bar 

Jeśli wiemy, że liftM jest fmap możemy rozpoznać Functor prawo tutaj odtwarzane

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

nie jestem osobiście wielkim fanem używając ($ i) jako funkcja, więc niech przepisać go jako wyraźnej lambda

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

Teraz, możemy zdecydować się wyeliminować asks za pomocą bar w miejscu połączenia (to znaczy używać bar w zależności od rodzaju bar :: FooEnv -> Int -> Int

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

i jako ostatni trik, możemy użyć flip iść sensu w funkcji fmap ped a nawet zwrotu wykorzystanie asks (dzięki Ørjan Johansen)

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

Nie mówię, że jest to najbardziej czytelny lub wspaniały sposób wykonania tego zadania, ale można zobaczyć, jak możemy po prostu zredukować elementy za pomocą równania rozumowania.

+2

Zauważ, że 'prosi f' jest równoważne' f <$> ask', więc zależnie od twojego gustu możesz go ponownie wprowadzić na końcu: 'pyta $ show. flip bar i' –

+0

Dzięki, @ j-abrahamson! Szczegółowe czynności są bardzo pomocne. Powinienem być bardziej konkretny, ponieważ nie chcę wstawiać 'bar'' do' foo'. Postaram się jednak zrozumieć kroki, które podjąłeś, aby móc spróbować stworzyć bezbłędną wersję 'bar''. Wolę "prosi" o "ask", więc dziękuję również, @ Ørjan-johansen, za dodanie tego ostatniego kroku. – arussell84

+0

Rozgryzłem to: 'bar 'i' = pyta $ flip bar i'' lub' bar '= pyta. odwróć pasek " – arussell84

Powiązane problemy