首页 > 解决方案 > 单个方法上的多个 MonadReader 约束

问题描述

看起来没有什么能阻止你定义这样的函数:

tmp :: (MonadReader Int m, MonadReader Bool m) => m Int
tmp = ifM ask ((+1) <$> ask) ((+2) <$> ask)

所以就像一个可类型化的上下文,只要你给它一个类型,ask 就会给你那个类型的环境部分,我认为这是一个合理的替代名称阴影的方法。

MonadReader但是,对于由定义的默认实例Control.Monad.Reader,看起来并没有一种方法可以实际调用此方法。所以我定义了一个新模块来尝试这样做:

{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses,
  UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Control.Monad.Reader.Extra
  ( MonadReader(ask)
  ) where

import Control.Monad.Reader (MonadReader(ask, local, reader))
import Control.Monad.Trans.Class (MonadTrans(lift))

instance {-# OVERLAPPABLE #-} ( Monad m
                              , MonadTrans t
                              , Monad (t m)
                              , MonadReader r m
                              ) =>
                              MonadReader r (t m) where
  ask = lift ask
  local _ _ = undefined
  reader f = lift (reader f)

只要第二个实例在范围内,您就可以tmp运行runReaderT (runReaderT tmp 1) True.

不幸的是,我无法为 local 找到一个体面的实现,因为它需要具有 type local :: (r -> r) -> (t m a) -> (t m a),但似乎没有办法将 alocal :: (r -> r) -> m a -> m a提升到此。

对于这种情况,本地的合理实现是什么?


最小完整示例:

{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, UndecidableInstances, LambdaCase, FlexibleContexts #-}
module Tmpfundep where

import Control.Monad.Reader
  ( MonadReader(ask, local, reader)
  , ReaderT(ReaderT)
  , runReaderT
  )

instance {-# INCOHERENT #-} (Monad m, MonadReader r m) =>
                            MonadReader r (ReaderT r' m) where
  ask = ReaderT (const ask)
  local f (ReaderT m) = ReaderT (local f . m)
  reader f = ReaderT (const (reader f))

withEnv :: r -> ReaderT r m a -> m a
withEnv r m = runReaderT m r

tmp :: (MonadReader Int m, MonadReader Bool m) => m Int
tmp = ask >>= \case
  True -> (+1) <$> ask
  False -> (+2) <$> ask

main :: IO ()
main = withEnv True (withEnv (1 :: Int) tmp) >>= print

能够运行这个echo main | stack exec -- runghc Tmpfundep.hs

标签: haskellmonad-transformers

解决方案


推荐阅读