4

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

2 回答 2

16

<$>当应用于IOs 时具有类型(a -> b) -> IO a -> IO b。所以既然mapM_ removeFile有类型[FilePath] -> IO ()b在这种情况下是IO (),所以结果类型就变成了IO (IO ())

为避免这样的嵌套,您不应<$>在尝试应用的函数产生IO值时使用。相反,您应该使用>>=or,如果您不想更改操作数的顺序,则=<<.

于 2013-05-25T13:46:19.063 回答
8

根据 sepp2k 的回答,这是一个很好的例子来展示和之间的Functor区别Monad

标准的 Haskell 定义是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

鉴于此,您可以>>=根据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和编写(>>=));你可以把它放在你的函数中以获得你想要的结果。但更好的做法是认识到你正在尝试做的事情基本上是单子的,因此这<$>不是工作的正确工具。您正在将一个动作的结果提供给另一个动作;这本质上需要您使用Monad.

于 2013-05-25T17:56:39.227 回答