【问题标题】:Type error with simple where-clause with Haskell's beam带有 Haskell 梁的简单 where 子句的类型错误
【发布时间】: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) $
   |  

现在,错误信息本身已经很清楚了,但我不太清楚==. 的哪一侧需要修改,也不知道如何修改。或者是缺少扩展名或类型注释的问题。

【问题讨论】:

    标签: haskell haskell-beam


    【解决方案1】:

    代码的相关部分

    selectFoosByBar bar = select $
        filter_ (\foo -> _fooBar foo ==. bar) $
            all_ $ _bazFoos bazDb
    

    如果我们查看==.,您将看到(==.) :: SqlEq expr a =&gt; a -&gt; a -&gt; 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 -&gt; SqlSelect Postgres Foo 也可以。
    • 是的,我会尝试这样做,但我无法安装任何后端。
    【解决方案2】:

    在违规行上,bar :: Word32(根据selectFoosByBar 的签名)。

    认为_fooBar fooColumnar (something) Word32

    错误消息说问题出在==. 的第一个参数上,但是查看type of ==.,我认为您可以更改任何一方以达成一致。

    为什么是bar :: Word32?它具有直观意义;您正在尝试按一个单词进行过滤,因此 arg 应该是一个单词。这表明您可能想对_fooBar foo 做一些事情以“摆脱”Word32。这可能是一个简单的函数,但更有可能是相反的:以某种方式将您的 ==. bar 操作提升到“查询表达式”空间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-30
      • 1970-01-01
      • 2017-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多