After reading some very basic haskell now I know how to \"chain\" monadic actions using bind, like:
echo = getLine >>= putStrLn
This typechecks:
import System.IO
filepath :: IO FilePath
filepath = undefined
someString :: IO String
someString = undefined
testfun = filepath >>= (\fp ->
someString >>= (\str ->
writeFile fp str ))
But I feel using do notation is more readable.
TL;DR:
writeFile <$> getFilename <*> getString >>= id :: IO ()
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
.
<$>
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
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 ()
It's much easier to use do
notation for this, rather than asking for a combinator
action1 :: MyMonad a
action2 :: MyMonad b
f :: a -> b -> MyMonad c
do
x <- action1
y <- action2
f x y