Манипулирование стека монады

Если у меня есть стек монад, скажем IO, State и Error, а также функция, которая использует только IO и Error. Как можно было бы "удалить" середину State монады из стека, чтобы я мог использовать свою функцию? Если бы порядок был IO, Error, State, я мог бы использовать lift для соответствия типам, но я хочу иметь возможность использовать свою функцию, если в стеке монады есть IO и Error и возможно, другие монады в любом порядке. Например:

fun :: ErrorT String IO ()
fun = throwError "error"

someCode :: ErrorT String (StateT Int IO) ()
someCode = do
    -- I want to use fun here

Ответ 1

Просто измените сигнатуру типа fun на fun :: (MonadError String m, MonadIO m) => m (). Затем он будет использоваться для любого стека монад, который имеет ошибку String и может выполнять IO (например, ErrorT String (StateT Int IO)).

Например:

fun :: (MonadError String m, MonadIO m) => m ()
fun = do
  liftIO $ putStrLn "in fun"
  throwError "error"

someCode :: ErrorT String (StateT Int IO) ()
someCode = do
  fun
  -- whatever you want