Home > other >  Type error with simple where-clause with Haskell's beam
Type error with simple where-clause with Haskell's beam

Time:12-31

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.

  • Related