2013-05-25 12 views
5

Dlaczego ta funkcja ma typ: deleteAllMp4sExcluding :: [Char] -> IO (IO()) zamiast deleteAllMp4sExcluding :: [Char] -> IO()Dlaczego istnieje zagnieżdżona monada IO, IO (IO()), jako wartość zwracana przez moją funkcję?

Ponadto, jak mógłbym przepisać to tak, że będzie on miał prostszą definicję?

Oto definicja funkcji:

import System.FilePath.Glob 
import qualified Data.String.Utils as S 

deleteAllMp4sExcluding videoFileName = 
    let dirGlob = globDir [compile "*"] "." 
     f = filter (\s -> S.endswith ".mp4" s && (/=) videoFileName s) . head . fst 
     lst = f <$> dirGlob 
    in mapM_ removeFile <$> lst 

Odpowiedz

16

<$> po nałożeniu IO S wpisać (a -> b) -> IO a -> IO b. Więc od mapM_ removeFile ma typ [FilePath] -> IO(), b w tym przypadku jest IO(), więc typ wyniku staje się IO (IO()).

Aby uniknąć takiego zagnieżdżania, nie powinieneś używać <$>, gdy funkcja, którą próbujesz zastosować, daje wartość IO. Zamiast tego należy użyć >>= lub, jeśli nie chcesz zmieniać kolejności operandów, =<<.

+0

moje intuicyjne uczucie, które pomaga: 'removeFile' dodaje 1 efekt.jeśli chcemy tylko jednego efektu na końcu, musimy nakarmić, jeśli coś z efektem 0. 'lst' już działa. więc musimy go najpierw usunąć, używając wiązania, które (wykonując obliczenia) uruchamia efekt, aby uzyskać wartość z efektem 0 – nicolas

8

Riffing na odpowiedź sepp2k, jest to doskonały przykład, aby pokazać różnicę między Functor i Monad.

Standardowa definicja Haskell z Monad idzie coś takiego (uproszczony):

class Monad m where 
    return :: a -> m a 
    (>>=) :: m a -> (a -> m b) -> m b 

Jednakże, nie jest to jedyny sposób, klasa mogła zostać zdefiniowane. Alternatywą przebiega tak:

class Functor m => Monad m where 
    return :: a -> m a 
    join :: m (m a) -> m a 

Biorąc pod uwagę, że można zdefiniować >>= pod względem fmap i join:

(>>=) :: Monad m => m a -> (a -> m b) -> m b 
ma >>= f = join (f <$> ma) 

Przyjrzymy się to w sposób uproszczony szkic problemu jesteś biegnąc do. Co robisz można schematycznie tak:

ma  :: IO a 
f  :: a -> IO b 
f <$> ma :: IO (IO b) 

Teraz utkniesz ponieważ potrzebny jest IO b, a klasa Functor ma operację, która będzie Ci tam z IO (IO b). Jedynym sposobem, aby dostać się tam, gdzie chcesz się zanurzyć się Monad, a operacja join jest właśnie to, co rozwiązuje go:

join (f <$> ma) :: IO b 

Ale definicją >>=join/<$>, jest taka sama, jak:

ma >>= f :: IO a 

Należy pamiętać, że biblioteka Control.Monad jest dostarczana z wersją join (zapisaną w kategoriach return i (>>=)); możesz umieścić to w swojej funkcji, aby uzyskać pożądany rezultat. Lepiej jednak jest rozpoznać, że to, co próbujesz zrobić, jest zasadniczo monadyczne, a zatem <$> nie jest właściwym narzędziem do tego zadania. Karmisz wynik jednej akcji innej osobie; to samo w sobie wymaga użycia Monad.