monads-tf:MonadState 的 MonadReader 实例

发布于 2024-12-02 22:21:33 字数 992 浏览 2 评论 0原文

考虑下一个例子。我有一个单子 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 之间的关系?

谢谢。

Consider the next example. I have a monad MyM that is just a StateT

{-# LANGUAGE TypeFamilies #-}

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

type MyS = Int
type MyM = StateT MyS

Usually MyM is used for reading and writing MyS state, so I have functions like the next:

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

But sometimes I need just to read MyS, so I want MonadReader context instead of MonadState:

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

And I want to write something like:

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

So basically I need every MonadState instance to be MonadReader instance too with the correspondent family type. Something like

instance MonadState m => MonadReader where
  type EnvType m = StateType m
  ...

But I can't find the way how to make it type check. Is it possible to express such the relation between MonadState and MonadReader?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

盛夏已如深秋| 2024-12-09 22:21:33

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

如果你的目标是编写根据可用内容读取状态或环境的代码,那么你有询问您打算如何处理,例如 ReaderT r (StateT sm) 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 这样的多态读取器值,您可以使用 unRS 从其中提取 MonadState。如果您想使用一些更狡猾的扩展,请尝试使用 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< /代码> 值。然而,我还没有彻底研究过这在实践中有多有用——为所有 m 生成 类型的值。 MonadReader m => m a,您几乎只能使用 asklocal 和 monadic 函数。

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

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 阅读器 =<< get,但该类型开始变得相当笨拙:

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

这是上述内容的概括,但实际上可能并不是一个有用的类型。

It sounds like what you want is essentially ask to have the same effect as get. I can't help but wonder why you don't just use get in that case :)

If your aim is to write code that either reads the state or the env depending on what is available, you have to ask what you plan to do with, say, ReaderT r (StateT s m) a, where you have both. For that reason, you can't just do:

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

because you'll conflict with existing instances. You can, however, do:

{-# 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

Then if you have a polymorphic reader value like f2, you can pull a MonadState out of it with unRS. If you want to use some more devious extensions, try this with RankNTypes:

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

Then you can do useStateAsEnv (liftM (+1) ask) and get a MonadState value. I haven't thoroughly investigated how useful this is in practice, however – to produce a value of type forall m. MonadReader m => m a, you can pretty much only use ask, local, and monadic functions.

Here's a similar, less general but probably more useful thing, using standard transformers:

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

Edit: thinking about this later you could probably generalise this to any state monad transformer with lift . runReaderT reader =<< get, but the type starts to be rather unwieldy:

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

which is a generalisation of the above but may not actually be a useful one.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文