Alan Coromano
Alan Coromano

Reputation: 26048

Types mismatch, should I necessarily use liftIO?

I want to perform a delete operation in Servant and return an error or (). Here's my code:

del :: Int -> ExceptT ServantErr IO ()
del myId = liftIO $ do
  cn <- getConnection
  a <- execute cn "delete from table1 where id = ?" [myId]
  case a of
    1 -> return ()
    _ -> throwE err503 --compile error

The error is:

  Couldn't match expected type ‘IO ()’
                with actual type ‘ExceptT ServantErr m0 a0’
    In the expression: throwE err503
    In a case alternative: _ -> throwE err503

I'd prefer not to use liftIO before each expression if possible:

del myId =  do
  cn <- liftIO getConnection
  a <- liftIO $ execute cn "delete from table1 where id = ?" [myId]
  case a of
    1 -> return ()
    _ -> throwE err503

How can I return an error then?

Upvotes: 1

Views: 160

Answers (1)

Michael
Michael

Reputation: 2909

I think you won't be able to avoid it. Everthing in a do-block must be in the same monad, so with the initial

 del :: Int -> ExceptT ServantErr IO ()
 del myId = liftIO $ do ...

every line must be in IO. But you can rearrange things in various ways, e.g. with a subordinate IO block, where some things come together:

 del myId = do
   a <- liftIO $ do 
     cn <- getConnection
     execute cn "delete from table1 where id = ?" [myId]
   case a of
     1 -> return ()
     _ -> throwE err503

or, with Control.Monad.unless, say:

del myId = do
  a <- liftIO $ do 
     cn <- getConnection
     execute cn "delete from table1 where id = ?" [myId]
  unless (a == 1) $ throwE err503

and any number of other ways. You don't say which getConnection and execute you are using, but are you sure they are not already MonadIO m => m ..? In which case you can drop the liftIO after all?

Upvotes: 7

Related Questions