-
While simplifying an expression, a Normal Form (
NF
) is a form where no further simplification can be performed; the expressions of this form has reached its final stage and cannot be simplified any more. -
Haskell
tries to evaluate an input expression into aNF
. -
Certain expressions do not have any
Normal Form
; for instance, theHaskell
definition of the formundefined = undefined
when simplified falls into an infinite loop as there is no final stage for it. -
The expressions evaluation rule has a significant effect on its simplification. As for the simplification to reach a final stage or stop at some point, depends on the evaluation rule. For instance
const x = \y -> x
returns a constant function by ignoring it's argumenty
thus returningx
always. Strict languages say "the argument expression shall be evaluated before the function call, and the called function shall receive the obtained value as its parameter".
This is called Call By Value
. With CBV
, an expression like const 0 undefined
would loop forever as it will try to evaluate the
arguments forever.
Lazy languages like haskell
say "the called function shall receive
the unevaluated argument expression as its parameter". This is called
Call By Need
. With CBN
, an expression like const 0 undefined
can
be as follows
const 0 undefined = (\x -> 0) undefined
= 0
-
The called function can decide if it needs the value of the argument. For instance, if the value is a list, the function can decide how many elements it needs to have.
-
In
haskell
yf the code is written as one long line (with the {;}s) then we always simplify at the first possible position. That position is called theHead
and the end result wouldHead NF
orHBF
-
Functional programming languages stop as soon as the overall shape of the whole expression is not a call, but something like
\f -> f x
and evaluating the argumentx
does not make sense any more, because we do not know the functionf
using it anyway. This is calledWeak HNF
orWHNF
.
-- data type definition
data Choice = Definitely
| Possibly
| NoWay
deriving ( Eq, Ord, Enum, Bounded, Show, Read )
-- class definition
class Equals a where
isEqual :: a -> a -> Bool
-- making a data type an instance of the class
instance Equals Choice where
isEqual Definitely Definitely = True
isEqual Possibly Possibly = True
isEqual NoWay NoWay = True
isEqual _ _ = False
instance (Equals a) => Equals [a] where
isEqual (a:as) (b:bs) = isEqual a b &&
isEqual as bs
isEqual as bs = null as && null bs
instance Eq Choice where
Definitely == Definitely = True
Possibly == Possibly = True
NoWay == NoWay = True
_ == _ = False
Define Fibonacci numbers using pairs of lazy list
fibs :: Num a => [a]
fibs = map snd $ iterate (\(x, y) -> (y, x + y)) (0, 1)
-- λ> take 10 fibs
-- [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
fib :: [Integer]
fib = 0 : 1 : zipWith (+) fib (tail fib)
a smiple triangle
λ> mapM_ print $ map (flip replicate '#') [1..5]
"#"
"##"
"###"
"####"
"#####"
Haskell
provides folding of a data structure from either the Left
or Right
. The standard haskell
library defines functions for both
folding from the Right as well as Left. The right fold is defined
similar to the following
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f acc xs = case xs of
[] -> acc
(y : ys) -> f y (foldr f acc ys)
The foldr
function takes a binary function and an identity
value
along with a list and reduces the list to a single value of the type
same as the identity
. It may be depcited pctorially as a binary tree
as follows, for a list of values [x1, x2, x3]
and a function fn
with an initial value as u
serving as identity
.
foldr fn u [x1, x2, ..., xn] = x1 `fn` (x2 `fn` (...(xn `fn` u)...))
fn
/ \
x1 fn
/ \
x2 fn
/ \
x3 u
The foldl
function does essentially the same, except that it folds
or reduces the list to the left side. For a function fn
and some
initial identity
element u
over the list of values [x1,x2,x3]
,
we may represent foldl definition pictorially as follows.
foldl fn u [x1, x2, ..., xn] = (...((u `fn` x1) `fn` x2) `fn` ...) `fn` xn
fn
/ \
fn x1
/ \
fn x2
/ \
u x3
Its defined as below in the standard haskell
library
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f acc xs = case xs of
[] -> acc
(y : ys) -> foldl f (f acc y) ys
We can have the below identity defining a relationship between the left fold and right fold functions.
foldr op u xs == foldl (flip op) u (reverse xs)
Mathematically composition of functions is denoted by a circle
o
. So,(f o g)(x) = f(g(x))
The same can be represented in haskell
using the standard (.)
operator defined in the Haskell
language as below.
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
A Functor
type class is for the things, which can be mapped over. In
normal terms we can say that a Functor
applies a normal function to
some wrapped value in order to return a new wrapped value.
Here is how the Functor
class is defined in the standard haskell
library.
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
The type of the wrapper should be * -> *
A
Functor
class takes a type constructor that takes one type. All instances of theFunctor
class should satisfy the below two laws:
fmap id == id
fmap (f . g) == fmap f . fmap g
All the instances of Functor
for standard data types like the
Lists
, Maybe
, Either
and IO
satisfy the above laws.
Here is how the popular structures like
lists
,Maybe
,Either
and->
are made instances of theFunctor
type class
-- list as a functor instance
instance Functor [] where
fmap = map
-- or
instance Functor [] where
fmap f [] = []
fmap f (x : xs) = f x : fmap f xs
-- Maybe as a functor instance
instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap f (Nothing) = Nothing
-- Either as a functor
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap f (Left x) = Left x
-- IO as a functor
instance Functor IO where
fmap f x = do
result <- x
return (f resullt)
-- (->) as a functor
instance Functor ((->) r) where
fmap f g = (\x -> f (g x))
With (->
) being an instance of the Functor
class, we can have
some of the below interesting points
fmap :: (a -> b) -> f a -> f b
replace all f's with (->
) r
fmap :: (a -> b) -> ((->) r a) -> ((->) r b)
But ((->) r a)
and ((->) r b)
can be written as r -> a
and r -> b
fmap :: (a -> b) -> (r -> a) -> (r -> b)
The above relation is nothing more than a function composition and can be re-written as below
instance Functor ((->) r) where
fmap = (.)
In Applicatives
we apply an already wrapped function over another
wrapped value. Applicatives
are defined in the standard haskell
library package Control.Applicative
. An Applicative
has the
following general class signature.
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
As per the definition, for a type to be an Applicative
, it first
needs to be a Functor
.
The pure
in the class definition, which has the form of a -> f a
is actually wrapping a normal value with a default minimal
context. This is called as lifting
as its actually is lifting a
simple value to a wrapped value as defined.
For a function to act as an Appllicative
it first needs to be a Functor
.
Instances of the
Applicative
Standard data types like Maybe
, Either
etc., which are all
Functors
may be made instances of the Applicative
class.
-- Maybe as an Applicative
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
_ <*> Nothing = Nothing
(Just f) <*> x = Just (f x)
Some important layouts an infix version of the
fmap
is<$>
and the same is defined as below
(<$>) :: (a -> b) -> f a -> f b
f <$> x = fmap f x
based on the above...
Maybe
as anApplicative
fmap f x = pure f <*> x
= f <$> x
= (<*>) . pure
-- the last one due to ETA reduction
List
as anApplicative
instance
instance Applicative [] where
pure x = [x]
fs <*> xs = [f x | f <- fs, x <- xs]
IO
as anApplicative
instance
instance Applicative IO where
pure = return
a <*> b = do
f <- a
x <- b
return (f x)
(->)
as anApplicative
instance
instance Applicative ((->) r) where
pure x = (\_ -> x)
f <*> g = \x -> f x (g x)
ZipList
as anApplicative
instance
List
was already made an instance of the Applicative
. But if
during combination of two or more lists using some binary or another
higher order functon, if we want to apply the function to each element
like first element of list1 with first element of list2 and so on, we
first need to define the list type in an alternative way. And
ZipList
is there for exactly the same purpose.
instance Applicative ZipList where
pure x = ZipList (repeat x)
ZipList fs <*> ZipList xs = ZipList (zipWith (\f x -> f x) fs xs)
newtype ZipList a = ZipList { getZipList :: [a] }
pure id <*> v = v -- identity
pure (.) <*> u <*> v <*> w = u <*> (v <*> w) -- composition
pure f <*> pure x = pure (f x) -- homomorphism
u <*> pure y = pure ($ y) <*> u -- interchange
In the abstract mathematical sense, we can define a Group
G
as a set of
un-ordered unique elements associated with a binary operation such that
- for every element x, y in the
Group G
,(x . y)
is also an element ofGroup G
- for every element x, y, z in the
Group G
,x . (y . z) = (x . y) . z
there is some elemente
(referred to as anidentity
element) in theGroup G
such that for any elementx
in theGroup G
,x . e = e . x = x
. - for any
x
inGroup G
, there exists some elementy
which satisfies the relationx . y = y . x = e
. That isy
is an inverse ofx
.
In haskell
such a group can be defined in terms of a type class as below
class Group g where
-- identity element
identity :: g
-- binary operation
bin :: g -> g -> g
-- inverse for an element
inverse :: g -> g
Now whenever we want to create an instance of such a group, we need to verify that the following laws hold good.
the below is not a valid
haskell
code, but is just a simplified means of expressing the previously defined laws
-- binary associative operation
forall x y z => x `bin` (y `bin` z) = (x `bin` y) `bin` z
-- an identity
forall x => identity `bin` x == x `bin` identity == x
-- inverse of every element
forall x => (inverse x) `bin` x == x `bin` (inverse x) == identity
For the actual real world usage in haskell
we prefer a much
simplified abstract concept than a Group
which is called a Monoid
.
In abstract mathematics a Monoid
is regarded as an algebraic data
structure with a single binary associative operation and an identity
element. Monoids
are an extension to the Semigroup(s)
with identity
.
If S
is some Set and .
is some binary function or operation, such
that S . S -> S
, then S
together with .
qualifies as a Monoid
if it satisfies the below laws.
- Associativity
- Identity element
A semigroup
together with an identity
element is called a monoid
A monoid
in which each each element has an inverse is called a group
.
Monoids
are types with an associative binary operation which have an
identity element. Monoid
class is defined in the standard haskell
library as below
Note: if a
Monoid
does not satisfy the identity law, then it is called aSemigroup
. It exists in thehaskell
packageData.Semigroup
class Monoid m where
mempty :: m
mappend :: m -> m -> m
mconcat :: [a] -> a
mconcat = foldr mappend mempty
Monoids
are defined in the standard package Data.Monoid
. The
infix
version of the function mappend
is (<>)
and is defined as below
infix r6 <>
(<>) :: (Monoid m) => m -> m -> m
(<>) = mappend
-- laws governing the monoids
a <> (b <> c) = (a <> b) <> c -- associativity
mempty <> a = a -- left identity
a <> mempty = a -- right identity
Instances of a
Monoid
should satisfy the below laws
mempty `mappend` x = x
x `mappend` mempty = x
(x `mappend` y) `mappend` z = x `mappend` (y `mappend` z)
Instances of
monoid
-- list as monoid
instance Monoid [a] where
mempty = []
mappend = (++)
-- Maybe as monoid
instance (Monoid a) => Monoid (Maybe a) where
mempty = Nothing
Nothing `mappend` m = m
m `mappend` Nothing = m
Just x `mappend` Just y = Just ( x `mappend` y)
They wrap Maybe values to provide a new monoid
instance.
-
The
First
wrapper returns the left-most or first nonNothing
value. -
The
Last
wrapper returns the right-most or last nonNothing
value.
Here is how they are defined under the package Data.Monoid
newtype First a = First { getFirst :: Maybe a }
deriving (Eq, Ord, Show, Read)
-- make First an instance of Monoid
instance Monoid (First a) where
mempty = First Nothing
z@(First (Just _)) `mappend` _ = z
First Nothing `mappend` z = z
newtype Last a = Last { getLast :: Maybe a }
deriving (Eq, Ord, Show, Read)
-- make Last an instance of Monoid
instance Monoid (Last a) where
mempty = Last Nothing
_ `mappend` z@(Last (Just _)) = z
z `mappend` Last Nothing = z
Ordering
data type as aMonoid
data Ordering = LT | GT | EQ
instance Monoid Ordering where
mempty = EQ
LT `mappend` _ = LT
EQ `mappend` y = y
GT `mappend` _ = GT
Here is the class definition for Monad
Monad
is a concept from a branch of mathematics known as category
theory. Monads are mathematical structures which can abstractly handle
uncertainty (during compile times). From Haskell's perspective
however, it is best to think of a monad as an abstract datatype of
actions. Haskell's do expressions provide a convenient syntax for
writing monadic expressions.
- Class definition of
Monad
class Applicative m => Monad (m :: * -> *) where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
fail :: String -> m a
All the instances of
Monads
should beApplicative
s andFunctor
s prior to that. All the instances ofMonads
should satisfy the below three laws
return x >>= f = f x -- left identity
x >>= return = x -- right identity
(m >>= f) >>= g = m >>= (\x -> f x >>= g) -- associativity
additionally instances of
Monad
andFunctor
should satisfy the below law
fmap f xs == xs >>= return . f
here is how
List
,Maybe
,Either
and(->)
are defined as instances ofMonad
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
[] >>= _ = []
(x : xs) >>= f = f x ++ (xs >>= f)
fail _ = []
instance Monad Maybe where
return = Just
Just x >>= f = f x
Nothing >>= _ = Nothing
fail = Nothing
instance (Error e) => Monad (Either e) where
return x = Right x
Right x >>= f = f x
Left err >>= f = Left err
fail msg = Left (strMsg msg)
instance Monad ((->) r) where
return x = \_ -> x
g >>= f = \x -> f (g x) x
-- simple example
λ> [0..9] >>= \x -> show (x + 1)
"12345678910"
We can model non-determinism in
haskell
by returning a list of alternatives. A non-deterministic computation fromAs
givingBs
becomes a functionA -> [B]
As an example we consider the list pairs of dice values summing up to a number
solution using list comprehensions
diceSum :: (Integral a) => a -> [(a, a)]
diceSum x = [(y1, y2) | y1 <- [1 .. 6], y2 <- [1 .. 6], y1 + y2 == x]
but lists being
monads
we can have a monadic equivalent as below
diceSum :: (Integral a) => a -> [(a, a)]
diceSum x = [1 .. 6] >>= \y1 ->
[1 .. 6] >>= \y2 ->
if y1 + y2 == x then return (y1, y2) else []
One function which is useful in conditional checking is guard
and its defined as below
guard :: (MonadPlus m) => Bool -> m ()
guard True = return ()
guard False = mzero
-- and specifically for list its version will be
guard :: Bool -> [()]
guard True = return ()
guard False = []
-- example
λ> ["abc","","","def","","ghi"] >>= \x -> guard (not (null x)) >> return x
["abc", "def", "ghi"]
- the below relation is valid as well from the
Monad
definition
(>>) :: (Monad m) => m () -> m () -> m ()
x >> y = x >>= \_ -> y
Maybe
, Either
and List
monads are quite similar in the sense that all of them represent the number of results a computation can have. They can all be used to represent computations which may fail.
Each of these monads can handle both SUCCESS and FAILURE
- for list
[xs]
FAILURE = empty list or[]
and SUCCESS =[xs]
, a Non-Empty list - for
Maybe a
FAILURE =Nothing
and SUCCESS =Just a
- for
Either a b
FAILURE =Left a
and SUCCESS =Right b
Given two computations in one of these monads, it might be interesting to amalgamate the following:
-
Find all valid solutions; for instance using lists, given 2 lists of valid solutions one can find all the valid solutions by simply concatenating the lists together.
-
Find the failure case as discussed earlier.
We can combine these two features into a typeclass called MonadPlus
The
MonadPlus
type class is formonads
which can also act asmonoids
They are theMonads
which support Choice and Failure
class Monad m => MonadPlus m where
mzero :: m a
mplus :: m a -> m a -> m a
mzero
is the monadic value standing for zero results.mplus
is a binary function which combines two computations.
here is how
List
andMaybe
are defined as instances ofMonadPlus
instance MonadPlus [] where
mzero = []
mplus = (++)
instance MonadPlus Maybe where
mzero = Nothing
Nothing `mplus` Nothing = Nothing
Just x `mplus` Nothing = Just x
Nothing `mplus` Just x = Just x
Just x `mplus` Just y = Just x
import Control.Monad.Error
-- Either allows the failing computations to include an error message
instance (Error e) => MonadPlus (Either e) where
mzero = Left nomsg
Left _ `mplus` m = m
Right x `mplus` _ = Right x
Instances of MonadPlus
are required to follow certain laws similar to Monads
as follows:
mzero `mplus` m = m -- Monoid
m `mplus` mzero = m
m `mplus` (n `mplus` o) = (m `mplus` n) `mplus` o
mzero >>= f = mzero -- Left Zero
m >> mzero = mzero
(m `mplus` n) >>= k = (m >>= k) `mplus` (n >>= k) -- Left Distribution
return m `mplus` n = return a -- Left Catch
Apart from the above defined
mzero
andmplus
functions, there are a few other functions like the below:
msum
One common task while working with the instances of MonadPlus
is to take a list of the monad
, for instance [Maybe a]
or [[a]]
, and fold
then down the list with mplus
. msum
fulfills this role as defined below:
msum :: MonadPlus m => [m a] -> m a
msum = foldr mplus mzero
A nice way of thinking about this is that it generalises the list-specific concat operation. Indeed, for lists, the two are equivalent. For Maybe
it finds the first Just x
in the list, or returns Nothing
if there aren't any.
guard
The
guard
function is useful for conditional evaluation as defined earlier under theMonad
section.
guard :: (MonadPlus m) => Bool -> m ()
guard True = return ()
guard False = mzero
Functor
,Applicative
andMonad
are three of the most import and related classes ofHaskell
. If thepure
andreturn
are ignored for a moment, the characteristics methods of the three can be summarized as below.
(<$>) :: Functor t => (a -> b) -> (t a -> t b)
(<*>) :: Applicative t => t (a -> b) -> (t a -> t b)
(=<<) :: Monad t => (a -> t b) -> (t a -> t b)
haskell
adds some syntax sugar for conversion between do
and >>=
the below are equivalent
do
a <- f
b <- g
c <- h
return (a, b, c)
which is same as...
f >>= \a ->
g >>= \b ->
h >>= \c ->
return (a, b, c)
There are some additional combinators for monads like ((>=>), (<=<))
which compose two different monadic actions in sequence. The operation (<=<)
is the monadic equivalent of the regular function composition operator (.)
and (>=>)
is same as flip (<=<)
.
definition of
>=>
and<=<
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c
specifically using
((->) r)
f >=> g = \x -> f x >>= g
= \x -> (\r -> g (f x r) r)
- or
(f >=> g) x r = g (f x r) r
correspondence between monadic
and non monadic
compositions
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
The 3 monad
laws can be expressed in terms of the kleisli arrows
as follows
return >=> f = f -- Left Identity
f >=> return = f -- Right Identity
(f >=> g) >=> h = f >=> (h >=> g) -- Associativity
miscellaneous identities
f >>= x = join (fmap f x)
fmap f x = x >>= (return . f)
join f = f >>= id
We can derive the below relation to assert the statement
fmap f x = x >>= return . f
from the ((->) r)
fmap f x r = (x >>= (const . f)) r
= (const . f) (x r) r
= const (f (x r)) r
= f (x r)
= (f . x) r
Translating
do
notation
Each line x <- e; ...
translates to e >>= \x -> ...
Each line e; ...
trnaslates to e >> ...
- An example of the above translation
do { x1 <- e1;
x2 <- e2;
e3;
x4 <- e4;
e5;
e6 }
-- is equivalent to
e1 >>= \x1 ->
e2 >>= \x2 ->
e3 >>
e4 >>= \x4 ->
e5 >>
e6
an example with pythagorean triplets
pythagoreanTriplets :: (Integral a) => a -> [a]
pythagoreanTriplets n = do
x <- [1 .. n]
y <- [x .. n]
z <- [y .. n]
guard (x^2 + y^2 == z^2)
return (x, y, z)
- re-writing the above function using chaining
λ> import Control.Monad
λ> let pythagoreanTriplets n = [1 .. n] >>=
\x -> [x .. n] >>=
\y -> [y .. n] >>=
\z -> guard (x^2 + y^2 == z^2) >> return (x, y, z)
…
λ> :t pythagoreanTriplets
pythagoreanTriplets :: (Num t, Eq t, Enum t) => t -> [(t, t, t)]
λ> pythagoreanTriplets 5
[(3, 4, 5)]
λ> pythagoreanTriplets 10
[(3, 4, 5), (6, 8, 10)]