Reputation: 4895
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.
Please explain the error and help make the more generic signature possible?
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
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