Использование MonadRandom с MonadState

У меня есть этот бит кода:

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 ()

И я хотел бы реорганизовать его подпись, чтобы она имела форму:

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

Я думал, что смогу оснастить RVar MonadState функциями:

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

и написать:

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 ()

Но я получаю эту необъяснимую ошибку:

    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. Пожалуйста, объясните ошибку и помогите сделать более общую подпись возможной?

  2. Если бы я хотел сделать:

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

Как бы я это реализовал? Я больше не могу использовать uniform. Потому что это привязывает меня к подписи RVar a, но я действительно хочу MonadRandom m => m a или, по крайней мере, Monad m => RVarT m a


person xiaolingxiao    schedule 16.12.2015    source источник
comment
Вы написали (uniform 0 1 :: RVar Double), но объявили тип функции m (). Монады должны совпадать — m должно быть равно RVar, что не так. Вы не можете не использовать сигнатуру (MonadRandom m, MonadState s m) => s -> m (), в частности потому, что функция uniform реализована для RVar, а не для какого-либо MonadRandom.   -  person user2407038    schedule 16.12.2015
comment
Конечно, вы можете иметь MonadState s m => s -> RVarT m (), просто используйте uniformT :: Distribution Uniform a => a -> a -> RVarT m a.   -  person user2407038    schedule 16.12.2015


Ответы (1)


uniform не является полиморфным в монаде, в которой он запускается (другими словами, вы не можете запустить его ни при каком выборе m, если все, что вы знаете, это RandomSource m s):

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

Однако, если у вас есть источник энтропии, вы можете runRVar его runRVar использовать в любом m случае RandomSource m s:

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

что означает, что вы можете написать foo с желаемой подписью типа как

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
person Cactus    schedule 17.12.2015