xiaolingxiao
xiaolingxiao

Reputation: 4895

Using MonadRandom with MonadState

I have this bit of code:

import Data.Random
import Control.Monad.State

foo :: s -> StateT s RVar ()
foo s = do
  p <- lift $ (uniform 0 1 :: RVar Double)
  if p > 0.5 then put s else return ()

And I would like to refactor its signature to be of form:

foo :: (MonadState s m, RandomSource m s) => s -> m ()

I thought I could equip RVar with MonadState functions:

{- LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances #-}
instance MonadState s m => MonadState s (RVarT m) where
  get   = lift get
  put   = lift . put
  state = lift . state

and write:

foo :: (MonadState s m, RandomSource m s) => s -> m ()
foo s = do
  p <- (uniform 0 1 :: RVar Double)
  if p > 0.5 then put s else return ()

But I am getting this inexplicable error:

    Couldn't match type ‘m’
                   with ‘t0 (RVarT Data.Functor.Identity.Identity)’
      ‘m’ is a rigid type variable bound by
          the type signature for
            foo :: (MonadState s m, RandomSource m s) => s -> m ()
          at ApproxMedian.hs:99:8
    Expected type: m Double
      Actual type: t0 (RVarT Data.Functor.Identity.Identity) Double
    Relevant bindings include
      foo :: s -> m () (bound at ApproxMedian.hs:100:1)
    In a stmt of a 'do' block: p <- lift $ (uniform 0 1 :: RVar Double)
    In the expression:
      do { p <- lift $ (uniform 0 1 :: RVar Double);
           if p > 0.5 then put s else return () }
    In an equation for ‘foo’:
        foo s
          = do { p <- lift $ (uniform 0 1 :: RVar Double);
                 if p > 0.5 then put s else return () }
Failed, modules loaded: Core, Statistics.
  1. Please explain the error and help make the more generic signature possible?

  2. If I wanted to do:

    foo :: (MonadRandom m, MonadState s m) => s -> m ()
    

How would I implement it? I cannot use uniform any more. Because it locks me to signature RVar a but I really want MonadRandom m => m a, or at the very least Monad m => RVarT m a

Upvotes: 0

Views: 174

Answers (1)

Cactus
Cactus

Reputation: 27626

uniform is not polymorphic in the monad it runs in (in other words, you can't run it in any choice of m if all you know is that RandomSource m s):

uniform :: Distribution Uniform a => a -> a -> RVar a

However, if you have a source of entropy, you can runRVar it in any m if RandomSource m s:

runRVar :: RandomSource m s => RVar a -> s -> m a

which means you can write foo with your desired type signature as

foo :: (MonadState s m, RandomSource m s) => s -> m ()
foo s = do
  p <- runRVar (uniform 0 1 :: RVar Double) s
  when (p > 0.5) $ put s

Upvotes: 3

Related Questions