2013-05-25 88 views
5

爲什麼這個功能有類型: deleteAllMp4sExcluding :: [Char] -> IO (IO()) 代替deleteAllMp4sExcluding :: [Char] -> IO()爲什麼嵌套IO monad,IO(IO())作爲函數的返回值?

而且,我怎麼能改寫這個,以便它有一個簡單的定義是什麼?當施加到IO s已鍵入(a -> b) -> IO a -> IO b

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 

回答

16

<$>

下面是函數定義。因此,由於mapM_ removeFile的型號爲[FilePath] -> IO(),因此bIO(),因此結果類型爲IO (IO())

爲避免像這樣嵌套,當您嘗試應用的函數產生一個IO值時,您不應該使用<$>。相反,您應該使用>>=或者,如果您不想更改操作數的順序=<<

+0

我的直覺感覺它有幫助:'removeFile'增加了1個效果。如果我們最終只想得到1個效果,那麼如果有0效果的東西,我們就必須餵食。 'lst'已經有效了。所以我們需要先移除它,通過使用bind(運行階段的計算)運行效果來獲得0效果的值 – nicolas

8

關於sepp2k的回答嗤之以鼻,這是一個很好的例子,顯示FunctorMonad之間的區別。

Monad標準哈斯克爾定義是這樣的(簡化):

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

然而,這是不是類可以被定義的唯一途徑。另一種運行這樣的:

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

鑑於這種情況,你可以在fmapjoin來定義>>=

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

我們將看看這個在你的問題的簡化草圖進入。你在做什麼,可以系統化如下:

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

現在,你就完蛋了,因爲你需要一個IO bFunctor類沒有操作,將讓你有從IO (IO b)。得到你想要的唯一途徑是動用Monadjoin操作正是解決它:

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

但到>>=join/<$>定義,這是一樣的:

ma >>= f :: IO a 

請注意,Control.Monad庫帶有版本join(根據return(>>=)編寫);你可以把它放在你的函數中來得到你想要的結果。但要做的更好的事情是要認識到你所要做的事情基本上是一次性的,因此<$>不適合工作。你將一個動作的結果提供給另一個動作;這本質上要求您使用Monad