beam
beam copied to clipboard
insertExpressions cannot `val_` lexical captures
Maybe I'm coming at this the wrong way, but the following does not work:
insertStuff :: Text -> [(Address, Text)] -> Pg ()
insertStuff name addresses = runInsert $ insert (tableSelector db) $ do
let modelize (addr, url) = Model
default_ -- UUID
default_ -- LocalTime
default_ -- LocalTime
(val_ $ addressToText addr)
(val_ name)
(val_ url)
(val_ Nothing)
insertExpressions $ modelize <$> addresses
It fails with:
Couldn't match type ‘Columnar
f0 uuid-types-1.0.3:Data.UUID.Types.Internal.UUID’
with ‘QGenExpr ctxt0 be0 s0 a1’
Expected type: C f0 uuid-types-1.0.3:Data.UUID.Types.Internal.UUID
Actual type: QGenExpr ctxt0 be0 s0 a1
The type variables ‘f0’, ‘ctxt0’, ‘be0’, ‘s0’, ‘a1’ are ambiguous
...
|
178 | default_
| ^^^^^^^^
But if I rid myself of lexical captures, it is happy:
insertStuff :: Text -> [(Address, Text)] -> Pg ()
insertStuff name addresses = runInsert $ insert (tableSelector db) $ do
let modelize name' (addr, url) = Model
default_
default_
default_
(val_ $ addressToText addr)
(val_ name')
(val_ url)
(val_ Nothing)
insertExpressions $ modelize name <$> addresses
Should this be the case? What am I encountering?
This looks like an artifact of GHC's ill-documented and unfortunately sometimes unintuitive type inference. My guess is that both would work if you simply provided a type signature:
modelize :: (Address, Text) -> ModelT (QExpr Postgres s)
You can insert placeholders for the Postgres and s if you'd like, since the key point here is forcing GHC to understand that f
is necessarily QExpr
.
This works! Thanks. I would have given this a shot cause I figured this would be the case, the trouble is I didn't know what the right Functor was to use in this case since it obviously was not Identity
. Perhaps a section of the documentation that talks about the various Functors in the FTDSL could go a decent way to help future people struggling with expression stuff?
I second the suggestion to describe the involved magical type classes. Being on the frontlines of type trickery often results in relatively indecipherable type errors. Another suggestion I just brought up on IRC was that the type class code could use a bit of variable name copywriting — single letter variables aren't informative enough for us drive-by code readers that need to quickly figure out a type signature for when GHC falls flat.
Otherwise, thanks for working on Beam!
For the record, how would one assign the QExpr'ed model to a variable? I can't seem to satisfy GHC with the following:
let notebook = Db.CachedNotebook {
cachedNotebookId = Beam.default_,
cachedNotebookPath = Beam.val_ relativePath
} :: Db.CachedNotebookT (Beam.QExpr Beam.Sqlite s)
Beam.runInsert $ Beam.insert cacheDbNotebooks $
Beam.insertExpressions [notebook]
Peculiarly, substitute the relativePath :: FilePath
out for a literal "foo"
, and it type checks.