Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get a random list item in Haskell

Tags:

haskell

After reviewing this SO question I am trying to use the random number generator to return a random list item based on the return of the randomIO generator.

Full Code:

module Randomizer where
import System.IO
import System.Random

data Action = Create | Destroy
  deriving (Enum, Eq, Show)

type History = [Action]

-- | this looks at three sets of histories, and returns an appropriate Action
type ThreeHistoryDecisionMaker = History -> History -> History -> Action

allThreeDecisionMakers :: [ThreeHistoryDecisionMaker]
allThreeDecisionMakers = [decision1, decision2, decision3, decision4, decision5]

chooseRandomDecision :: [ThreeHistoryDecisionMaker] -> Int -> Strategy3P
chooseRandomDecision = allThreeDecisionMakers !! randomIO(0,4) 

But I get the following errors:

special_program1.hs:249:16:
    Couldn't match type ‘Action’
                  with ‘History -> History -> History -> Action’
    Expected type: [[ThreeHistoryDecisionMaker] -> Int -> ThreeHistoryDecisionMaker]
      Actual type: [ThreeHistoryDecisionMaker]
    In the first argument of ‘(!!)’, namely ‘allThreeDecisionMakers’
    In the expression: all3PStrategies !! randomIO (0, 4)

special_program1.hs:249:35:
    Couldn't match expected type ‘(t0, t1) -> Int’
                with actual type ‘IO a0’
    The function ‘randomIO’ is applied to one argument,
    but its type ‘IO a0’ has none
    In the second argument of ‘(!!)’, namely ‘randomIO (0, 4)’
    In the expression: all3PStrategies !! randomIO (0, 4)

Why is the first error block wanting to expect a list of everything inside it?

What does the second code block mean?

like image 777
chris Frisina Avatar asked Dec 14 '22 19:12

chris Frisina


1 Answers

randomIO is not a "random function". Such a thing doesn't exist in Haskell, it wouldn't be referentially transparent. Instead, as the name suggests, it's an IO action which can yield a random value. It makes no sense to index a list with an IO action, !! randomIO(0,4) isn't possible. (It's impossible also for another reason: randomIO creates unlimited values, you want randomRIO (with an R for "range parameter") if you need to specify a (0,4) range.)

What you need to to do to get the value yielded by the action: well, monads! If you haven't learned the theory about those yet, never mind. A random-indexer could look thus:

atRandIndex :: [a] -> IO a  -- note that this is gives itself an IO action
atRandIndex l = do
    i <- randomRIO (0, length l - 1)
    return $ l !! i

I suggest you actually use that function to implement your task.

But back to the code you posted... there's more problems. If you specify the type of chooseRandomDecision with two arguments, then you need to actually define it as a function of these arguments! But your definition doesn't accept any arguments at all, it merely uses the globally-defined list allThreeDecisionMakers (use of global variables never needs to be stated in the type).

Moreover, if you're choosing from a list of THDMakers, then the resulting element will also have that type, what else! So unless Strategy3P is simply another synonym of History -> History -> History -> Action, this won't do as a result, even if you contain it in the right monad.

like image 76
leftaroundabout Avatar answered Dec 28 '22 06:12

leftaroundabout