I am trying to use
MonadRandom. I put it into
randomPref function, but the whole thing brows up afterwards! Any tips are appreciated.
module AgentGenerator where import System.Random import Data.Hashable import Control.Monad.Random import System.Environment -- Generate agents and write to a file -- 'fname' - output filename -- 's' - number of agent sets -- 'n' - number of agents in a set -- 'x' - number of preferences per agent generate fname s n x = do writeFile fname $ show $ generateAgentSets s n x -- Agent type: Name, List of preferences data Agent = Agent String [Double] deriving Show type AgentSet = [Agent] -- Generate 's' sets of 'n' agents each, with 'x' preferences each generateAgentSets:: Integer -> Integer -> Integer -> [AgentSet] generateAgentSets s n x = [generateAgents n x | i <- [1..s] ] -- Generate n agents with 'x' preferences each generateAgents:: Integer -> Integer -> AgentSet generateAgents n x = [createAgent (show i) x | i <- [1..n]] -- Create agent 'name' with 'x' preferences createAgent:: String -> Integer -> Agent createAgent name x = Agent name prefs where prefs = [ randomPref (i + hashed) | i <- [1..x] ] where hashed = fromIntegral ( hash name ) -- Generate single random value between [0, 1] based on the seed -- TODO: Get rid of the seed thing and use MonadRandom instead randomPref :: (RandomGen g) => Integer -> Rand g [Double] randomPref seed = getRandomR (0.0, 1.0)Answer1:
data Agent = Agent String [Double]
createAgent, you are trying to construct an
Agent using the types
Agent String [Rand g [Double]]
Another type error is that in
randomPref, the signature says that you are generating a list of random doubles, but you only generate one double. I'm also not 100% sure how the function should work, given that you never use the seed value anywhere. You either want to return a
Rand monad or take a seed value and use that to generate a plain double. Having both doesn't really make sense.
Here's a version that uses a seed, and returns a plain double
randomPref :: Integer -> Double randomPref seed = evalRand (getRandomR (0.0, 1.0)) g where g = mkStdGen (fromIntegral seed)
System.Random here as an example, but you might want to replace it with some other instance of
However, the above is a pretty questionable use of
MonadRandom and unless it's really important to generate each agent with a specific seed, it's probably more logical to implement
randomPref like this
randomPref :: RandomGen g => Rand g Double randomPref = getRandomR (0.0, 1.0)
Now we don't take in a seed value, we just declare that
randomPref is a random double. However, you can't just use a random double like it was a regular double, so we need to change the other functions too. First,
createAgent:: RandomGen g => String -> Int -> Rand g Agent createAgent name x = Agent name <$> prefs where prefs = replicateM x randomPref
We change the signature to reflect the fact that we are actually returning a random
<$> operator is from the module
Control.Applicative and it is used to apply a function expecting a plain value to a
Rand value. This is just a nicer way to write
fmap (Agent name) prefs.
prefs is defined in terms of
replicateM (from the module Control.Monad), which replicates a monadic value x times, so you get x random prefs. On another note, I've changed all the functions to use
Int values instead of
Integers. Unless you really want to generate billions of agents, this makes the code a lot faster, and many standard library functions (like
replicateM) only accept machine ints.
generateAgents:: RandomGen g => Int -> Int -> Rand g AgentSet generateAgents n x = mapM (\i -> createAgent (show i) x) [1..n]
generateAgents is changed in a similar way. We note in the signature that we are returning a random
AgentSet, and change the list comprehension into
mapM is like the standard
map function, except that is works with functions that return monadic values (such as
generateAgentSets:: RandomGen g => Int -> Int -> Int -> Rand g [AgentSet] generateAgentSets s n x = replicateM s (generateAgents n x)
generateAgentSets follows the same routine. We've replaced the list comprehension with
replicateM to generate s instances of random agent sets.
The biggest change is required in the
generate :: RandomGen g => FilePath -> Int -> Int -> Int -> g -> IO () generate fname s n x g = do let randomSets = generateAgentSets s n x agentSets = evalRand randomSets g writeFile fname $ show agentSets
We need to pass in a random number generator, which is then used with
evalRand to turn the
Rand AgentSet values into plain
AgentSet values that can then be written to disk.
To get a better understanding why we need
<$> and functions such as
replicateM instead of plain old list comprehensions, you might want to read <a href="http://learnyouahaskell.com/functors-applicative-functors-and-monoids#functors-redux" rel="nofollow">Chapter 11</a> and <a href="http://learnyouahaskell.com/a-fistful-of-monads#getting-our-feet-wet-with-maybe" rel="nofollow">Chapter 12</a> from <a href="http://learnyouahaskell.com/" rel="nofollow">Learn you a Haskell for Great Good</a>.