目前我正在尝试使用beam
的更多可组合性。不幸的是,我无法弄清楚我想做的事情是否可行。我要做的是编写一个函数,它接受Q
查询,并从查询中选择两次,一次未经修改,一次包装成" count(*)" -aggregate。
到目前为止,我有了这个功能:
runPaginatedQuery :: ( QExprToIdentity res ~ a
, FromBackendRow Sqlite (QExprToIdentity res)
, ProjectibleWithPredicate ValueContext SqliteExpressionSyntax res
, ProjectibleWithPredicate AnyType SqliteExpressionSyntax res
)
=> Connection -> Int -> Int
-> (forall s. Q SqliteSelectSyntax db s res)
-> IO ([a], Int)
runPaginatedQuery conn limit offset q = do
(Just count) <- runBeamSqlite conn $ runSelectReturningOne $ select $ aggregate_ (const $ countAll_) q
l <- runBeamSqlite conn $ runSelectReturningList $ select q
return (l,count)
函数类型检查,但是如果我尝试使用它,例如
main = do
conn <- open "test.db"
(ss :: [Student], n) <- runPaginatedQuery conn 20 0 (all_ (_students schoolDb))
print n
我收到以下类型错误:
src/Main.hs:57:56: error:
• Couldn't match type ‘res0’
with ‘StudentT (QExpr SqliteExpressionSyntax s)’
because type variable ‘s’ would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context:
forall s. Q SqliteSelectSyntax SchoolDb s res0
at src/Main.hs:57:27-81
Expected type: Q SqliteSelectSyntax SchoolDb s res0
Actual type: Q SqliteSelectSyntax
SchoolDb
s
(StudentT
(QExpr
(Database.Beam.Backend.SQL.SQL92.Sql92SelectTableExpressionSyntax
(Database.Beam.Backend.SQL.SQL92.Sql92SelectSelectTableSyntax
SqliteSelectSyntax))
s))
• In the fourth argument of ‘runPaginatedQuery’, namely
‘(all_ (_students schoolDb))’
In a stmt of a 'do' block:
(ss :: [Student], n) <- runPaginatedQuery
conn 20 0 (all_ (_students schoolDb))
In the expression:
do conn <- open "test.db"
(ss :: [Student], n) <- runPaginatedQuery
conn 20 0 (all_ (_students schoolDb))
print "hi"
|
57 | (ss :: [Student], n) <- runPaginatedQuery conn 20 0 (all_ (_students schoolDb))
| ^^^^^^^^^^^^^^^^^^^^^^^^^
我无法理解错误消息。
编辑:似乎我想要实现的目标是不可能的,因此我接受了@ chi的答案,因为它有一个很好的解释为什么这是不可能的。
答案 0 :(得分:1)
基本上,runPaginatedQuery
要求查询的结果类型res
不依赖于s
。但你的确如此。
Expected type: Q SqliteSelectSyntax SchoolDb s res0
Actual type: Q SqliteSelectSyntax
SchoolDb
s
(StudentT (QExpr
(Database.Beam.Backend.SQL.SQL92.Sql92SelectTableExpressionSyntax
(Database.Beam.Backend.SQL.SQL92.Sql92SelectSelectTableSyntax
SqliteSelectSyntax))
s))
-- ^^^
我看不出任何简单的修复方法。 runPaginatedQuery
的类型看起来过于严格。
直观地说,它应该采用(forall s. Q SqliteSelectSyntax db s (F s))
之类的内容,其中F
是某种类型级函数,需要使QExprToIdentity (F s)
独立于s
。然而,我们无法对Haskell中的类型级函数F
进行普遍量化。
您的代码即将要求这样做,但隐含地要求F s
本身是res
类型,它独立于s
,这太过分了。
答案 1 :(得分:1)
在某些特定情况下,您可以按照自己的要求进行操作:
问题在于,对于一般的res
来说,很难做任何事情。
但实际上,您汇总了一些table
。因此,如果您按摩类型,
它将进行类型检查并解决问题,因为我们可以将s
放置到res
的位置。
runPaginatedQuery
:: (Beamable table, FromBackendRow Sqlite (table Identity))
=> Connection -> Int -> Int
-> (forall s. Q Sqlite db s (table (QExpr Sqlite s)))
-> IO ([table Identity], Int)
此`runPaginatedQuery将接受您的示例
all_ (_students schoolDb)
查询。
但是不会接受更有趣的查询,例如不会按原样返回整个表的查询
do s <- all_ (_students schoolDb)
return (studentFoo s)
我仍然认为有可能使其工作:
您需要定义自己的类型类,并在其后添加实例
QExprToIdentity
(和ThreadRewritable
等的结构)。
我并不是说这很容易,但是我很确定这是可能的。
顺便说一句,下一次,请提供一个更完整的示例,以便尝试提供帮助的人们可以快速开始:
这是您可以加载到GHCi中的完整示例:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
import Database.Beam
import Database.Beam.Sqlite
import Database.Beam.Sqlite.Connection
import Database.Beam.Sqlite.Syntax
import Database.SQLite.Simple
runPaginatedQuery
:: (Beamable table, FromBackendRow Sqlite (table Identity))
=> Connection -> Int -> Int
-> (forall s. Q Sqlite db s (table (QExpr Sqlite s)))
-> IO ([table Identity], Int)
runPaginatedQuery conn limit offset q = do
Just count <- runBeamSqlite conn $ runSelectReturningOne $ select $ aggregate_ (const $ countAll_) q
l <- runBeamSqlite conn $ runSelectReturningList $ select q
return (l,count)
data StudentT f = Student
{ studentId :: C f Int
}
deriving (Generic, Beamable)
type Student = StudentT Identity
instance Table StudentT where
newtype PrimaryKey StudentT f = StudentId (C f Int)
deriving (Generic, Beamable)
primaryKey = StudentId . studentId
data SchoolDb f = SchoolDb
{ _students :: f (TableEntity StudentT) }
deriving (Generic, Database be)
schoolDb :: DatabaseSettings be SchoolDb
schoolDb = defaultDbSettings
main = do
conn <- open "test.db"
(ss :: [Student], n) <- runPaginatedQuery conn 20 0 (all_ (_students schoolDb))
print (n :: Int)