CSnerd
CSnerd

Reputation: 2229

Confusion about return function and Monad

Sorry, I am a newbie to Haskell. This questions may be very easy...

From Hoogle, return signature is return :: Monad m => a -> m a and head signature is head :: [a] -> a

Before I did this head $ return [1,2,3], I thought that ghci would throw errors because m [a] is not the same [a]. But to my surprise, it returned [1,2,3]. And tail $ return [1,2,3] returned []. Why?

One more question:

I wrote a function to generate random number:

drawFloat :: Float -> Float -> IO Float
drawFloat x y = getStdRandom (randomR (x,y))
randList = mapM (const $ drawFloat 2 10) [1..10] -- generate a list of random numbers

When I want to get the head of the list, I first tried head randList (failed) but head <$> randList worked. What is <$>? Can someone explain? Thanks!

Upvotes: 3

Views: 195

Answers (2)

Daniel Wagner
Daniel Wagner

Reputation: 152847

I thought that ghci would throw errors because m [a] is not the same as [a].

Maybe not, but m [a] and [b] can unify! For example, we can set m ~ [] and b ~ [a] so that m [a] ~ [] [a] ~ [[a]] and [b] ~ [[a]]. Then we just need to check that [] is a Monad, which it is. And this is just what's happening:

> return [1,2,3] :: [[Int]]
[[1,2,3]]

Then it should be clear why head returns [1,2,3] and tail returns [].

randList = mapM (const $ drawFloat 2 10) [1..n]

As a comment, not answering your question yet: this is better spelled replicateM n (drawFloat 2 10).

head randList (failed) but head <$> randList worked. What is <$>?

The problem here is that head is still expecting a list. Before, when you were using return, the monad hadn't been chosen yet, and so it could be chosen to be []; but here, it's clear that the monad you're using is IO. So head can't make progress. The solution is to teach head how to handle IO. There are many ways, and <$> is one of them; it has this type:

> :t (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b

The way to read this is: given a pure function, teach it how to handle IO (or any other kind of effect that qualifies as a Functor). For example, it also has this type:

(<$>) :: ([a] -> a) -> IO [a] -> IO a

There are several other kinds of teachers. Two commonly used ones are <*> and =<<:

Prelude Control.Applicative> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Prelude Control.Applicative> :t (=<<)
(=<<) :: Monad m => (a -> m b) -> m a -> m b

Read the first as: given an effectful computation that produces a pure function, produce a function that can handle effects. The second should be read as: given a function which has some effects before it produces its output, produce a function which can handle effects.

At this point, if these explanations aren't helpful, you should turn to one of the many excellent monad tutorials available elsewhere on the web. My personal favorites are You Could Have Invented Monads! and All About Monads.

Upvotes: 12

thor
thor

Reputation: 22500

Regarding your first question:

return takes a "raw" value and gives you a monad with the value "wrapped" in it. Since you are using head and tail on return [1,2,3] , the monad in question is the list monad []. So in this context,

return [1,2,3] == [[1,2,3]]

apply head and tail on it, you will get [1,2,3] and [], respectively.

On the second question, note that the type of randList is IO [Float]. So it's not a list any more, but a list inside a monad. To apply any function to the content of the monad, you can use fmap (<$> is its infix shorthand), which has the type:

fmap :: Functor f => (a -> b) -> f a -> f b

A monad is always a Functor. So what fmap does, is that it takes an ordinary function, (head in your example), and a monadic value, applies the function to the "content" of the monad, and returns a return-value of the function wrapped in the same monad f.

Upvotes: 3

Related Questions