2013-05-25 6 views
5

Warum hat diese Funktion den Typ: deleteAllMp4sExcluding :: [Char] -> IO (IO()) statt deleteAllMp4sExcluding :: [Char] -> IO()Warum gibt es eine verschachtelte IO-Monade, IO (IO()), als Rückgabewert meiner Funktion?

Auch, wie ich diese umschreiben könnte, so dass es eine einfachere Definition haben würde? Hier

ist die Funktionsdefinition:

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 

Antwort

16

<$> wenn auf IO s angewendet hat (a -> b) -> IO a -> IO b geben. Also, da mapM_ removeFile hat Typ [FilePath] -> IO(), b in diesem Fall ist IO(), so wird der Ergebnistyp IO (IO()).

Um eine Verschachtelung auf diese Weise zu vermeiden, sollten Sie <$> nicht verwenden, wenn die Funktion, die Sie anwenden möchten, einen Wert IO ergibt. Verwenden Sie lieber >>= oder, wenn Sie die Reihenfolge der Operanden nicht ändern möchten, =<<.

+0

mein intuitives Gefühl es hilft es: 'RemoveFile' fügt 1 Effekt hinzu.wenn wir am Ende nur 1 Effekt haben wollen, müssen wir etwas mit 0 füttern. 'lst' hat bereits einen Effekt. Also müssen wir es zuerst entfernen, indem wir den Bind verwenden, der den Effekt ausführt, um den Wert mit dem Wert 0 zu erhalten – nicolas

8

Riffing auf Sepp2k's Antwort, dies ist ein hervorragendes Beispiel, um den Unterschied zwischen Functor und Monad zu zeigen.

Die Standard Haskell Definition von Monad geht so etwas (vereinfacht):

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

Dies ist jedoch nicht die einzige Möglichkeit, die Klasse definiert worden sein könnte. Eine Alternative läuft wie folgt aus:

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

Da Sie >>= in Bezug auf fmap und join definieren:

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

wir an dieser in einer vereinfachten Skizze des Problems aussehen werde, du bist hineinrennen. Was Sie tun kann wie folgt schematisiert werden:

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

Jetzt Sie stecken, weil Sie einen IO b benötigen, und die Functor Klasse hat keine Operation, die Sie dort von IO (IO b) bekommen. Der einzige Weg, zu bekommen, wo Sie wollen, ist in Monad zu tauchen und der join Betrieb ist genau das, was es löst:

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

Aber durch die join/<$> Definition von >>=, ist dies das gleiche wie:

ma >>= f :: IO a 

Beachten Sie, dass die Bibliothek Control.Monad mit einer Version join (in Bezug auf return und (>>=) geschrieben) geliefert wird; Sie könnten das in Ihre Funktion einfügen, um das gewünschte Ergebnis zu erhalten. Aber das Bessere ist zu erkennen, dass das, was Sie zu tun versuchen, grundsätzlich monadisch ist, und somit ist das <$> nicht das richtige Werkzeug für den Job. Du fütterst das Ergebnis einer Handlung einem anderen; Das erfordert von Ihnen, dass Sie Monad verwenden.