Например, брать монад трансформеры и городить вот такое
aFunctionThatDoesIOAndMayReturnAnError :: (MonadError YourErrorType m, MonadIO m) => m YourResultType
В этом случае m это какой-то абстрактный тип монады, о котором мы говорим, что в нём можно делать сайд-эффекты и он может завершиться с ошибкой, в этом случае делать вложенные флатмапы не придется (придется, правда, время от времени хреначить liftIO, например, которое вытащит из m контекст IO)
Не очень представляю, как такое переделать на псевдокодный Котлин, тут прошу прощения