Chapter 3. Patterns of Composition

Function composition is a fundamental part of functional programming. This chapter is concerned with exploring the composition characteristics of the fundamental type-classes: Functor, Applicative Functor, Arrow, and Monad. We will see that Functor embeds into Applicative Functor, which embeds into Arrow, which embeds into Monad. After exploring Monad composition, we'll also look into Monad transformers (a technique for composing different types of Monad). As we move through the successive types, from the most general (Functor) to the most powerful (Monad), we will see how they differ in the ways they can be composed.

Note that this chapter does not have the last word on composition patterns, for example, in the following chapter we will explore another pattern for composition: functional lenses.

  • Functor
  • Applicative functor
  • Monad
  • Monad transformers
  • Arrow

Functor

The Functor type-class gives us a way to generalize function application to arbitrary types. Let's first look at regular function application. Suppose we defined a function of primitive types:

  f :: Num a => a -> a
  f = (^2)

We can apply it directly to the types it was intended for:

  f 5
  f 5.0  –- etc

To apply the f function to a richer type, we need to make that type an instance of Functor and then use the fmap function:

--  fmap   function     Functor
fmap       f            (Just 5)
fmap       (f . read)   getLine

The Functor type-class defines fmap:

class Functor f where
  fmap :: (a -> b) -> f a -> f b

Let's create our own Maybe' type and make it an instance of Functor:

data Maybe' a = Just' a | Nothing'
  deriving (Show)

instance Functor Maybe' where
  fmap _ Nothing' =   Nothing'
  fmap f (Just' x) = Just' (f x)

By making Maybe' a Functor type-class, we are describing how single-parameter functions may be applied to our type, assuming the function types align with our Functor class, for example:

  -- we can do this
  fmap f (Just' 7)
  fmap show (Just' 7)

  -– but still not this
  fmap f (Just' "7")

The fmap function lifts our function into the realm of Functor; fmap also lifts function composition to the level of Functor. This is described by the functor laws:

  -- law of composition
  fmap (f . g)  ==  fmap f . fmap g

  -- e.g.
  fmap (f . read) getLine  -- is the same as
  (fmap f) . (fmap read) $ getLine

  -- identity law
  fmap id  ==  id
  -- e.g.
  fmap id (Just 1) = id (Just 1)  

The fmap function is to Functor what map is to the List type, as shown in the following code:

  ns = map (^2) [1, 2, 3, 5, 7]   
  -- 'map' lifts f to operate on List type

We can also write the preceding code as follows:

  ns' = fmap (^2) [1, 2, 3, 5, 7] 

because List is a Functor:

  instance Functor List where
    fmap = map

The Functor type­class abstracts the idea of function application to a single argument and thereby gives us a way to apply our functions to arbitrary types.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset