
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