假设我有以下类型。
data Row = Row
{
id :: !AddressID
}
具有以下内部转换函数。
makeAddress :: MonadIO m => MonadError Error m => Connection -> Row -> m Address
makeAddress _ Row{..} = return $ Address "Potato"
然后我有下面的功能 从数据库中读取,使用。Postgres.Simple:
findMany
:: MonadIO m
=> MonadReader Context m
=> MonadError Error m
=> [AddressID]
-> m [Address]
findMany ids = do
db <- view Context.db
xs <- liftIO $ PG.query db sql_query_addr $ PG.Only (PG.In (map unAddressId ids))
if (length xs) == (length ids)
then do
let addresses = concat (map (makeAddress db) xs)
return addresses
else
throwError $ AddressNotFound Nothing
-----------------------------------------------------------------------------------------------------------------------
sql_query_addr :: PG.Query
sql_query_addr = [qms|
SELECT *
FROM addresses a
WHERE a.id in ?
|]
无法编译。
• Could not deduce (MonadIO [])
arising from a use of ‘makeAddress’
from the context: (MonadIO m, MonadReader Context m,
MonadError Error m)
bound by the type signature for:
findMany :: forall (m :: * -> *).
(MonadIO m, MonadReader Context m, MonadError Error m) =>
[AddressID] -> m [Address]
at app/Impl/ReadModelApi/FindMany.hs:(22,1)-(27,18)
• In the first argument of ‘map’, namely ‘(makeAddress db)’
In the first argument of ‘concat’, namely
‘(map (makeAddress db) xs)’
In the expression: concat (map (makeAddress db) xs)
|
34 | let quotations = concat (map (makeAddress db) xs)
| ^^^^^^^^^^^^^^^^^
我意识到,我的 makeAddress
函数是不必要的复杂,这是一个最小的情况,从一个更大的,更有副作用的转换函数中归纳出来的。
但我不明白为什么会编译失败,我本来以为。
给定这个类型 makeAddress :: MonadIO m => MonadError Error m => Connection -> Row -> m Address
的类型 makeAddress db
是 MonadIO m => MonadError Error m -> Row -> m Address
. 鉴于 xs
有型 [Row]
, map (makeAddress db) xs
应给 [Addresses]
.
而且,鉴于内部和外部 m
(在 makeAddress
并在 findMany
)的一个实例。MonadIO
类型类,这些应该是兼容的单体?
显然这是不正确的,但我不知道我的推理在哪里出了问题,也不知道因此如何修正我的实现。
你说:"当然。
makeAddress :: MonadIO m => MonadError Error m => Connection -> Row -> m Address
当然,而且:
makeAddress db :: MonadIO m => MonadError Error m -> Row -> m
很接近了 实际上,它是 m Address
但我认为这只是一个错字。还有..:
map (makeAddress db) xs :: [Address]
这是你的第一个错误 你已经失去了 m
! 实际上是这样的。
map (makeAddress db) xs :: MonadIO m => MonadError Error m => [m Address]
对这个错误的解释是:
concat :: [[a]] -> [a]
于是 [m Address]
等于 [[a]]
,我们必须选择 m ~ []
和 a ~ Address
¹;但随后 []
不是一个可以做IO的单体,所以 MonadIO m
约束不满足。呜呜!
而不是 concat
,您可以使用 sequenceA
:
sequenceA :: Applicative m => [m a] -> m [a]
-- OR, specializing,
sequenceA :: MonadIO m => MonadError m => [m Address] -> m [Address]
这个 map
-sequenceA
组合是如此常见,它有自己的名字。
traverse :: Applicative m => (a -> m b) -> [a] -> m [b]
如果你没看过 ~
之前,你可以用 =
在这个答案中,到处都是,没有任何重要的东西会丢失。
concat (map f list)
要求 f
来返回一个列表。这是如此 map f list
可以产生一个列表的列表,以 concat
.
因此,在你的代码中,你使用的是 makeAddress
选择 m = []
以致于 map (makeAddress ...) xs :: [[Address]]
和 concat (....) :: [Address]
. 现在.., makeAddress
要求单体 m
是类 MonadIO
但 m = []
不是,因此出现错误。
尝试使用类似
...
then mapM (makeAddress db) xs
else ...