monads-tf:MonadState的MonadReader实例

时间:2011-09-03 11:15:04

标签: haskell monads typeclass monad-transformers type-families

考虑下一个例子。我的monad MyM只是StateT

{-# LANGUAGE TypeFamilies #-}

import Control.Monad.State
import Control.Monad.Reader

type MyS = Int
type MyM = StateT MyS

通常MyM用于读写MyS状态,所以我有类似下一个的功能:

f1 :: (MonadState m, StateType m ~ MyS) => m ()
f1 = modify (+1)

但有时我只需要阅读MyS,所以我想要MonadReader上下文而不是MonadState

f2 :: (MonadReader m, EnvType m ~ MyS) => m Int
f2 = liftM (+1) ask

我想写一些类似的东西:

f3 :: (MonadState m, StateType m ~ MyS) => m Int
f3 = f1 >> f2

所以基本上我需要每个MonadState实例都是MonadReader实例,并且具有相应的族类型。像

这样的东西
instance MonadState m => MonadReader where
  type EnvType m = StateType m
  ...

但是我找不到如何进行类型检查的方法。是否可以表达MonadStateMonadReader之间的关系?

感谢。

1 个答案:

答案 0 :(得分:4)

听起来你想要的基本上askget具有相同的效果。我不禁想知道你为什么不在这种情况下使用get:)

如果您的目标是编写根据可用内容读取状态或env的代码,则必须询问您打算如何处理,例如,ReaderT r (StateT s m) a,您同时拥有两者。出于这个原因,你不能只做:

instance MonadState m => MonadReader m where
  type EnvType m = StateType m
  ask = get

因为您将与现有实例发生冲突。但是,您可以这样做:

{-# LANGUAGE GeneralizedNewtypeDeriving, TypeFamilies #-}
newtype ReadState m a = RS { unRS :: m a }
  deriving (Monad)

instance MonadState m => MonadReader (ReadState m) where
  type EnvType (ReadState m) = StateType m
  ask = RS get
  local f (RS m) = RS $ do
    s <- get
    modify f
    x <- m
    put s
    return x

然后,如果您有像f2这样的多态读取器值,则可以使用MonadState从中提取unRS。如果您想使用更加狡猾的扩展程序,请使用RankNTypes

进行尝试
useStateAsEnv :: (MonadState n) => (forall m . (MonadReader m, EnvType m ~ StateType n) => m a) -> n a
useStateAsEnv m = unRS m

然后,您可以执行useStateAsEnv (liftM (+1) ask)并获得MonadState值。我没有彻底调查过这在实践中有多大用处 - 但是为了生成forall m. MonadReader m => m a类型的值,您几乎只能使用asklocal和monadic函数。< / p>

使用标准变换器,这是一个类似的,不那么通用但可能更有用的东西:

readerToState :: (Monad m) => ReaderT r m a -> StateT r m a
readerToState reader = StateT $ \env -> do
  res <- runReaderT reader env
  return (res, env)

编辑:稍后考虑这个问题你可以将它概括为任何带有lift . runReaderT reader =<< get的状态monad变换器,但类型开始相当笨重:

:: (Monad m, MonadTrans t, MonadState (t m)) => ReaderT (StateType (t m)) m b -> t m b

其中 是上述的概括,但实际上可能并不实用。