0

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

Cactus
  • 27,075
  • 9
  • 69
  • 149
xiaolingxiao
  • 4,793
  • 5
  • 41
  • 88
  • You wrote `(uniform 0 1 :: RVar Double)` but you declared the type of the function to be `m ()`. The monads must match - `m` must be equal to `RVar`, which it is not. You may *not* use the signature `(MonadRandom m, MonadState s m) => s -> m ()` in particular because the `uniform` function is implemented for RVar, not for any MonadRandom. – user2407038 Dec 16 '15 at 16:50
  • You can have `MonadState s m => s -> RVarT m ()`, of course, simply use `uniformT :: Distribution Uniform a => a -> a -> RVarT m a`. – user2407038 Dec 16 '15 at 16:51

1 Answers1

3

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
Cactus
  • 27,075
  • 9
  • 69
  • 149