Question

Took my first crack at Monad Transformers. Wrote a simple genetic algorithm for what my class calls the "facility location" problem. The algorithm is not so important.

I generally followed the format explained in this chapter of Real World Haskell.

My transformer stack looks like this

newtype FacilityApp a = MyA {
   runA :: RandT StdGen ( ReaderT Environment ( StateT (Population, Int) IO)) a
  } deriving (Monad, MonadIO, MonadReader Environment, MonadState (Population,Int),     MonadRandom)

runFacility :: FacilityApp a -> Input -> StdGen -> IO a
runFacility k input g =
  let env = mkEnv input defParms
      (pop, g') = runRand (genInitialPopulation env) g
      state = (pop, numRounds defParms)
      ra = runA k
      rr = runReaderT rrand env
      rs = evalStateT rr state
      rrand = evalRandT ra g'
  in rs

Later in my code, I define my main "step" action for running a round of mating and surviving. In my step action, I have no trouble generating a random number and using that. However, I'd like to move the randomness of a particular function out of the step action and into that function. Unfortunately I'm getting type errors, and need some schooling on both why this doesn't work, and how to make this work.

You probably don't really need this, but my mate function just stitches two vectors together:

mate :: CustomerService -> CustomerService -> Int -> CustomerService      
mate a b split = (fst $ V.splitAt split a) V.++ (snd $ V.splitAt split b)  

So, this works:

offspring' :: CustomerService -> CustomerService -> Int -> (CustomerService, CustomerService)
offspring' a b split = (mate a b split, mate b a split)  

step :: FacilityApp [CustomerService]
step = do
  (pop, n) <- get
  env <- ask
  let e = (warehouses env, customers env)
  let sorted@(p1:p2:_) = sortBy (sortByCost e) $ filter (validSolution e) pop  
  let (_:_:rest) = reverse sorted
-- these next two lines are of my concern
  split <- getRandomR (1, V.length p1)
  let (c1,c2) = offspring' p1 p2 split
-- eventually put the new children in the state and step again
  put (c1:c2:rest, (n-1))
  if n > 0 then step else return pop

BUT I would really rather define offspring like so:

offspring :: (RandomGen g) => CustomerService -> CustomerService -> Rand g (CustomerService, CustomerService)
offspring a b = do
  split <- getRandomR (1, V.length a)
  return (mate a b split, mate b a split)

However when I try to call this function in my step action with something like

(c1,c2) <- offspring p1 p2

The compiler yells at me that type Rand doesn't match expected type FacilityApp... which I guess makes sense to me, but I'm not sure how to make this work for me.

I think maybe this needs some type of lifting and returning? But I can't figure it out. Can someone walk me through the problem here?

As a side question, notice that I am using the StateT to hold my round counter. This is faster than adding an argument to step. Is there an even more efficient way to take care of this?

Was it helpful?

Solution

Your offspring function returns a Rand computation. Rand, however, is just a specific MonadRandom instance (is is RandT over Identity), and not your FacilityApp monad. You should either change Rand to FacilityApp or, given that you are only using MonadRandom functions, generalise the type to any MonadRandom:

offspring :: (MonadRandom m) => CustomerService -> CustomerService
          -> m (CustomerService, CustomerService)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top