chris Frisina
chris Frisina

Reputation: 19688

Get a random list item in 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?

Upvotes: 4

Views: 5506

Answers (2)

Shon
Shon

Reputation: 4098

This answer offers a simple, effective solution to the problem posed in the title: "Get a random list item in Haskell".

The package Test.QuickCeck provides a number of helpful, straightforward functions for generating random values (http://hackage.haskell.org/package/QuickCheck-2.7.6/docs/Test-QuickCheck.html#g:5). A function that returns random values from a list (wrapped IO) can be built by composing the QuickTest functions elements and generate:

import Test.QuickCheck (generate, elements)

randItem :: [a] -> IO a
randItem = generate . elements

chris Frisina's function chooseRandomDecision would then look like this:

chooseRandomDecision :: [ThreeHistoryDecisionMaker] -> IO ThreeHistoryDecisionMaker
chooseRandomDecision = randItem

The user Cale in the #haskell channel on freenode helped coach me to this solution.

note: This solution works with QuickCheck 2.7.6, but needs some alteration for earlier versions. You can update to the latest version with cabal install QuickCheck. See this question.

Upvotes: 0

leftaroundabout
leftaroundabout

Reputation: 120711

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.

Upvotes: 13

Related Questions