Rank2Types,“......会逃脱它的范围”(光束)
Rank2Types, "... would escape its scope" (beam)
目前我正在尝试使用更多 beam
的可组合性。不幸的是,我无法弄清楚我正在尝试做的事情是否可行。我想做的是编写一个函数,它接受一个 Q
查询,并从查询中选择两次,一次未修改,一次包装到 "count(*)"-聚合中。
到目前为止我得到了这个函数:
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的回答,因为它有很好的解释为什么不可能。
基本上,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
本身是一种独立于 s
的类型 res
,这太多了。
在某些特定情况下,您可以按照您的要求去做:
问题是,对于一般人来说 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)
查询。
但不会接受更有趣的查询,例如 return 整个 table
do s <- all_ (_students schoolDb)
return (studentFoo s)
我仍然认为有可能让它发挥作用:
您需要定义自己的类型-class,实例如下
QExprToIdentity
(and 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)
目前我正在尝试使用更多 beam
的可组合性。不幸的是,我无法弄清楚我正在尝试做的事情是否可行。我想做的是编写一个函数,它接受一个 Q
查询,并从查询中选择两次,一次未修改,一次包装到 "count(*)"-聚合中。
到目前为止我得到了这个函数:
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的回答,因为它有很好的解释为什么不可能。
基本上,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
本身是一种独立于 s
的类型 res
,这太多了。
在某些特定情况下,您可以按照您的要求去做:
问题是,对于一般人来说 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)
查询。
但不会接受更有趣的查询,例如 return 整个 table
do s <- all_ (_students schoolDb)
return (studentFoo s)
我仍然认为有可能让它发挥作用:
您需要定义自己的类型-class,实例如下
QExprToIdentity
(and 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)