beam icon indicating copy to clipboard operation
beam copied to clipboard

insertExpressions cannot `val_` lexical captures

Open ProofOfKeags opened this issue 5 years ago • 4 comments

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?

ProofOfKeags avatar Mar 27 '19 04:03 ProofOfKeags

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.

tathougies avatar Mar 27 '19 07:03 tathougies

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?

ProofOfKeags avatar Mar 27 '19 19:03 ProofOfKeags

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!

moll avatar Apr 05 '19 11:04 moll

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.

moll avatar Apr 05 '19 11:04 moll