为什么这个函数有类型:
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
Run Code Online (Sandbox Code Playgroud)
sep*_*p2k 17
<$>当应用于IOs时有类型(a -> b) -> IO a -> IO b.所以既然mapM_ removeFile有类型[FilePath] -> IO (),b在这种情况下IO (),结果类型就变成了IO (IO ()).
为避免这样嵌套,<$>当您尝试应用的函数产生IO值时,不应使用.相反,你应该使用>>=或,如果你不想改变操作数的顺序,=<<.
Riffing上sepp2k的答案,这是示区别一个很好的例子Functor和Monad.
标准的Haskell定义是Monad这样的(简化):
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
Run Code Online (Sandbox Code Playgroud)
但是,这不是可以定义类的唯一方法.另一种运行方式如下:
class Functor m => Monad m where
return :: a -> m a
join :: m (m a) -> m a
Run Code Online (Sandbox Code Playgroud)
鉴于此,您可以定义>>=在以下方面fmap和join:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
ma >>= f = join (f <$> ma)
Run Code Online (Sandbox Code Playgroud)
我们将在您遇到的问题的简化草图中看到这一点.你正在做什么可以像这样模式化:
ma :: IO a
f :: a -> IO b
f <$> ma :: IO (IO b)
Run Code Online (Sandbox Code Playgroud)
现在你被困了,因为你需要一个IO b,并且Functor该类没有任何可以让你从那里获得的操作IO (IO b).获得你想要的唯一方法是进入Monad,join操作正是解决它的方法:
join (f <$> ma) :: IO b
Run Code Online (Sandbox Code Playgroud)
但是通过join/ <$>定义>>=,这与以下相同:
ma >>= f :: IO a
Run Code Online (Sandbox Code Playgroud)
请注意,该Control.Monad库附带了一个版本join(根据return和编写(>>=)); 你可以把它放在你的函数中以获得你想要的结果.但最好的办法是要认识到你所要做的事情基本上是一元的,因此这<$>不是工作的正确工具.你把一个动作的结果喂给另一个动作; 本质上要求你使用Monad.