带有多个参数的绑定函数

thk*_*ang 16 monads haskell

在阅读了一些非常基本的haskell后,我知道如何使用bind "链接"monadic动作,如:

echo = getLine >>= putStrLn
Run Code Online (Sandbox Code Playgroud)

(>>=) 运算符在这种方式下非常方便,但是如果我想链接带有多个参数的monadic动作(或仿函数)呢?

鉴于(>>=) :: m a -> (a -> m b) -> m b它似乎(>>=)只能提供一个论点.

例如,writeFile接受两个参数(a FilePath和内容).假设我有一个返回a的monadic动作FilePath,以及另一个返回String写入的动作.如何在writeFile不使用do-notation的情况下将它们组合在一起,但是以一般方式?

是否有任何类型的功能: m a -> m b -> (a -> b -> m c) -> m c可以做到这一点?

And*_*ewC 14

TL; DR:

writeFile <$> getFilename <*> getString >>= id   :: IO ()
Run Code Online (Sandbox Code Playgroud)

Monads是适用的

由于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)
Run Code Online (Sandbox Code Playgroud)

当然IO是一个仿函数,但Control.Applicative给你<$>可以定义为f <$> mx = fmap f mx.

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

<$>并且<*>让你使用纯函数f而不是Applicative/Monadic计算产生的参数,所以if f :: String -> String -> BoolgetFileName, getString :: IO Stringthen

f <$> getFileName <*> getString :: IO Bool
Run Code Online (Sandbox Code Playgroud)

同样,如果g :: String -> String -> String -> Int,那么

g <$> getString <*> getString <*> getString :: IO Int
Run Code Online (Sandbox Code Playgroud)

IO (IO ())IO ()

这意味着

writeFile <$> getFilename <*> getString :: IO (IO ())
Run Code Online (Sandbox Code Playgroud)

但是你需要一些类型的东西IO (),不是IO (IO ()),所以我们需要join :: Monad m => m (m a) -> m a像在Xeo的注释中一样使用,或者我们需要一个函数来获取monadic结果并运行它,即类型(IO ()) -> IO ()绑定它.那就是id,所以我们可以做到

join $ writeFile <$> getFilename <*> getString :: IO ()
Run Code Online (Sandbox Code Playgroud)

要么

writeFile <$> getFilename <*> getString >>= id :: IO ()
Run Code Online (Sandbox Code Playgroud)

  • 需要注意的是,自GHC 7.10起,每个Monad应该是一个应用者.因此,您可以在Monads上使用这些应用程序运算符而无需任何修改. (2认同)

Tom*_*lis 7

为此使用do符号要容易得多,而不是要求组合器

action1 :: MyMonad a
action2 :: MyMonad b
f :: a -> b -> MyMonad c

do
    x <- action1
    y <- action2
    f x y
Run Code Online (Sandbox Code Playgroud)

  • 它可能在概念上更容易,但应用风格`f <$> x <*> y >> = id`提升到更高的抽象层(如AndrewC的答案所示),导致更短和(如果熟悉)更容易 - 读代码. (2认同)

Sib*_*ibi 3

此类型检查:

import System.IO

filepath :: IO FilePath
filepath = undefined

someString :: IO String
someString = undefined

testfun = filepath   >>= (\fp -> 
          someString >>= (\str -> 
          writeFile fp str  ))
Run Code Online (Sandbox Code Playgroud)

但我觉得使用 do 表示法更具可读性。