[Haskell-cafe] working with Random.randoms

Daniel Fischer daniel.is.fischer at web.de
Fri Jun 13 13:05:21 EDT 2008


Am Freitag, 13. Juni 2008 18:38 schrieb Stephen Howard:
> Hi List,
>
> I am a newcomer doing my obligatory struggling with Haskell's type
> system, and I've got a nut I've not been able to crack.  Given:
>
> import Random
>
> random_test :: Int -> String
> random_test n = do
>     g <- getStdGen
>     take n (randoms g)::String
>
> I'm expecting that I ought to be able to pass this action an integer and
> get back a random string that long (given, not all characters may be
> printable).
>
> But GHCI tells me:
>
> RandomTest.hs:7:4:
>     Couldn't match `[]' against `IO'
>       Expected type: []
>       Inferred type: IO
>     In a 'do' expression: g <- getStdGen
>     In the definition of `random_test':
>         random_test n = do
>                           g <- getStdGen
>                             take n (randoms g) :: String
>
> And yet, when I run these lines in GHCI by hand, things seem to work
> (though the string is the same set of random characters each time,
> another bit that I need to solve):
>
> Prelude> :module Random
> Prelude Random> g <- getStdGen
> Prelude Random> take 5 (randoms g)::String
> "\1025049\315531\882767\1032009\334825"
>
>
> I'm guessing that randoms is returning an IO type but I'm not sure how

No, getStdGen is what's in IO, it has type IO StdGen. Since you can't get rid 
of IO safely, the type of random_test must be Int -> IO String.
Best practice is to separate into a pure part:

pure_random_test :: Int -> StdGen -> String
pure_random_test n g = take n (randoms g)

and the IO bound part:

random_test :: Int -> IO String
random_test n = do
	g <- getStdGen
	return $ pure_random_test n g

or, if we like it better:

random_test n = fmap (pure_random_test n) getStdGen

> to go about extracting the String to return to the calling action.
> Changing the type signature to Int -> IO  String only gives me a
> different error.
>
> Where am I going wrong?

You must put the String (take n (randoms g)) into the IO monad.

The way it works in ghci is due to the fact, that in ghci, you are basically 
in an IO loop/do-block.
When you type 
g <- getStdGen
the action is performed, and the result is bound to the identifier g.
When you then type
take 5 (randoms g),
it's transformed into
do .....
    let it = take 5 (randoms g)
    print it
    return it

Within one session, all calls to getStdGen return the same StdGen, that's why 
you always get the same sequence of random Chars.
>
> thanks,
> Stephen

HTH,
Daniel



More information about the Haskell-Cafe mailing list