# How to refactor this code combining IO and Either?

**URL:** <https://discourse.haskell.org/t/how-to-refactor-this-code-combining-io-and-either/14865>\
**Category:** Learn\
**Created:** [October 19, 2026, 11:55am UTC](https://discourse.haskell.org/t/how-to-refactor-this-code-combining-io-and-either/14865 "2026-10-19T11:55:19Z")\
**Posts on this page:** 1\
**Showing post:** 10

<div class="post-metadata">

**Author:** ![vshabanov](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/vshabanov/32/2080_2.png) [@vshabanov](https://discourse.haskell.org/u/vshabanov)\
**Post date:** [October 20, 2026, 11:35pm UTC](https://discourse.haskell.org/t/how-to-refactor-this-code-combining-io-and-either/14865/10 "2026-10-20T23:35:56Z")

</div>

You could use plain exceptions, i.e. `readLibFromFile_` and `mkPlaylist` could simply `fail` or `throwIO` on error, and then you get a straightforward

```haskell
handleMakePlaylist :: MakePlaylistOptions -> IO ()
handleMakePlaylist opts = do
    lib <- readLibFromFile_ opts.libraryPath
    plist <- mkPlaylist lib' opts.firstTrack opts.maxTracks
    putStr $ renderResolvedPlaylist plist'

```

Or you can make all functions pure with `Either String ...` results and chain them using the plain `Either` monad:

```haskell
handleMakePlaylist :: MakePlaylistOptions -> IO ()
handleMakePlaylist opts = do
    contents <- readFile opts.libraryPath -- throws, could be wrapped with `try`
    putStr $ either ("Error: " <>) id $ do 
        lib <- parseLib contents 
        plist <- mkPlaylist lib' opts.firstTrack opts.maxTracks
        Right $ renderResolvedPlaylist plist'

```

Local use of `ExceptT` is fine too, but I find the two approaches above easier.

---

_[View the full topic](https://discourse.haskell.org/t/how-to-refactor-this-code-combining-io-and-either/14865)._
