在变压器堆栈内调用monadic函数

时间:2014-05-02 00:16:34

标签: haskell monads monad-transformers

我在Monad变形金刚的第一次破解。为我的班级称之为“设施位置”的问题写了一个简单的遗传算法。算法并不那么重要。

我通常遵循本章Real World Haskell中解释的格式。

我的变压器堆栈看起来像这样

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

后来在我的代码中,我定义了我的主要“步骤”动作,用于运行一轮交配和幸存。在我的步骤中,我可以轻松生成随机数并使用它。但是,我想将特定函数的随机性从步骤操作移到该函数中。不幸的是,我遇到类型错误,并且需要一些学校教育,为什么这不起作用,以及如何使这项工作。

你可能并不真的需要这个,但我的配偶功能只是将两个向量拼接在一起:

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

所以,这有效:

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

但我真的宁愿定义后代:

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)

然而,当我尝试使用类似

之类的步骤调用此函数时
(c1,c2) <- offspring p1 p2

编译器对我大吼大叫说Rand类型与预期类型FacilityApp不匹配...我觉得这对我有意义,但我不知道如何让这对我有用。

我想也许这需要某种类型的提升和返回?但我无法弄明白。有人可以告诉我这个问题吗?

作为一个附带问题,请注意我正在使用StateT来举行我的圆形计数器。这比向步骤添加参数要快。是否有更有效的方法来处理这个问题?

1 个答案:

答案 0 :(得分:3)

您的offspring函数返回Rand次计算。但是,Rand只是一个特定的MonadRandom个实例(RandT超过Identity),而不是您的FacilityApp monad。您应该将Rand更改为FacilityApp,或者,假设您只使用MonadRandom个功能,请将类型概括为任何MonadRandom

offspring :: (MonadRandom m) => CustomerService -> CustomerService
          -> m (CustomerService, CustomerService)