I am trying to create a select query with a simple where-clause using Haskell's beam. From https://haskell-beam.github.io/beam/user-guide/queries/select/#where-clause, I believed that this would work:
{-# 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
but I am missing some vital detail, so I get the following compile error:
<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) $
|
Now, the error message itself is quite clear, but what I can't quite figure out is which side of ==. I need to modify nor how to do it. Or if it's a matter of some missing extension or type annotation.
CodePudding user response:
The relevant portions of the code
selectFoosByBar bar = select $
filter_ (\foo -> _fooBar foo ==. bar) $
all_ $ _bazFoos bazDb
If we look at ==. you will see (==.) :: SqlEq expr a => a -> a -> expr Bool, so both sides of ==. need to have the same type.
Now look at what is on the left of (==.) we see _fooBar foo :: Columnar f Word32. We can't get out of Columnar but we can make bar into something beam can work with using val_:
selectFoosByBar bar = select $
filter_ (\foo -> _fooBar foo ==. val_ bar) $
all_ $ _bazFoos bazDb
Notice this only work if we remove the type annotation. With a type annotation it will look like:
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
I think it needs this huge annotation so ghc can keep track of the be abstracted out backend.
Edit:
If we enable ConstraintKinds we can simplify the annotation:
type MagicSql be =
HasSqlValueSyntax
(Sql92ExpressionValueSyntax
(Sql92SelectTableExpressionSyntax
(Sql92SelectSelectTableSyntax
(Sql92SelectSyntax (BeamSqlBackendSyntax be)))))
selectFoosByBar
:: (HasQBuilder be, HasSqlEqualityCheck be Word32, MagicSql be Word32) =>
Word32 -> SqlSelect be (FooT Identity)
CodePudding user response:
On the offending line, bar :: Word32 (per the signature of selectFoosByBar).
I think _fooBar foo is a Columnar (something) Word32.
The error message says the problem is with the first arg to ==., but looking at the type of ==., I think you could change either side to get agreement.
Why is bar :: Word32? It makes intuitive sense; you're trying to filter by a word so the arg should be a word. That suggests that you probably want to do something to _fooBar foo to get a Word32 "out of" it. That might be a straightforward function, but more likely it's going to be the opposite: somehow lifting your ==. bar operation up into the "query expression" space.
