working with Random.randoms

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 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? thanks, Stephen

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

Stephen Howard
I am a newcomer doing my obligatory struggling with Haskell's type system,
That's your mistake. Don't struggle with the type system, the type system is your friend; when it tells you you are doing something wrong, it's usually right.
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
My immediate reaction on seeing this is that "do" is for manipulating monads, but the only monad you've indicated in the type is [] (from String = [Char]), and that's probably an accident. In GHCi: Prelude> :t Random.getStdGen Random.getStdGen :: IO System.Random.StdGen What this says is that getStdGen isn't a function, which is to say it doesn't always return the same value (ie it isn't pure -- it wouldn't be much use if it were!). In effect it uses the real world to get a new value, and such things are kept safely in the IO monad. In C, everything is in the IO monad; there's no way of telling from the type whether something is pure or not¹. In Haskell (apart from some ugliness that's supposed to stay hidden) you have to distinguish between pure and impure, and the type checker keeps track of all this for you.
And yet, when I run these lines in GHCI by hand,
The top level of GHCi is IO (which shouldn't come as a surprise!)
things seem to work (though the string is the same set of random characters each time, another bit that I need to solve
That's a somewhat obscure aspect of GHCi, I think, to do with "not reverting top-level expressions". If you do this: Prelude> :set +r Prelude> g <- Random.getStdGen -- what's the type? 1954047482 7573 Prelude> g <- Random.getStdGen -- what's the type? 1626678354 7697 Prelude> you see that you get a different StdGen each time.
I'm guessing that randoms is returning an IO type but I'm not sure how 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.
If you do any IO, you've done some IO! So you need to lift out the StdGen and pass that to the functions that you want to use it. main :: IO() do gen <- getStdGen the_list <- real_programme gen print the_list You should be able to deduce from the fact that the first argument of real_programme here is of type StdGen (not IO anything) and its result type is [something] that real_programme is pure, and gen has only one value throughout. So if you want more than one random list out of it, you either need to use Random.split or pass more than one gen in. [1] Were you truly perverse, you could define your own libraries in Haskell so that YourInt = IO Int; YourDouble = IO Double etc, and make appropriate classes in place of Num &c (hiding the proper ones), so that + :: YourInt -> YourInt -> YourInt and so on (you'd have to define your own Bool and if, too, but Haskell can do that). Then the type checker would overlook all sorts of mistakes that it could otherwise have caught. -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk http://www.chaos.org.uk/~jf/Stuff-I-dont-want.html (updated 2008-04-26)

Great reply! One minor point: If real_programme is to be pure, you should use let: On Jun 14, 2008, at 12:30, Jon Fairbairn wrote:
main :: IO() do gen <- getStdGen the_list <- real_programme gen let the_list = real_programme gen print the_list
You should be able to deduce from the fact that the first argument of real_programme here is of type StdGen (not IO anything) and its result type is [something] that real_programme is pure, and gen has only one value throughout.
Cheers Robert

Robert Vollmert
Great reply!
Thanks.
One minor point: If real_programme is to be pure, you should use let:
Whoops! I was thinking "let" but wrote the wrong thing. If my email had been through a type-checker, it would have spotted the mistake. -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk http://www.chaos.org.uk/~jf/Stuff-I-dont-want.html (updated 2008-04-26)

Thanks for the replies. This was my solution: module RandomTest ( random_test ) where import Random random_test :: Int -> IO String random_test n = do g <- newStdGen return $ take n (randomRs printable_ascii g) where printable_ascii = ('!','~') The "struggling with the type system" was supposed to be tongue-in-cheek. I trust it, it just takes some time to get used to it. And I appreciate Dan's comments about separating the pure code from the non-pure, but in this case the code was succinct enough that one more layer of functions seemed to do more harm than good when it came to legibility. - Stephen Jon Fairbairn wrote:
Stephen Howard
writes: I am a newcomer doing my obligatory struggling with Haskell's type system,
That's your mistake. Don't struggle with the type system, the type system is your friend; when it tells you you are doing something wrong, it's usually right.
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
My immediate reaction on seeing this is that "do" is for manipulating monads, but the only monad you've indicated in the type is [] (from String = [Char]), and that's probably an accident.
In GHCi:
Prelude> :t Random.getStdGen Random.getStdGen :: IO System.Random.StdGen
What this says is that getStdGen isn't a function, which is to say it doesn't always return the same value (ie it isn't pure -- it wouldn't be much use if it were!). In effect it uses the real world to get a new value, and such things are kept safely in the IO monad. In C, everything is in the IO monad; there's no way of telling from the type whether something is pure or not¹. In Haskell (apart from some ugliness that's supposed to stay hidden) you have to distinguish between pure and impure, and the type checker keeps track of all this for you.
And yet, when I run these lines in GHCI by hand,
The top level of GHCi is IO (which shouldn't come as a surprise!)
things seem to work (though the string is the same set of random characters each time, another bit that I need to solve
That's a somewhat obscure aspect of GHCi, I think, to do with "not reverting top-level expressions". If you do this:
Prelude> :set +r Prelude> g <- Random.getStdGen -- what's the type? 1954047482 7573 Prelude> g <- Random.getStdGen -- what's the type? 1626678354 7697 Prelude>
you see that you get a different StdGen each time.
I'm guessing that randoms is returning an IO type but I'm not sure how 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.
If you do any IO, you've done some IO! So you need to lift out the StdGen and pass that to the functions that you want to use it.
main :: IO() do gen <- getStdGen the_list <- real_programme gen print the_list
You should be able to deduce from the fact that the first argument of real_programme here is of type StdGen (not IO anything) and its result type is [something] that real_programme is pure, and gen has only one value throughout. So if you want more than one random list out of it, you either need to use Random.split or pass more than one gen in.
[1] Were you truly perverse, you could define your own libraries in Haskell so that YourInt = IO Int; YourDouble = IO Double etc, and make appropriate classes in place of Num &c (hiding the proper ones), so that
+ :: YourInt -> YourInt -> YourInt
and so on (you'd have to define your own Bool and if, too, but Haskell can do that). Then the type checker would overlook all sorts of mistakes that it could otherwise have caught.

On Fri, 13 Jun 2008, Stephen Howard wrote:
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).
If you only need an arbitrary sequence and it is ok for you to get always the same one, you can construct a random generator using mkStdGen.
participants (5)
-
Daniel Fischer
-
Henning Thielemann
-
Jon Fairbairn
-
Robert Vollmert
-
Stephen Howard