2013-05-25 18 views
5

Perché questa funzione ha il tipo: deleteAllMp4sExcluding :: [Char] -> IO (IO()) invece di deleteAllMp4sExcluding :: [Char] -> IO()Perché esiste un MONAD IO nidificato, IO (IO()), come valore di ritorno della mia funzione?

Inoltre, come ho potuto riscrivere questo in modo che avrebbe una definizione più semplice?

Ecco la definizione della funzione:

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 

risposta

16

<$> quando applicato IO s è digitare (a -> b) -> IO a -> IO b. Quindi, dal momento che mapM_ removeFile ha tipo [FilePath] -> IO(), b in questo caso è IO(), quindi il tipo di risultato diventa IO (IO()).

Per evitare il nesting in questo modo, non utilizzare <$> quando la funzione che si sta tentando di applicare produce un valore IO. Piuttosto dovresti usare >>= o, se non vuoi cambiare l'ordine degli operandi, =<<.

+0

la mia sensazione intuitiva è utile: 'removeFile' aggiunge 1 effetto.se vogliamo solo 1 effetto alla fine, dobbiamo dare da mangiare se qualcosa con effetto 0. 'lst' ha già un effetto. quindi dobbiamo rimuoverlo prima, usando il bind che (stage a computation that) esegue l'effetto per ottenere il valore con l'effetto 0 – nicolas

8

Riffing sulla risposta di sepp2k, questo è un eccellente esempio per mostrare la differenza tra Functor e Monad.

La definizione standard Haskell di Monad va qualcosa come questo (semplificato):

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

Tuttavia, questo non è l'unico modo in cui la classe avrebbe potuto essere definito. Un'alternativa corre come questo:

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

Dato che, è possibile definire >>= in termini di fmap e join:

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

Vedremo questo in uno schema semplificato del problema si è imbattersi. Quello che stai facendo può essere schematizzato in questo modo:

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

Ora sei bloccato in quanto è necessario un IO b, e la classe Functor non ha alcuna operazione che ti arriva da IO (IO b). L'unico modo per arrivare dove si vuole è quello di immergersi in Monad, e l'operazione join è precisamente ciò che lo risolve:

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

Ma la definizione join/<$> di >>=, questo è lo stesso:

ma >>= f :: IO a 

Si noti che la libreria Control.Monad viene fornita con una versione di join (scritta in termini di return e (>>=)); potresti metterlo nella tua funzione per ottenere il risultato desiderato. Ma la cosa migliore da fare è riconoscere che ciò che stai cercando di fare è fondamentalmente monadico, e quindi che lo <$> non è lo strumento giusto per il lavoro. Stai dando il risultato di un'azione all'altra; che intrinsecamente richiede l'utilizzo di Monad.