Reputation: 1381
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?
Upvotes: 0
Views: 131
Reputation: 34378
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)
Upvotes: 3