Jun, Hyunje

Code, specifically for web

Programming languages

Some geeky hobbies

Let me guess we’re about to filter a list of integers, xs.

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 Bool, not 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

Cool! 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 liftM2 is (x -> y -> z) -> (a -> x) -> (a -> y) -> a -> z, right?

liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r

What? Monad? No, no, no, we’ve talked about compositing functions, not about monad. Is this article another monad tutorial?

Monads Everywhere
Monads Everywhere

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 Monad m.

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 liftM, liftM3, liftM4, liftM5, and 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 (odd, (> 50)).

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.

Happy coding!

read other posts