带有多个参数的绑定函数

时间:2014-03-01 14:17:23

标签: haskell monads

在阅读了一些非常基本的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可以执行此操作?

3 个答案:

答案 0 :(得分:13)

TL; DR:

writeFile <$> getFilename <*> getString >>= id   :: IO ()

Monads是适用的

由于ghc 7.10每个Monad(包括IO)也是一个申请人,但在此之前,你可以使用相当于

的任何Monad来制作一个申请人。
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,可以定义为<$>

使用Applicative来使用带有任意数量参数的函数

<*>f允许您对Applicative / Monadic计算生成的参数使用纯函数f :: String -> String -> Bool,因此如果getFileName, getString :: IO Stringf <$> 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  ))

但我觉得使用符号更具可读性。