Несоответствие типов, обязательно ли использовать liftIO?

Я хочу выполнить операцию удаления в Servant и вернуть ошибку или (). Вот мой код:

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

Ошибка:

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

Я бы предпочел не использовать liftIO перед каждым выражением, если это возможно:

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

Как я могу вернуть ошибку тогда?


person Alan Coromano    schedule 04.04.2016    source источник


Ответы (1)


Я думаю, вам не удастся этого избежать. Все в do-блоке должно быть в одной монаде, поэтому с начальным

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

каждая строка должна быть в IO. Но вы можете переставлять вещи по-разному, например. с подчиненным блоком ввода-вывода, где некоторые вещи объединяются:

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

или с помощью Control.Monad.unless скажите:

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

и любым другим способом. Вы не говорите, какие getConnection и execute используете, но уверены ли вы, что они уже не MonadIO m => m ..? В таком случае вы все-таки можете бросить liftIO?

person Michael    schedule 04.04.2016