Search code examples
haskellmonadsservant

How to resolve type mismatch between SqlBackend and PersistEntityBackend?


Using both the servant and persistent libraries to create a REST API and I'm running into a type mismatch error using insertUnique to create a new Entity.

Here's the offending function:

createUser :: Entity User -> App Int64
createUser p = do
    maybeNewUser <- runDb (insertUnique (User (userUsername $ entityVal p) (userSpotifyUser $ entityVal p)))
    case maybeNewUser of
        Nothing -> throwError err400
        Just newUser -> return $ fromSqlKey newUser

With the following error:

Couldn't match expected type ‘persistent-2.2.4.1:Database.Persist.Sql.Types.SqlBackend’
        with actual type ‘persistent-2.2.4.1:Database.Persist.Class.PersistEntity.PersistEntityBackend
                            (String
                             -> String
                             -> time-1.5.0.1:Data.Time.Clock.UTC.UTCTime
                             -> time-1.5.0.1:Data.Time.Clock.UTC.UTCTime
                             -> User)’
In the first argument of ‘runDb’, namely
[snip]

And for reference, the runDb function:

runDb :: (MonadReader Config m, MonadIO m) => SqlPersistT IO b -> m b
runDb query = do
    pool <- asks getPool
    liftIO $ runSqlPool query pool

And the App newtype:

newtype App a = App
    { runApp :: ReaderT Config (ExceptT ServantErr IO) a } deriving
        (Functor, Applicative, Monad, MonadReader Config, MonadError ServantErr, MonadIO)

I've tried type hinting the result of insertUnique as SqlBackend, but that causes an equally confusing error. Are the SqlBackend and PersistentEntityBackend types not interchangeable?

Or perhaps the monad is malformed?

Any assistance is greatly appreciated.


Solution

  • I was able to figure it out with the help of this answer.

    Like get and others, insertUnique in this context returns a plain User not an Entity User.

    createUser :: User -> App Int64
    createUser p = do
        user <- (User (userUsername $ entityVal p) (userSpotifyUser $ entityVal p))
        let insertUser = insertUnique user :: SqlPersistT IO (Maybe (Key User))
        maybeNewUserKey <- runDb insertUser
        case maybeNewUserKey of
            Nothing -> throwError err400
            Just newUserKey ->
                return $ fromSqlKey newUserKey
    

    Makes much more sense in hindsight since runDb accepts a SqlPersistT transformed monad.