我正在使用servant-client
生成的函数并尝试将它们包装在自定义monad中。我有servant-client
生成的函数,其中包含以下签名:
getDomain
:: AuthenticateReq (AuthProtect "x-api-key")
-> Maybe Int
-> Maybe Int
-> Manager
-> BaseUrl
-> ExceptT ServantError IO Domain
及其后面的包装:
domain
:: (MonadReader Env m, MonadIO m, MonadThrow m, MonadError ServantError m)
=> Int -> m (Domain)
domain domId = do
auth <- mkAuth
manager <- asks envManager
url <- asks envBaseUrl
baseUrl <- parseBaseUrl url
r <-
liftIO $ runExceptT $ getDomain auth (Just domId) Nothing manager baseUrl
case r of
Left e -> throwError e
Right x -> return x
因为我将有许多这样的函数和包装器,我想提取一个辅助函数,它将:
到目前为止,我的尝试如下:
helper
:: (Monad m, MonadIO m, MonadReader Env m, MonadThrow m)
=> (Manager -> BaseUrl -> m b) -> m b
helper f = do
manager <- asks envManager
url <- asks envBaseUrl
baseUrl <- parseBaseUrl url
f manager baseUrl
但不幸的是,由于以下错误,它无法编译:
Could not deduce (MonadReader Env IO)
arising from a use of ‘helper’
from the context (MonadReader Env m,
MonadIO m,
MonadThrow m,
MonadError ServantError m)
bound by the type signature for
domain :: (MonadReader Env m, MonadIO m, MonadThrow m,
MonadError ServantError m) =>
Int -> m Domain
并且我不明白为什么编译器推断出MonadReader Env IO约束。我想知道为什么会发生这种情况以及如果可能的话,进行这种重构的方法是什么。我是否需要对domain
函数本身进行任何更改以适应此更改?
<小时/> 的更新:
重构版domain
如下:
domain
:: (MonadReader Env m, MonadIO m, MonadThrow m, MonadError ServantError m)
=> Int -> m (Domain)
domain domId = do
auth <- mkAuth
r <-
liftIO $ runExceptT $ helper $ getDomain auth (Just domId) Nothing
case r of
Left e -> throwError e
Right x -> return x
更新2:
我现在已经找到了一个解决方案:
domain
:: (MonadReader Env m, MonadIO m, MonadThrow m, MonadError ServantError m)
=> Int -> m (Domain)
domain domId = do
auth <- mkAuth
helper $ getDomain auth (Just domId) Nothing
helper
:: (MonadReader Env m, MonadIO m, MonadError e m)
=> (Manager -> BaseUrl -> ExceptT e IO b) -> m b
helper f = do
env <- ask
r <- liftIO $ runExceptT $ runReaderT (helper1 f) env
case r of
Left e -> throwError e
Right x -> return x
-- based on
-- https://github.com/passy/giphy-api/blob/4b006db2fe566b7200f6c81b8c56cabe2a595b90/src/Web/Giphy.hs#L359
helper1
:: (Monad m, MonadTrans t, MonadThrow (t m), MonadReader Env (t m))
=> (Manager -> BaseUrl -> m b) -> t m b
helper1 f = do
manager <- asks envManager
url <- asks envBaseUrl
baseUrl <- parseBaseUrl url
lift $ f manager baseUrl
将helper
和helper1
合并到一个函数中也是一件好事,到目前为止我还没有成功。我仍然不清楚原始问题,但我认为它与getDomain
明确使用IO
作为基本monad有关。
根据本杰明的建议删除liftIO $ runExceptT
和case
表达式
domain
:: (MonadReader Env m, MonadIO m, MonadThrow m, MonadError ServantError m)
=> Int -> m (Domain)
domain domId = do
auth <- mkAuth
helper $ getDomain auth (Just domId) Nothing
helper
:: (Monad m, MonadIO m, MonadReader Env m, MonadThrow m)
=> (Manager -> BaseUrl -> m b) -> m b
helper f = do
manager <- asks envManager
url <- asks envBaseUrl
baseUrl <- parseBaseUrl url
f manager baseUrl
导致以下错误:
Couldn't match type ‘m’ with ‘ExceptT ServantError IO’
‘m’ is a rigid type variable bound by
the type signature for
domain :: (MonadReader Env m, MonadIO m, MonadThrow m,
MonadError ServantError m) =>
Int -> m Domain
at <path>
Expected type: m Domain
Actual type: ExceptT ServantError IO Domain
Relevant bindings include
domain :: Int -> m Domain
(bound at <path>)
In a stmt of a 'do' block:
helper $ getDomain auth (Just domId) Nothing
In the expression:
do { auth <- mkAuth;
helper $ getDomain auth (Just domId) Nothing }
In an equation for ‘domain’:
domain domId
= do { auth <- mkAuth;
helper $ getDomain auth (Just domId) Nothing }
即使使用类型孔,也会指示返回值为正确类型:
domain
:: (MonadReader Env m, MonadIO m, MonadThrow m, MonadError ServantError m)
=> Int -> m (Domain)
domain domId = do
auth <- mkAuth
x <- helper $ getDomain auth (Just domId) Nothing
return _
helper
:: (Monad m, MonadIO m, MonadReader Env m, MonadThrow m)
=> (Manager -> BaseUrl -> m b) -> m b
helper f = do
manager <- asks envManager
url <- asks envBaseUrl
baseUrl <- parseBaseUrl url
f manager baseUrl
错误:
Found hole ‘_’ with type: Domain
Relevant bindings include
x :: Domain (bound at <path>)
auth :: AuthenticateReq (AuthProtect "x-api-key")
(bound at <path>)
domId :: Int (bound at <path>)
domain :: Int -> m Domain
(bound at <path>)
In the first argument of ‘return’, namely ‘_’
In a stmt of a 'do' block: return _
In the expression:
do { auth <- mkAuth;
x <- helper $ getDomain auth (Just domId) Nothing;
return _ }