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

时间:2013-05-25 13:29:01

标签: haskell

为什么这个函数有类型: 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

2 个答案:

答案 0 :(得分:17)

应用于<$>时,

IO的类型为(a -> b) -> IO a -> IO b。因此,由于mapM_ removeFile的类型为[FilePath] -> IO (),因此bIO (),因此结果类型为IO (IO ())

为避免这样嵌套,当您尝试应用的函数产生<$>值时,不应使用IO。相反,你应该使用>>=,或者,如果你不想改变操作数的顺序,=<<

答案 1 :(得分:8)

关于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

鉴于此,您可以根据>>=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的{​​{1}} / <$>定义,这与以下内容相同:

>>=

请注意,ma >>= f :: IO a 库附带Control.Monad版本(根据joinreturn编写);你可以把它放在你的函数中以获得你想要的结果。但更好的做法是要认识到你所做的事情基本上是一元的,因此(>>=)不适合这项工作。你把一个动作的结果喂给另一个动作;这本质上要求您使用<$>