# Either with IO?

When we create `IO`

actions that may require I/O we risk running into all kinds of errors.
For example, when we use `writeFile`

, we could run out of disk space in the middle of writing,
or the file might be write protected. While these scenarios aren't super common, they are definitely
possible.

We could've potentially encoded Haskell functions like `readFile`

and `writeFile`

as `IO`

operations
that return `Either`

, for example:

```
readFile :: FilePath -> IO (Either ReadFileError String)
writeFile :: FilePath -> String -> IO (Either WriteFileError ())
```

However there are a couple of issues here, the first is that now composing `IO`

actions
became more difficult. Previously we could write:

```
readFile "input.txt" >>= writeFile "output.html"
```

But now the types no longer match - `readFile`

will return an `Either ReadFileError String`

,
but `writeFile`

wants to take a `String`

as input. We are forced to handle the error
before calling `writeFile`

.

## Composing IO + Either using ExceptT

One way to handle this is by using **monad transformers**. Monad transformers are a method
to stack monad capabilities on top of one another. They are called transformers because
**they take a type that has an instance of monad as input, and return a new type that
implements the monad interface, stacking a new capability on top of it**.

For example, if we want to create a value with a type similar to `IO (Either Error a)`

that we can compose using the monadic interface (the function `>>=`

) with other values
of a type similar to `IO (Either Error a)`

, we can using a monad transformer
called `ExceptT`

.
Let's see how `ExceptT`

is defined:

```
newtype ExceptT e m a = ExceptT (m (Either e a))
```

Remember, a `newtype`

is a new name for an existing type. And if we substitute
`e`

with `Error`

and `m`

with `IO`

we'll get exactly `IO (Either Error a)`

as we wanted.
And we can convert an `ExceptT Error IO a`

into `IO (Either Error a)`

using
the function `runExceptT`

:

```
runExceptT :: ExceptT e m a -> m (Either e a)
```

`ExceptT`

implements the monadic interface in a way that combines the capabilities of
`Either`

, and whatever `m`

it takes. Because `ExceptT e m`

has a `Monad`

instance,
a specialized version of `>>=`

would look like this:

```
-- Generalized version
(>>=) :: Monad m => m a -> (a -> m b) -> m b
-- Specialized version, replace `m` with `ExceptT e m`
(>>=) :: ExceptT e m a -> (a -> ExceptT e m b) -> ExceptT e m b
```

Unsure how this works? Try to implement `>>=`

for `IO (Either Error a)`

:

```
bindExceptT :: IO (Either Error a) -> (a -> IO (Either Error b)) -> IO (Either Error b)
```

## Solution

```
bindExceptT :: IO (Either Error a) -> (a -> IO (Either Error b)) -> IO (Either Error b)
bindExceptT mx f = do
x <- mx -- `x` has the type `Either Error a`
case x of
Left err -> pure (Left err)
Right y -> f y
```

Note that we didn't actually use the implementation details of `Error`

or `IO`

,
`Error`

isn't mentioned at all, and for `IO`

we only used the monadic interface with
the do notation. We could write the same function with a more generalized type signature:

```
bindExceptT :: Monad m => m (Either e a) -> (a -> m (Either e b)) -> m (Either e b)
bindExceptT mx f = do
x <- mx -- `x` has the type `Either e a`
case x of
Left err -> pure (Left err)
Right y -> f y
```

And because `newtype ExceptT e m a = ExceptT (m (Either e a))`

we can just
pack and unpack that `ExceptT`

constructor and get:

```
bindExceptT :: Monad m => ExceptT e m a -> (a -> ExceptT e m b) -> ExceptT e m b
bindExceptT mx f = ExceptT $ do
-- `runExceptT mx` has the type `m (Either e a)`
-- `x` has the type `Either e a`
x <- runExceptT mx
case x of
Left err -> pure (Left err)
Right y -> f y
```

Note that when stacking monad transformers, the order in which we stack them matters. With

`ExceptT Error IO a`

, we have an`IO`

operation that when run will return`Either`

an error or a value.

`ExceptT`

can enjoy both worlds - we can return error values using the function `throwError`

:

```
throwError :: e -> ExceptT e m a
```

and we can "lift" functions that return a value of the underlying monadic type `m`

to return
a value of `ExceptT e m a`

instead:

```
lift :: m a -> ExceptT e m a
```

for example:

```
getLine :: IO String
lift getLine :: ExceptT e IO String
```

(Actually,

`lift`

is also a type class function from`MonadTrans`

, the type class of monad transformers. So technically`lift getLine :: MonadTrans t => t IO String`

, but we are specializing for concreteness)

Now, if we had:

```
readFile :: FilePath -> ExceptT IOError IO String
writeFile :: FilePath -> String -> ExceptT IOError IO ()
```

We could compose them again without issue:

```
readFile "input.txt" >>= writeFile "ouptut.html"
```

But remember - the error type `e`

(in both the case `Either`

and `Except`

)
must be the same between composed functions! This means that the type representing
errors for both `readFile`

and `writeFile`

must be the same - that would also
force anyone using these functions to handle these errors - should a user who
called `writeFile`

be required to handle a "file not found" error? Should a user
who called `readFile`

be required to handle an "out of disk space" error?
There are many many more possible IO errors! "network unreachable", "out of memory",
"cancelled thread", we cannot require a user to handle all these errors, or
even cover them all in a data type.

So what do we do?

We give up on this approach **for IO code**, and use a different one: Exceptions.
As we'll see in the next chapter.

Note - when we stack

`ExceptT`

on top of a different type called`Identity`

that also implements the`Monad`

interface, we get a type that is exactly like`Either`

called`Except`

(without the`T`

at the end). You might sometimes want to use`Except`

instead of`Either`

because it has a more appropriate name and better API for error handling than`Either`

.