Binding functions that take multiple arguments

前端 未结 3 1195
梦如初夏
梦如初夏 2021-01-01 17:03

After reading some very basic haskell now I know how to \"chain\" monadic actions using bind, like:

echo = getLine >>= putStrLn
3条回答
  •  滥情空心
    2021-01-01 17:33

    TL;DR:

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

    Monads are Applicative

    Since ghc 7.10 every Monad (including IO) is also an Applicative, but even before that, you could make an Applicative out of any Monad using the equivalent of

    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)
    

    And of course IO is a functor, but Control.Applicative gives you <$> which can be defined as f <$> mx = fmap f mx.

    Use Applicative to use functions that take as many arguments as you like

    <$> and <*> let you use pure functions f over arguments produced by an Applicative/Monadic computation, so if f :: String -> String -> Bool and getFileName, getString :: IO String then

    f <$> getFileName <*> getString :: IO Bool
    

    Similarly, if g :: String -> String -> String -> Int, then

    g <$> getString <*> getString <*> getString :: IO Int
    

    From IO (IO ()) to IO ()

    That means that

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

    but you need something of type IO (), not IO (IO ()), so we need to either use join :: Monad m => m (m a) -> m a as in Xeo's comment, or we need a function to take the monadic result and run it, i.e. of type (IO ()) -> IO () to bind it with. That would be id then, so we can either do

    join $ writeFile <$> getFilename <*> getString :: IO ()
    

    or

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

提交回复
热议问题