无法在 ReaderT 包装中推断出 MonadReader
Could not deduce MonadReader in ReaderT wrapping
遵循并适应 this blog post, I've been trying to produce a solution which should allow testing of a function which reads env vars (using 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
在上面“问”的站点,出现了如下错误:
/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)
类型。最简单的解决方案是用 MockEnv
新类型包装器包装 ask
。例如,以下作品:
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)
的实例定义工作正常。
遵循并适应 this blog post, I've been trying to produce a solution which should allow testing of a function which reads env vars (using 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
在上面“问”的站点,出现了如下错误:
/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)
类型。最简单的解决方案是用 MockEnv
新类型包装器包装 ask
。例如,以下作品:
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)
的实例定义工作正常。