[英]Why is there a nested IO monad, IO (IO ()), as the return value of my function?
為什么這個函數有類型: deleteAllMp4sExcluding :: [Char] -> IO (IO ())
而不是deleteAllMp4sExcluding :: [Char] -> IO ()
另外,我怎么能重寫這個以便它有一個更簡單的定義?
這是函數定義:
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
應用於IO
的<$>
具有類型(a -> b) -> IO a -> IO b
。 因此mapM_ removeFile
具有[FilePath] -> IO ()
,在這種情況下b
是IO ()
,因此結果類型變為IO (IO ())
。
為避免這樣嵌套,當您嘗試應用的函數產生IO
值時,不應使用<$>
。 相反,你應該使用>>=
或者,如果你不想改變操作數的順序, =<<
。
關於sepp2k的答案,這是一個很好的例子來展示Functor
和Monad
之間的區別。
Monad
的標准Haskell定義是這樣的(簡化):
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
鑒於此,您可以根據fmap
和join
定義>>=
:
(>>=) :: 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 b
,並且Functor
類沒有任何操作可以從IO (IO b)
到達那里。 獲得你想要的唯一方法是進入Monad
, join
操作正是解決它的方法:
join (f <$> ma) :: IO b
但是通過>>=
的join
/ <$>
定義,這與:
ma >>= f :: IO a
需要注意的是Control.Monad
庫帶有一個版本的join
(書面來講return
和(>>=)
); 你可以把它放在你的函數中以獲得你想要的結果。 但更好的做法是要認識到你要做的事情基本上是monadic,因此<$>
不適合這項工作。 你把一個動作的結果喂給另一個動作; 本質上要求你使用Monad
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.