After reading some very basic haskell now I know how to "chain" monadic actions using bind, like:
echo = getLine >>= putStrLn
(>>=)
operator is very handy in this fashion, but what if I want to chain monadic actions (or functors) that take multiple arguments?
Given that (>>=) :: m a -> (a -> m b) -> m b
it seems like (>>=)
can supply only one argument.
For example, writeFile
takes two arguments (a FilePath
and the contents). Suppose I have a monadic action that returns a FilePath
, and another action that returns a String
to write. How can I combine them with writeFile
, without using the do
-notation, but in a general way?
Is there any function with the type: m a -> m b -> (a -> b -> m c) -> m c
that can do this?
Functions can accept more than one argument. When calling a function, you're able to pass multiple arguments to the function; each argument gets stored in a separate parameter and used as a discrete variable within the function.
You can bind multiple arguments multiple times, exactly as partial application, but the context object only once, you can't override it once you bound it, because this is defined by specs that I linked: "The bind() function creates a new function (a bound function) with the same function body (internal call property in ...
Except for functions with variable-length argument lists, the number of arguments in a function call must be the same as the number of parameters in the function definition. This number can be zero. The maximum number of arguments (and corresponding parameters) is 253 for a single function.
Unlimited Number of Positional Argument ValuesPython lets us define a function that handles an unknown and unlimited number of argument values. Examples of built-in functions with a unlimited number of argument values are max and min .
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With