Let me guess we’re about to filter a list of integers,
xs = [1..100]
It’s not a big deal in Haskell to filter even numbers out.
filter odd xs
It’s the same when getting only numbers larger than 50.
filter (> 50) xs
So far, so good. But what if we want to get numbers which are odd and larger than 50 at the same time? Maybe we should use
filter (odd && (> 50)) xs
The code above doesn’t work because what we want to
&& is the result of the functions, not the functions themselves. The types are mismatched:
odd :: Integral a => a -> Bool (> 50) :: (Ord a, Num a) => a -> Bool && :: Bool -> Bool -> Bool
Both of the parameters of
&& should be
a -> Bool.
Then, how about the code below? What we want to do is basically something like function composition, so may it work?
filter (odd . (> 50)) xs
The answer is, as you may have expected, no. The types are also mismatched too:
odd :: Integral a => a -> Bool (> 50) :: (Ord a, Num a) => a -> Bool (.) :: (b -> c) -> (a -> b) -> a -> c
What we want is actually a function having a type
(a -> Bool) -> (a -> bool) -> a -> Bool. Then why not create one?
and :: (a -> Bool) -> (a -> Bool) -> a -> Bool and f g x = (f x) && (g x) filter (odd `and` (> 50)) xs
Easy. We can make it in more general form.
lift :: (x -> y -> z) -> (a -> x) -> (a -> y) -> a -> z lift f g h x = f (g x) (h x) and = lift (&&) filter (odd `and` (> 50)) xs
lift actually lifts a function with type
x -> y -> z to be applied into other functions! But wait,
lift’s looking so useful. Why not have it in Haskell by default?
import Control.Monad (liftM2) and = liftM2 (&&) filter (odd `and` (> 50)) xs
Yes, we already have. Haskell is awesome. So, the type of
(x -> y -> z) -> (a -> x) -> (a -> y) -> a -> z, right?
liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
Monad? No, no, no, we’ve talked about compositing functions, not about monad. Is this article another monad tutorial?
No. I promise it won’t be. Monad itself is not what I want to talk about in this article. I still want to talk about function composition. Then, why the heaven is the term
Monad coming out in the type of
liftM2? Well, in Haskell, function is monad. No, more specifically, function can be a kind of monad, something called reader monad.
As I promised, I’m not going to explain what is monad. Instead, I’ll just show what reader monad looks like.
instance Monad ((->) x)
((->) x) is just the prefix form of
x ->. Now, let’s replace
m in the type of
liftM2 with this, because
x -> is an instance of
liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r liftM2' :: (a1 -> a2 -> r) -> (x -> a1) -> (x -> a2) -> (x -> r)
By alpha equivalence, it’s exactly the same type as
lift we made above.
There are similar functions such as
liftM6. Sorry, I lied. There’s no
liftM6, 5 is enough! Anyway, they are all about lifting a function with n parameters, making it able to be applied to monadic values. In easy words, it makes functions to work well with some complex values, as we made
&& work with complex values, in our case, return values of other functions (
All in all, what I’d like to show in this article is how monad can help us. Monad matters not because it is a monoid in the category of endofunctors, but because it just helps us. You don’t need to master category theory or get a Ph.D. in CS before trying Haskell, or even after trying Haskell at all. Please don’t be afraid of monad, just enjoy Haskell. Monad will be with you some time, because it’s created to help us.