9787

How to refactor code to use MonadRandom

Question:

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:

You've defined Agent as

data Agent = Agent String [Double]

But in 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)

I've used mkStdGen from System.Random here as an example, but you might want to replace it with some other instance of RandomGen.

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

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 Agent. The <$> 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. mapM is like the standard map function, except that is works with functions that return monadic values (such as Rand).

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 function

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 fmap/<$> and functions such as mapM and 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>.

Recommend

  • Is it possible to disable Django two-factor authentication with a setting?
  • The method addPreferencesFromResource(int) from the type PreferenceActivity is deprecated
  • Windows Phone ANID to ANID2 conversion on C#?
  • WordPress - Add/Edit Post Screen Options not showing Categories
  • Confusion with virtualenvs and Python packages
  • What is meant by ?? in Dart
  • Any gotchas I should be aware of regarding this approach to persistent logins (“Remember Me”)?
  • Hide full path to project in Project browser?
  • Server-Client malfunction in Android (socket is not bound/socket is already in use)
  • Exporting SAS DataSet on to UNIX as a text file…with delimiter '~|~'
  • parallelize process in missForest package
  • Can't zip RDDs with unequal numbers of partitions
  • How to clear specified format data from clipboard?
  • concatenating select menus into a single form input
  • Initializing a class using malloc
  • Slowly changing dimension - What is Pure type 6 implementation
  • How to run a set of SQL queries from a file, in PHP?
  • Set the default timezone in symfony
  • Is WITH the replacement for a #TEMP table?
  • Can I use Jquery to automatically find and set the width and height of a variety of images?
  • Can a Collections.shuffle be considered equivalent to a series of Randoms?
  • custom string delimiters stringtemplate-4
  • Problem with Django using Apache2 (mod_wsgi), Occassionally is “unable to import from module” for no
  • Set focus to first invalid form element in AngularJS
  • Find group of records that match multiple values
  • Authentication in Play! and RestEasy
  • Use of this Javascript
  • C++ Partial template specialization - design simplification
  • Scrapy recursive link crawler
  • Sails.js/waterline: Executing waterline queries in toJSON function of a model?
  • NetLogo BehaviorSpace - Measure runs using reporters
  • How to handle AllServersUnavailable Exception
  • Redux, normalised entities and lodash merge
  • Do create extension work in single-user mode in postgres?
  • How to get next/previous record number?
  • Comma separated Values
  • Python: how to group similar lists together in a list of lists?
  • Error creating VM instance in Google Compute Engine
  • Hits per day in Google Big Query
  • how does django model after text[] in postgresql [duplicate]