3

次の例を考えてみましょう。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欲しいです:MonadReaderMonadState

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
  ...

しかし、タイプチェックを行う方法がわかりません。このようなとの関係を表現することは可能MonadStateですMonadReaderか?

ありがとう。

4

1 に答える 1

4

あなたが望むのは本質的askにと同じ効果を持つことのように聞こえますget。私は仕方がないのですが、なぜあなたはgetその場合に使用しないのか疑問に思います:)

利用可能なものに応じて状態または環境のいずれかを読み取るコードを作成することが目的の場合は、たとえば、両方がある場合に、何をする予定かを尋ねる必要が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

これは、標準のトランスを使用した、同様の、あまり一般的ではありませんが、おそらくより便利なものです。

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 m, MonadTrans t, MonadState (t m)) => ReaderT (StateType (t m)) m b -> t m b

これ上記の一般化ですが、実際には有用ではない場合があります。

于 2011-09-03T12:52:26.363 に答える