说我有以下类型:
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
类型类的实例,那么这些应该兼容吗? / p>
显然这是不正确的,但是我不知道我的推理在哪里出现问题,或者如何解决我的实现问题。
答案 0 :(得分:4)
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 ...
答案 1 :(得分:3)
您说:
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的monad,因此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]
¹如果您以前从未见过~
,则可以在此答案中的所有位置将其替换为=
,而不会失去任何重要意义。