Building Applications with Many Effects

Building Applications with Many Effects

So far in this book you’ve learned about different individual Monads, like IO actions, lists of values, and optional values. As you start to write more sophisticated applications, it’s frequently desirable to compose the properties of these individual Monads to build up types with new behaviors that combine the properties of individual monadic actions. This technique is used in a huge number of real world Haskell applications, and it will enable you to read and write real world professional Haskell applications.

In this chapter you’ll learn about how to compose monadic computations using Monad transformers. You’ll learn about some common Monad transformers provided by the MTL and Transformers libraries, and learn how to make use of tagless final encodings to use Monad transformers effectively.

You’ll be able to build new Monads that combine different sorts of monadic computations, like having a read-only environment, dealing with exception handling, and performing IO.

In the next chapter you will learn how to use Monad transformers to create a core application Monad out of an MTL stack, using the Monad transformers that you learn about in this chapter.

Building Out The Monad Transformer Library

In this chapter we focused on two specific Monad transformers: StateT and ExceptT. The transformers library provides several other commonly used Monad transformers. One of the most common Monad transformers is the ReaderT transformer. This Monad transformer lets you write computations that have a read-only environment:

newtype ReaderT r m a = ReaderT {runReaderT :: r -> m a}

The two basic operations for a ReaderT Monad are ask, which fetches the value from the read-only environment, andlocal, which lets you run a ReaderT action with a modified local read-only environment. Their types are:

ask :: Monad m => ReaderT r m r
local :: Monad m => (r -> r) -> ReaderT r m a -> ReaderT r m a

In this exercise, write Functor, Applicative, Monad, MonadIO, and MonadTrans instances for ReaderT, and provide a definition for both ask and local. Once you have created a working definition of ReaderT, add a new class called MonadReader:

class Monad m => MonadReader r m | m -> r where
  ask :: m r
  local :: (r -> r) -> m a -> m a

Next, finish writing the following instances:

instance Monad m => MonadReader r (Reader.ReaderT r m) where
instance MonadReader r m => MonadReader r (ExceptT e m) where
instance MonadReader r m => MonadReader r (StateT s m) where

Hint 1

Some high level hint text

Hint 2

Some more detailed hint text

Hint 3

Even more detailed hint text

Solution

A complete solution for the exercise

A New FilePackParser

Refactor the FilePackParser application that you wrote earlier in this book to use the following Monad:

newtype FilePackParser a = FilePackParser
  { runFilePackParser :: StateT Text (ExceptT Text IO) a }

Hint 1

Some high level hint text

Hint 2

Some more detailed hint text

Hint 3

Even more detailed hint text

Solution

A complete solution for the exercise