簡體   English   中英

為什么有嵌套的IO monad,IO(IO())作為我函數的返回值?

[英]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 () ,在這種情況下bIO () ,因此結果類型變為IO (IO ())

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

關於sepp2k的答案,這是一個很好的例子來展示FunctorMonad之間的區別。

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

鑒於此,您可以根據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 b ,並且Functor類沒有任何操作可以從IO (IO b)到達那里。 獲得你想要的唯一方法是進入Monadjoin操作正是解決它的方法:

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

但是通過>>=join / <$>定義,這與:

ma >>= f :: IO a

需要注意的是Control.Monad庫帶有一個版本的join (書面來講return(>>=) ); 你可以把它放在你的函數中以獲得你想要的結果。 但更好的做法是要認識到你要做的事情基本上是monadic,因此<$>不適合這項工作。 你把一個動作的結果喂給另一個動作; 本質上要求你使用Monad

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM