带有 Haskell 梁的简单 where 子句的类型错误
Posted
技术标签:
【中文标题】带有 Haskell 梁的简单 where 子句的类型错误【英文标题】:Type error with simple where-clause with Haskell's beam 【发布时间】:2021-12-30 12:31:48 【问题描述】:我正在尝试使用 Haskell 的梁创建一个带有简单 where 子句的选择查询。从https://haskell-beam.github.io/beam/user-guide/queries/select/#where-clause,我相信这会奏效:
-# LANGUAGE DeriveAnyClass #-
-# LANGUAGE DeriveAnyClass #-
-# LANGUAGE DeriveGeneric #-
-# LANGUAGE FlexibleInstances #-
-# LANGUAGE StandaloneDeriving #-
-# LANGUAGE TypeFamilies #-
-# LANGUAGE TypeSynonymInstances #-
module Lib where
import Data.Int ( Int32 )
import Data.Word ( Word32 )
import Database.Beam
data FooT f
= Foo
_fooId :: Columnar f Int32
, _fooBar :: Columnar f Word32
deriving (Generic, Beamable)
instance Table FooT where
data PrimaryKey FooT f =
FooId (Columnar f Int32) deriving (Generic, Beamable)
primaryKey = FooId . _fooId
type Foo = FooT Identity
type FooId = PrimaryKey FooT Identity
deriving instance Show Foo
deriving instance Eq Foo
data BazDb f = BazDb
_bazFoos :: f (TableEntity FooT)
deriving (Generic, Database be)
bazDb :: DatabaseSettings be BazDb
bazDb = defaultDbSettings
selectFoosByBar :: HasQBuilder be => Word32 -> SqlSelect be Foo
selectFoosByBar bar = select $
filter_ (\foo -> _fooBar foo ==. bar) $
all_ $ _bazFoos bazDb
但我缺少一些重要的细节,所以我得到以下编译错误:
<SNIP>/Lib.hs:42:22: error:
• Couldn't match type ‘QGenExpr QValueContext be QBaseScope Word32’
with ‘Word32’
Expected type: Word32
Actual type: Columnar (QExpr be QBaseScope) Word32
• In the first argument of ‘(==.)’, namely ‘_fooBar foo’
In the expression: _fooBar foo ==. bar
In the first argument of ‘filter_’, namely
‘(\ foo -> _fooBar foo ==. bar)’
• Relevant bindings include
foo :: FooT (QExpr be QBaseScope) (bound at src/Lib.hs:42:15)
selectFoosByBar :: Word32 -> SqlSelect be Foo
(bound at src/Lib.hs:41:1)
|
42 | filter_ (\foo -> _fooBar foo ==. bar) $
|
现在,错误信息本身已经很清楚了,但我不太清楚==.
的哪一侧需要修改,也不知道如何修改。或者是缺少扩展名或类型注释的问题。
【问题讨论】:
【参考方案1】:代码的相关部分
selectFoosByBar bar = select $
filter_ (\foo -> _fooBar foo ==. bar) $
all_ $ _bazFoos bazDb
如果我们查看==.
,您将看到(==.) :: SqlEq expr a => a -> a -> expr Bool
,因此==.
的两边需要具有相同的类型。
现在看看 (==.) 左边是什么,我们看到了_fooBar foo :: Columnar f Word32
。我们无法摆脱 Columnar 但我们可以使用 val_
将 bar 制作成 beam 可以使用的东西:
selectFoosByBar bar = select $
filter_ (\foo -> _fooBar foo ==. val_ bar) $
all_ $ _bazFoos bazDb
请注意,这仅在我们删除类型注释时才有效。使用类型注释,它看起来像:
selectFoosByBar
:: (HasQBuilder be, HasSqlEqualityCheck be Word32,
HasSqlValueSyntax
(Sql92ExpressionValueSyntax
(Sql92SelectTableExpressionSyntax
(Sql92SelectSelectTableSyntax
(Sql92SelectSyntax (BeamSqlBackendSyntax be)))))
Word32) =>
Word32 -> SqlSelect be (FooT Identity)
selectFoosByBar bar = select $
filter_ (\foo -> _fooBar foo ==. val_ bar) $
all_ $ _bazFoos bazDb
我认为它需要这个巨大的注释,以便 ghc 可以跟踪be
抽象出的后端。
编辑:
如果我们启用ConstraintKinds
,我们可以简化注解:
type MagicSql be =
HasSqlValueSyntax
(Sql92ExpressionValueSyntax
(Sql92SelectTableExpressionSyntax
(Sql92SelectSelectTableSyntax
(Sql92SelectSyntax (BeamSqlBackendSyntax be)))))
selectFoosByBar
:: (HasQBuilder be, HasSqlEqualityCheck be Word32, MagicSql be Word32) =>
Word32 -> SqlSelect be (FooT Identity)
【讨论】:
太棒了!我试过val_
,但类型约束愚弄了我。 “我认为它需要这个巨大的注释,以便 ghc 可以跟踪被抽象出的后端。”事实证明确实如此,所以只要我可以将自己限制在一个后端,例如selectFoosByBar :: Word32 -> SqlSelect Postgres Foo
也可以。
是的,我会尝试这样做,但我无法安装任何后端。【参考方案2】:
在违规行上,bar :: Word32
(根据selectFoosByBar
的签名)。
我认为_fooBar foo
是Columnar (something) Word32
。
错误消息说问题出在==.
的第一个参数上,但是查看type of ==.
,我认为您可以更改任何一方以达成一致。
为什么是bar :: Word32
?它具有直观意义;您正在尝试按一个单词进行过滤,因此 arg 应该是一个单词。这表明您可能想对_fooBar foo
做一些事情以“摆脱”Word32
。这可能是一个简单的函数,但更有可能是相反的:以某种方式将您的 ==. bar
操作提升到“查询表达式”空间。
【讨论】:
以上是关于带有 Haskell 梁的简单 where 子句的类型错误的主要内容,如果未能解决你的问题,请参考以下文章
Haskell - 在“where”中定义一个带有守卫的函数