继我的goal of reducing the boilerplate required to use Haxl with a relational database之后,我试图通过Persistent在一个存在量化的类型中打包原始SQL请求的结果。但是类型检查器不允许它:
data SomeRawSql where
SomeRawSql :: forall b. RawSql b => [b] -> SomeRawSql
packedVal = let res = runDB $ rawSql "SELECT * FROM ..." [toPersistValue (pack "ABC")]
in fmap SomeRawSql res
这导致fmap上的类型错误:Ambiguous type variable ‘b0’ arising from a use of ‘SomeRawSql’ prevents the constraint ‘(RawSql b0)’ from being solved.
来自persistent的rawSql的类型是:
rawSql :: (RawSql a, MonadIO m)
=> Text -- ^ SQL statement, possibly with placeholders.
-> [PersistValue] -- ^ Values to fill the placeholders.
-> ReaderT SqlBackend m [a]
runDB
是一个辅助函数,它连接到数据库并返回IO [a]
。根据rawSql的定义,我希望满足RawSql约束。我不明白为什么会出现这种错误。
答案 0 :(得分:3)
rawSql
普遍量化。这意味着,它不会“从数据库中提取RawSql
实例”,这将是存在类型SomeRawSql
表达的内容。相反,它可以从数据库中提取值,前提是它们具有RawSql
实例。调用者选择的是什么类型。
您还可以将通用量化包装在无参数类型中:
data SomeRawSql where
SomeRawSql :: (forall b. RawSql b => [b]) -> SomeRawSql
但我不认为这是明智的,它只会让你选择一种类型的负担。参数化是一件好事,它允许您实际跟踪哪些类型的位置。没有真正的理由不要绕过它!
一个完全不同的主题是你想要检索一个你真不知道的类型的值。 rawSql
未涵盖这些问题,您需要使用Dynamic
这样的包装器自行实现。