只知道一个类型类,在一个存在类型中打包一个值

时间:2018-01-16 10:40:19

标签: haskell yesod existential-type haxl

继我的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约束。我不明白为什么会出现这种错误。

1 个答案:

答案 0 :(得分:3)

rawSql 普遍量化。这意味着,它不会“从数据库中提取RawSql实例”,这将是存在类型SomeRawSql表达的内容。相反,它可以从数据库中提取值,前提是它们具有RawSql实例。调用者选择的是什么类型。

您还可以将通用量化包装在无参数类型中:

data SomeRawSql where
  SomeRawSql :: (forall b. RawSql b => [b]) -> SomeRawSql

但我不认为这是明智的,它只会让你选择一种类型的负担。参数化是一件好事,它允许您实际跟踪哪些类型的位置。没有真正的理由不要绕过它!

一个完全不同的主题是你想要检索一个你真不知道的类型的值。 rawSql未涵盖这些问题,您需要使用Dynamic这样的包装器自行实现。