为什么这个函数有类型:
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
答案 0 :(得分:17)
<$>
时, IO
的类型为(a -> b) -> IO a -> IO b
。因此,由于mapM_ removeFile
的类型为[FilePath] -> IO ()
,因此b
为IO ()
,因此结果类型为IO (IO ())
。
为避免这样嵌套,当您尝试应用的函数产生<$>
值时,不应使用IO
。相反,你应该使用>>=
,或者,如果你不想改变操作数的顺序,=<<
。
答案 1 :(得分:8)
关于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
的{{1}} / <$>
定义,这与以下内容相同:
>>=
请注意,ma >>= f :: IO a
库附带Control.Monad
版本(根据join
和return
编写);你可以把它放在你的函数中以获得你想要的结果。但更好的做法是要认识到你所做的事情基本上是一元的,因此(>>=)
不适合这项工作。你把一个动作的结果喂给另一个动作;这本质上要求您使用<$>
。