7

I am with trouble related to Haskell Random generator. At university, i have to deal with Java all my way around, so now I'm, corrupted with it. I am developing a game in Haskell, and now I face something like 'chance to do something', and that chance needs to be like Int -> Bool. In Java, I would have done

new Random().nextInt(100) 

and there, problem solved! In Haskell I have to choose something in a monad IO or something with a seed. None of these does what I want. I don't really want to use IO monad in my pure model, and the seed is awkward to use because I need to remember my new seed every time...

Is there something simple like Java's Random?

Landei
  • 54,104
  • 13
  • 100
  • 195
Illiax
  • 1,002
  • 1
  • 8
  • 21
  • 8
    Using the IO Monad does not mean that you have to corrupt with IO your whole model... Just get your seed right at the beginning, and give it to your pure computations... Your program will be of type IO () remember ? So you're already in the IO Monad... (if your game is 100% pure, then it must not be really fun to play!) – Ptival Sep 07 '11 at 06:17
  • 1
    You'll 'corrupt' your self soon or later, a game need I/O from player :D – Zhen Sep 07 '11 at 07:25
  • 2
    The top-rated answer on this question: [http://stackoverflow.com/questions/2110535/sampling-sequences-of-random-numbers-in-haskell](http://stackoverflow.com/questions/2110535/sampling-sequences-of-random-numbers-in-haskell) gives an excellent write-up of how to thread a stateful random number generator through code. – stusmith Sep 07 '11 at 10:57
  • "I don't really want to use IO monad in my pure model". In Java, `new Random()` is far from pure. And every time you use `nextInt`, your `Random` object mutates. So your goals of 1) a pure model, and 2) something like Java's Random, are in conflict since Java's Random is not pure. – Dan Burton Sep 07 '11 at 14:54
  • java is soo far from pure model as i see it :P, i meant that. but indeed, I was focused the wrong head, my head is already corrupt! thanks for andswers and comments everybody! – Illiax Sep 07 '11 at 16:20

4 Answers4

12

Believe it or not, you'll have to use different approaches in Haskell than you did in Java. There are a couple packages that can help you, but you will have to get a different attitude in your head to use them successfully. Here are some pointers:

Searching for the word "random" on Hackage's package list will turn up many, many more specific packages for more specific needs.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • 2
    You don't "have to". You can use IO everywhere as you did in Java. – Rotsor Sep 07 '11 at 09:06
  • @Rotsor: Although unless you also put everything into `ST` (monad transformers are fun), you still need to cope with immutability. And even then it's rather ugly and pointless, so much that I'd expect any sane programmer to abandon it. –  Sep 07 '11 at 09:41
  • 2
    @delnan, I agree that it's ugly and pointless (the same way programming in Java is), but don't see how `ST` is necessary. `IO` can do everything `ST` can and more. – Rotsor Sep 07 '11 at 10:01
  • 1
    @Rotsor Except that IO can't encapsulate the effects, which is the whole point of ST. – augustss Sep 07 '11 at 12:50
  • 2
    @augustss: Ah, but encapsulating effects isn't a matter of what `IO` can't do! Rather, it's about what `IO` can't *not* do. Not doing things in very precise ways is more difficult, yet often more effective, than simply doing whatever one pleases all the time. – C. A. McCann Sep 08 '11 at 22:10
6

Sorry, but you will have to live with that. How can there be a function in a pure functional language that gives you different values on each call? Answer is: it cannot - only in the IO-Monad or something similiar like the state-monad where you can pass your seed around (and don't have the same input every time) can such a things exist.

You may alsow have a look as this question "How can a time function exist in functional programming?" as it's in the same direction as yours.

Community
  • 1
  • 1
Random Dev
  • 51,810
  • 9
  • 92
  • 119
  • 3
    I down voted you because you didn't really give a solution. The obvious solution is to use MonadRandom – Axman6 Sep 07 '11 at 08:10
  • well thank you ... but the question was if there is something simple and he don't like IO Monad ... do you think MonadRandom is so much better then? – Random Dev Sep 07 '11 at 08:14
  • 5
    @Carsten, of course it is much better because it does not allow arbitrary IO. – Rotsor Sep 07 '11 at 09:08
  • nevermind ... I had the understanding that the OP don't wanted to enter "monad"-zone or "do"-notation or whatever at all – Random Dev Sep 07 '11 at 09:36
3

I think, "you will have to live with that", is neither useful nor correct. It really depends on the abstractions you are using. If your application is naturally bound to a monad, then it makes sense to use a monadic random number generator, which is just as convenient as Java's random number generator.

In the case of a game using modern abstractions your application is naturally bound to functional reactive programming (FRP), where generating random numbers is no problem at all and doesn't require you to pass around generators explicitly. Example using the netwire library:

movingPoint :: MonadIO m => (Double, Double) -> Wire m a (Double, Double)
movingPoint x0 =
    proc _ -> do
        -- Randomly fades in and out of existence.
        visible <- wackelkontakt -< ()
        require -< (visible, ())

        -- 'rnd' is a random value between -1 and 1.
        rnd <- noise1 -< ()

        -- dx is the velocity.
        let dx = (sin &&& cos) (rnd * pi)

        -- Integration of dx over time gives us the point's position.
        -- x0 is the starting point.
        integral x0 -< dx

Is there any way to express this easier and more concisely? I guess not. FRP also proves Zhen's comment wrong. It can handle user input purely.

ertes
  • 2,287
  • 15
  • 10
2

It is somewhat unintuitive that something that neither does input nor output needs to be handled as if it had. Let's say you defined it as follows:

random100 = unsafePerformIO $ randomRIO (1, 100) -- This will not work!

That would indeed give you a random number - in a way. What you truly need is a way to encode that you want a new pseudo-random number every time. This means information needs to go from one random number generation to the next. Most languages just ignore this "minor detail", but Haskell forces you to pay attention. You might thank Haskell when you find yourself in the spot to properly reproduce your pseudo-random result in a multi-threaded context.

There's a number of ways you can make these connections, most of which have been mentioned already. If you are reluctant to use a monad: Note that it might generally be a good thing to have your code in a monadic form (but not using IO!). Down the road, you might well come into situations where you want more monad features, such as a reader for configuration - then all ground work would be done already.

Peter Wortmann
  • 2,272
  • 14
  • 14
  • 1
    There should be a way to mark code as non-solutions. Because it's too easy to just see the grey box and think that's the answer. – augustss Sep 07 '11 at 12:51
  • @augustss, huh? I'm not sure how to take your comment. The question was "Is there something simple like this?". A small illustration just seemed like a good idea. – Peter Wortmann Sep 07 '11 at 13:35
  • Yeah, but this simple illustration, while it "works", will be considered wrong by a good 99% of the community. I think @augustss would like to draw attention to that fact. – luqui Sep 07 '11 at 19:01
  • 1
    @Peter I was just making a general remark about stackoverflow. Sometimes you want to make code fragments that illustrate how not to do something. I can see from your answer that you are not suggesting that this is the right way. I'm just worried that people might not read the text and just copy the code. – augustss Sep 08 '11 at 10:53