2

I have a game , user vs computer and I want to randomly choose who starts the game. I have

a = getStdRandom $ randomR (0, 1)

This gets a random number 0 or 1. However it is a IO Int, so I can't have an if statement comparing it to a number like

if a == 0 then userStarts else computerStarts 

I have tried to compare IO Int with IO Int and it doesn't work, and I have also tried

Converting IO Int to Int

I am very new to Haskell, not sure how to approach this. Code details requested:

randomNumber =  getStdRandom $ randomR (0, length symbols - 5) --  this will be 0 or 1
randomNumber2 =  getStdRandom $ randomR (0, length symbols - 5) -- according to 
                     -- the solution I need another function returning IO int.

a = do
   x <- randomNumber
   randomNumber2 $ pureFunction x

Error I get:

• Couldn't match expected type ‘t0 -> IO b
                  with actual type ‘IO Int’
    • The first argument of ($) takes one argument,
      but its type ‘IO Int’ has none
      In a stmt of a 'do' block: randomNumber2 $ pureFunction x
      In the expression:
        do x <- randomNumber
           randomNumber2 $ pureFunction x

    • Relevant bindings include
        a :: IO b
          (bound at Path:87:1)

    randomNumber2 $ pureFunction x

Path:89:20: error:
    Variable not in scope: pureFunction :: Int -> t0

     randomNumber2 $ pureFunction x
Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • 3
    have you tried using a `do` block as recommended by the first answer to the linked question? Because that (along with the `>>=` operator, for which the `do` block is just syntactic sugar) is the only way to do it. – Robin Zigmond Feb 06 '19 at 16:08
  • I tried but I get the error that pureFunction is not in scope, and why do I need two functions returning an IO int? –  Feb 06 '19 at 16:15
  • 3
    An `Int` is a fixed, constant integer value, An `IO Int` is essentially a routine which can be called many times, and which will generate an integer value each time (potentially, distinct values will be returned). `do` blocks, applicative/monadic helpers, etc. help in calling that routine and get the actual integer, roughly put. – chi Feb 06 '19 at 16:18
  • For `randomNumber2 $ pureFunction x` to make sense you need `randomNumber2` to have type `Something -> IO Int` and `pureFunction` to be `Int -> Something`. `randomNumber2` is just `IO Int`. – Alexey Romanov Feb 07 '19 at 07:06
  • Related: https://stackoverflow.com/questions/41522491/is-there-a-way-to-place-some-impure-code-inside-pure-functions . – atravers Nov 17 '20 at 06:11

2 Answers2

10

When you say a = getStdRandom $ randomR (0,1) you are saying "let a be the action of getting a random value between 0 and 1". What you want is within some function's do block a <- getStdRandom $ randomR (0,1) which is "let a be the result of running the action of getting a random value between 0 and 1".

As such:

import System.Random

main :: IO ()
main = do
  a <- getStdRandom $ randomR (0, 1 :: Int)
  if a == 0 then userStarts else computerStarts

-- Placeholders for completeness
userStarts, computerStarts :: IO ()
userStarts = putStrLn "user"
computerStarts = putStrLn "computer"

N.B. I specified the 1 is an int or else the compiler won't know if you want a random int, int64, double, float, or something else entirely.

EDIT: @monocell makes a good point that generating an int in a range just to get a boolean is somewhat indirect. You can just directly generate a boolean result and this requires no range:

  a <- getStdRandom random
  if a then userStarts else computerStarts
Thomas M. DuBuisson
  • 64,245
  • 7
  • 109
  • 166
2

Not sure how your code looks like, but have you tried doing what the linked resource recommends (using the do block)?

do
   (result, newGenerator) <- randomR (0, 1) generator
   -- ...

That will get you access to result, which is of the same type as 0 and 1.

Can you show your code/the error you get?

Piotr Justyna
  • 4,888
  • 3
  • 25
  • 40