Звучит так, будто вы хотите, чтобы ask
имел тот же эффект, что и get
. Я не могу не задаться вопросом, почему вы просто не используете 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
вы можете в значительной степени использовать только ask
, local
и монадические функции.
Вот аналогичная, менее общая, но, вероятно, более полезная вещь, использующая стандартные трансформаторы:
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
который является обобщением вышеупомянутого, но на самом деле не может быть полезным.