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.