*Haskell, an open source programming language, is the outcome of 20 years of research. It is named after the logician, Haskell Curry. It has all the advantages of functional programming and an intuitive syntax based on mathematical notation. Lets continue our exploration of Haskell.*

In this article, we shall explore more type classes in Haskell. Consider the* Functor* type class:

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

It defines a function* fmap,* which accepts a function as an argument that takes input of* Type a* and returns *Type b*, and applies the function on every *Type a* to produce *Type b*. The* f* is a type constructor. An array is an instance of the *Functor* class and is defined as shown below:

instance Functor [] where fmap = map

The* Functor* type class is used for types that can be mapped over. Examples of using the *Functor* type class for arrays are shown below:

ghci> fmap length [abc, defg] [3,4] ghci> :t length length :: [a] -> Int ghci> map length [abc, defg] [3,4] ghci> :t map map :: (a -> b) -> [a] -> [b]

An instance of a* Functor* class must satisfy two laws. First, it must satisfy the identity property where running the map over an id must return the id. The term id represents identity.

fmap id = id

For example:

ghci> id [abc] [abc] ghci> fmap id [abc] [abc]

Second, if we compose two functions and *fmap* over it, then it must be the same as mapping the first function with the *Functor*, and then applying the second function as shown below:

fmap (f . g) = fmap f . fmap g

This can also be written for a *Functor F* as follows:

fmap (f . g) F = fmap f (fmap g F)

For example:

ghci> fmap (negate . abs) [1, 2, 3, 4, 5] [-1,-2,-3,-4,-5] ghci> fmap negate (fmap abs [1, 2, 3, 4, 5]) [-1,-2,-3,-4,-5]

The* Maybe* data type can also be an instance of the* Functor* class:

data Maybe a = Just a | Nothing deriving (Eq, Ord) instance Functor Maybe where fmap f (Just x) = Just (f x) fmap f Nothing = Nothing

For example:

ghci> fmap (+2) (Nothing) Nothing ghci> fmap (+2) (Just 3) Just 5

The two laws hold good for the *Maybe* data type:

ghci> id Nothing Nothing ghci> id Just 4 Just 4 ghci> fmap (negate . abs) (Just 4) Just (-4) ghci> fmap negate (fmap abs (Just 4)) Just (-4)

The* Applicative* type class is defined to handle cases where a function is enclosed in a Functor, like Just (*2):

class Functor f => Applicative f where -- | Lift a value. pure :: a -> f a -- | Sequential application. (<*>) :: f (a -> b) -> f a -> f b

The <$> is defined as a synonym for fmap:

(<$>) :: Functor f => (a -> b) -> f a -> f b f <$> a = fmap f a

The* Applicative Functor* must also satisfy a few mathematical laws. The *Maybe* data type can be an instance of the* Applicative* class:

instance Applicative Maybe where pure = Just (Just f) <*> (Just x) = Just (f x) _ <*> _ = Nothing

A few examples of *Maybe* for the *Applicative type* class are shown below:

ghci> import Control.Applicative ghci> Just (+2) <*> Just 7 Just 9 ghci> (*) <$> Just 3 <*> Just 4 Just 12 ghci> min <$> Just 4 <*> Just 6 Just 4 ghci> max <$> Just Hello <*> Nothing Nothing ghci> max <$> Just Hello <*> Just World Just World

The* Applicative Functor* unwraps the values before performing an operation.

For a data type to be an instance of the* Monoid* type class, it must satisfy two properties:

1. Identity value

2. Associative binary operator

a * (b * c) = (a * b) * c

These are defined in the *Monoid* type class:

class Monoid a where mempty :: a -- identity mappend :: a -> a -> a -- associative binary operation

Lists can be a* Monoid*. The identity operator is [] and the associative binary operator is (++). The instance definition of lists for a* Monoid* is given below:

instance Monoid [a] where mempty = [] mappend = (++)

Some examples of lists as *Monoid* are shown below:

ghci> import Data.Monoid ghci> (a `mappend` b) `mappend` c abc ghci> a `mappend` (b `mappend` c) abc ghci> mempty `mappend` [5] [5]

The* Monad* type class takes a wrapped value and a function that does some computation after unwrapping the value, and returns a wrapped result. The *Monad* is a container type and hence a value is wrapped in it. The *bind* operation (>>=) is the important function in the *Monad* class that performs this operation. The return function converts the result into a wrapped value. *Monads* are used for impure code where there can be side effects, for example, during a system call, performing IO, etc. A data type that implements the *Monad* class must obey the *Monad* Laws. The definition of the *Monad* class is as follows:

class Monad m where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a fail :: String -> m a

The *Maybe type* is an instance of a *Monad* and is defined as:

instance Monad Maybe where return x = Just x Nothing >>= f = Nothing Just x >>= f = f x fail _ = Nothing

So, when m is *Maybe*, and a and b are of Type Int, the bind operation becomes:

(>>=) :: Maybe Int -> (Int -> Maybe Int) -> Maybe Int

Heres an example of how the* Maybe Monad* is used:

ghci> return (Just 5) Just 5 ghci> return Nothing Nothing ghci> Just 5 >>= \x -> return (x + 7) Just 12 ghci> Nothing >>= \x -> return (x + 7) Nothing ghci> Just 5 >>= \x -> return (x + 7) >>= \y -> return (y + 2) Just 14

The *newtype* keyword is used in Haskell to define a new data type that has only one constructor and only one field inside it. The* Writer* data type can be defined using the record syntax as follows:

newtype Writer w a = Writer { runWriter :: (a, w) }

It can be an instance of a* Monad* as follows:

import Data.Monoid newtype Writer w a = Writer { runWriter :: (a, w) } instance (Monoid w) => Monad (Writer w) where return x = Writer (x, mempty) (Writer (x,v)) >>= f = let (Writer (y, v)) = f x in Writer (y, v `mappend` v)

To test the definition, you can write a double function as shown below:

double :: Int -> Writer String Int double x = Writer (x * 2, doubled ++ (show x))

You can execute it using:

ghci> runWriter $ double 3 (6, doubled 3) ghci> runWriter $ double 3 >>= double (12, doubled 3 doubled 6)

The evaluation for the bind operation is illustrated below:

ghci> runWriter $ double 3 >>= double (12, doubled 3 doubled 6) ghci> runWriter $ ((double 3) >>= double) (12, doubled 3 doubled 6) ghci> runWriter $ ((Writer (6, doubled 3)) >>= double) (12, doubled 3 doubled 6)

The arguments to* runWriter* are matched to the *bind* function definition in the *Writer Monad.* Thus, x == 6, v == doubled 3, and f == double. The function application of f x is double 6 which yields (12, doubled 6). Thus y is 12 and v is doubled 6. The result is wrapped into a *Writer Monad* with y as 12, and the string v concatenated with v to give doubled 3 doubled 6. This example is useful as a logger, where you want a result and log messages appended together. As you can see, the output differs with input, and hence this is impure code that has side effects.

When you have data types, classes and instance definitions, you can organise them into a module that others can reuse. To enclose the definitions inside a module, prepend them with the module keyword. The module name must begin with a capital letter followed by a list of types and functions that are exported by the module. For example:

module Control.Monad.Writer.Class ( MonadWriter(..), listens, censor, ) where ...

You can import a module in your code or at the GHCi prompt, using the following command:

import Control.Monad.Writer

If you want to use only selected functions, you can selectively import them using:

import Control.Monad.Writer(listens)

If you want to import everything except a particular function, you can hide it while importing, as follows:

import Control.Monad.Writer hiding (censor)

If two modules have the same function names, you can explicitly use the fully qualified name, as shown below:

import qualified Control.Monad.Writer

You can then explicitly use the listens functions in the module using *Control.Monad.Writer.listens*. You can also create an alias using the as keyword:

import qualified Control.Monad.Writer as W

You can then invoke the listens function using *W.listens*.

Let us look at an example of the iso8601-time 0.1.2 Haskell package. The module definition is given below:

module Data.Time.ISO8601 ( formatISO8601 , formatISO8601Millis , formatISO8601Micros , formatISO8601Nanos , formatISO8601Picos , formatISO8601Javascript , parseISO8601 ) where

It then imports a few other modules:

import Data.Time.Clock (UTCTime) import Data.Time.Format (formatTime, parseTime) import System.Locale (defaultTimeLocale) import Control.Applicative ((<|>))

This is followed by the definition of functions. Some of them are shown below:

-- | Formats a time in ISO 8601, with up to 12 second decimals. -- -- This is the `formatTime` format @%FT%T%[email protected] == @%%Y-%m-%dT%%H:%M:%S%[email protected] formatISO8601 :: UTCTime -> String formatISO8601 t = formatTime defaultTimeLocale %FT%T%QZ t -- | Pads an ISO 8601 date with trailing zeros, but lacking the trailing Z. -- -- This is needed because `formatTime` with %Q does not create trailing zeros. formatPadded :: UTCTime -> String formatPadded t | length str == 19 = str ++ .000000000000 | otherwise = str ++ 000000000000 where str = formatTime defaultTimeLocale %FT%T%Q t -- | Formats a time in ISO 8601 with up to millisecond precision and trailing zeros. -- The format is precisely: -- >YYYY-MM-DDTHH:mm:ss.sssZ formatISO8601Millis :: UTCTime -> String formatISO8601Millis t = take 23 (formatPadded t) ++ Z ...

The availability of free and open source software allows you to learn a lot from reading the source code, and it is a very essential practice if you want to improve your programming skills.

## Connect With Us