无法在 ReaderT 包装中推断 MonadReader

问题描述

遵循并调整 this blog post,我一直在尝试制定一个解决方案,该解决方案应该允许测试读取环境变量的函数(使用 System.Environment.lookupEnv)。

那样,我应该能够为测试注入一个人工环境,该环境可以被读取,而不是执行实际的 IO 操作。

但是,尝试读取环境时类型检查失败。

{-# LANGUAGE GeneralisednewtypeDeriving #-}

...
import           RIO.Map (Map)
import qualified RIO.Map as Map
...
import qualified System.Environment as E (lookupEnv)
...

newtype MockEnv m a = MockEnv
  { mockEnv :: ReaderT (Map String String) m a
  } deriving (applicative,Functor,Monad,MonadTrans)

runMockEnv :: MockEnv m a -> Map String String -> m a
runMockEnv (MockEnv e) = runReaderT e

class Monad m => MonadEnv m where
  lookupEnv :: String -> m (Maybe String)

instance MonadEnv IO where
  lookupEnv = E.lookupEnv

instance Monad m => MonadEnv (MockEnv m) where
  lookupEnv k = Map.lookup k <$> ask
                              -- ^^^ error occurs here

在上面“ask”的站点,产生如下错误

/home/[REDACTED].hs:45:34: error:
    • Could not deduce (MonadReader (Map String String) (MockEnv m))
        arising from a use of ‘ask’
      from the context: Monad m
        bound by the instance declaration
        at [REDACTED].hs:44:10-40
    • In the second argument of ‘(<$>)’,namely ‘ask’
      In the expression: Map.lookup k <$> ask
      In an equation for ‘lookupEnv’: lookupEnv k = Map.lookup k <$> ask
   |
45 |   lookupEnv k = Map.lookup k <$> ask
   |                                  ^^^


--  While building package [REDACTED]

请您帮我理解为什么无法进行类型检查以及我需要做些什么来修复它?提前致谢。

解决方法

这些类型看起来不匹配。我们有:

lookupEnv :: String -> MockEnv m (Maybe String)
k :: String
ask :: MonadReader r m => m r
Map.lookup :: Map.lookup :: Ord k => k -> Map k a -> Maybe a
Map.lookup k :: Map String a -> Maybe a

所以,这一切都意味着我们需要您当前拥有 ask 的位为 MockEnv m (Map String a) 类型。最简单的解决方案是用 ask 新类型包装器包装 MockEnv。例如,以下工作:

  lookupEnv k = Map.lookup k <$> MockEnv ask

更强大的解决方案(以及 GHC 暗示您需要 MonadReader 实例的建议)是让 MockEnv m 成为 MonadReader 的实例:

instance Monad m => MonadReader (Map String String) (MockEnv m) where
  ask = MockEnv ask
  local f (MockEnv r) = MockEnv (local f r)

使用此实例,您对 MonadEnv (MockEnv m) 的实例定义工作正常。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...