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.