在阅读了一些非常基本的haskell后,我知道如何使用 bind “链接”monadic动作,如:
echo = getLine >>= putStrLn
(>>=)
运算符以这种方式非常方便,但如果我想链接带有多个参数的monadic动作(或仿函数)呢?
鉴于(>>=) :: m a -> (a -> m b) -> m b
似乎(>>=)
只能提供一个参数。
例如,writeFile
有两个参数(一个FilePath
和内容)。假设我有一个返回FilePath
的monadic动作,以及另一个返回String
的动作。如何在不使用writeFile
符号的情况下将它们与do
结合使用,但一般情况下呢?
是否有任何类型的函数:m a -> m b -> (a -> b -> m c) -> m c
可以执行此操作?
答案 0 :(得分:13)
TL; DR:
writeFile <$> getFilename <*> getString >>= id :: IO ()
由于ghc 7.10每个Monad(包括IO
)也是一个申请人,但在此之前,你可以使用相当于
import Control.Applicative -- not needed for ghc >= 7.10
instance Applicative M where
pure x = return x
mf <*> mx = do
f <- mf
x <- mx
return (f x)
当然,IO
是一个仿函数,但Control.Applicative
为您<$>
提供了f <$> mx = fmap f mx
,可以定义为<$>
。
<*>
和f
允许您对Applicative / Monadic计算生成的参数使用纯函数f :: String -> String -> Bool
,因此如果getFileName, getString :: IO String
和f <$> getFileName <*> getString :: IO Bool
那么
g :: String -> String -> String -> Int
同样,如果是g <$> getString <*> getString <*> getString :: IO Int
,那么
IO (IO ())
IO ()
到writeFile <$> getFilename <*> getString :: IO (IO ())
这意味着
IO ()
但你需要IO (IO ())
类型的内容,而不是join :: Monad m => m (m a) -> m a
,所以我们需要使用Xeo's comment中的(IO ()) -> IO ()
,或者我们需要一个函数来获取monadic结果并运行它,即类型为id
以将其绑定。那将是join $ writeFile <$> getFilename <*> getString :: IO ()
,所以我们可以做
writeFile <$> getFilename <*> getString >>= id :: IO ()
或
{{1}}
答案 1 :(得分:7)
为此使用do
表示法要容易得多,而不是要求组合符
action1 :: MyMonad a
action2 :: MyMonad b
f :: a -> b -> MyMonad c
do
x <- action1
y <- action2
f x y
答案 2 :(得分:3)
这个样式:
import System.IO
filepath :: IO FilePath
filepath = undefined
someString :: IO String
someString = undefined
testfun = filepath >>= (\fp ->
someString >>= (\str ->
writeFile fp str ))
但我觉得使用符号更具可读性。