Monads
eg.
type IO a = World-> (a,World)
the above is the approx. The form of the IO monad is predefined by a direct
internal implementation.
So, in short, let us say that Monad contains types that are essentially special
kinds of functions that perhaps might generate some value at times, and can be
composed associatively, with the existence of an identity special function with
respect to the special composition. Mind you again that it is not exactly
composition but a binary operator that
This is the first understanding ^^^^
A better understanding would be that they are any first class objects
Here understand what objects could mean -
could be any functional object with a fixed value and type.
But at the same time it could represent imperative objects like IO, State, which
have some external side effects wrapped within them,
you can think of it as some value with some context around it. Like, in case of
'Either' it is that the value could be erroneous
so any operation on it should be passed through (obj >>= op) for safety
It also wraps side effects, for example IO, the value it has changes depends on the
time it executes, hence imperative in nature.
In a deeper sense, the >>= handles the context and then pass the value to the
incoming function
"Specialized Composition Wrappers": These are structures that add specific behavior
or context to values. For instance:
Maybe provides a context of possible failure.
Either gives a context of success or error, with additional information on errors.
List allows computations to represent multiple possibilities.
"Action Objects": Monads like IO or State encapsulate actions or state
transformations that are handled in sequence.
IO represents interactions with the outside world, such as reading input, writing
output, or accessing the file system. It encapsulates these actions as pure values
that are only executed when the program runs.
State models computations that pass a state along, where each operation might
modify or use the state, giving an effect similar to mutable state in other
languages but within a purely functional framework.
for example
The class type is the following
class Monad m where
return :: a -> m a --this is the identity
>>= :: m a -> (a -> m b) -> m b -- this is the special composition
>> :: m a -> m b -> m b
g >> f = g >>= \_ -> f
For consistent behavior, there are some laws one must follow, Haskell only imposes
the type class definition.
g >>= f
here first g is executed, and the return value if exists is passed onto f, by doing
f x, which executes the second monadic function and returns something
Applications -
1) The List Monad
data [] a = [] | (a:[a])
class Monad [] where
return x = [x]
p >>= f = concat (map f p)
let us define cartesian product on lists
cp :: [[a]] -> [[a]]
cp [] = []
cp (x:xs) = x >>= \y -> ((cp xs) >>= \z -> (y:z)) >>= return
--alt
--cp (x:xs) = do
-- p <- x
-- q <- (cp xs)
-- return (p:q)
--
-- isn't a new way for list comprehensions??
Isnt it nice and elegant
In fact list comprehension are a syntactic sugar for the above
Do you realise the power of monads????
A new form of abstraction, a new paradigm, monadic programming, dirty work is
hidden in monads!!
How do you scale this up??
Remember - monads, dirty work and the actions. P.S. The above was still non
actionable object* so purely functional during execution
isn't this a beauty of higher order functions, a common wrapper operation around
every possible operation on an object
monads could then be used to create new syntactic elements? another benefit?
Where else can they be used in a non-imperative fashion?
any element wise operation thing, lets say tensor operations perhaps could be
defined in this way
lets say a graph, and >>= extracts the root for traversal, or some other kind of
operation on the graph, etc.
______
We spoke about the imperative objects like IO, which when generated carry out some
imperative action.
We also say that Monads prevent side effects, but what are these side effects?
We know Haskell is inherently lazily typed, and functional in nature
But we want imperative actions, Why not do it like how Scheme does it?
Scheme is applicative in execution, so imperative actions are executed wherever
they are defined, but due to Haskell's lazy nature, these actions won't be executed
wherever they are defined, but when they are needed, which is unpredictable and
highly dependent on execution.
This could mean that, in an extreme sense, that imperative actions may not even be
executed, both the substitution model and the environment model goes for a toss.
So, we introduce monads, actions embedded within objects
class Monad m where
return :: a -> m a --this is the identity
>>= :: m a -> (a -> m b) -> m b -- this is the special composition
>> :: m a -> m b -> m b
g >> f = g >>= \_ -> f
For consistent behavior, there are some laws one must follow, Haskell only imposes
the type class definition.
Lets discuss the laws
Certainly! Below is a **concise summary** of the **Monad Laws** in Haskell, the
**key takeaways**, and the **implications** of adhering to these laws.
---
## **Monad Laws in Haskell**
Monads in Haskell must satisfy three fundamental laws to ensure consistent and
predictable behavior. These laws are **Left Identity**, **Right Identity**, and
**Associativity**.
### **1. Left Identity**
For any value `a` and any function `f` that takes a normal value and returns a
monadic value,
```haskell
return a >>= f ≡ f a
```
---
### **2. Right Identity**
For any monadic value `m`,
```haskell
m >>= return ≡ m
```
### **3. Associativity**
For any monadic value `m` and functions `f` and `g` that take a normal value and
return a monadic value,
```haskell
(m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
```
IN SHORT, if we do not follow these laws while defining any monadic, be sure to
expect unexpected behaviours. The compiler does not enforce this
## **Conclusion**
The **Monad Laws**—**Left Identity**, **Right Identity**, and **Associativity**—are
essential principles that ensure monads in Haskell behave consistently and
predictably. These laws are **not enforced by the Haskell compiler** but are
**implicitly required** by the `Monad` type class. It is the **programmer's
responsibility** to implement these laws correctly when defining new monads.
Adhering to these laws guarantees reliable abstractions, facilitates function
composition, maintains referential transparency, and prevents logical errors,
thereby enabling the creation of robust and maintainable Haskell programs.
for example - Lists
return x = [x]
why do we have this way?
isn't return something that does alter the value??
nono, it is something that satisfies the lawsss
so, the laws suggest the overall value shall remain same meaning, for all purposes
of the sequencing/bind operator, inserting a return anywhere would not affect the
chain of execution, thus for all purposes the value does not change
what about associativity? why is it needed?
we want the bind operator to execute everything in sequential order as defined, so
how does one enforce without affecting the purely lazy nature of Haskell
associativity says the irrespective of the order of execution, the final result
shall dependent only on the sequence of operations in the overall expression
So, the law says define your >>= so that the final result depends only on the
sequence, so this is a responsibility of the programmer again. So, its not some
Haskell magic. Well IO is defined inside Haskell, that can be Haskell magic, but
what about other monadic instances defined by the programmer??
example of a faulty monad
--
data FaultyMonad a = Faulty a deriving Show
instance Monad FaultyMonad where
return x = Faulty x
-- Incorrect bind implementation
(Faulty x) >>= f = f (x + 1) -- Arbitrary modification
--
data FaultyState s a = FaultyState (s -> (a, s))
instance Monad (FaultyState s) where
return x = FaultyState (\s -> (x, s))
(FaultyState h) >>= f = FaultyState (\s ->
let (a, s') = h s
FaultyState g = f a
in g s') -- Suppose g s' alters the state incorrectly
_______
The Haskell library System.IO provides many more actions than just putChar
and getChar, including actions to open and read files, to write and close files,
to
buffer output in various ways and so on. We will not go into details in this book.
But perhaps two more things need to be said. Firstly, there is no function of type
IOa->a1. Once you are in a room performing input–output actions, you stay
in the room and can’t come out of it. To see one reason this has to be the case,
suppose there is such a function, runIO say, and consider
int :: Int
int= x- y
where x = runIO readInt
y = runIO readInt
1 Actually there is, and it’s called unsafePerformIO, but it is a very unsafe
function.
10.1 The IO monad
243
readInt = do {xs <- getLine; return (read xs :: Int)}
The action readInt reads a line of input and, provided the line consists entirely
of digits, interprets it as an integer. Now, what is the value of int? The answer
depends entirely on which of x and ygetsevaluated first. Haskell does not
prescribe
whether or not x is evaluated before y in the expression x-y. Put it this way:
input
output actions have to be sequenced in a de
terministic fashion, and Haskell is a
lazy functional language in which it is difficult to determine the order in which
things happen. Of course, an expression such as x-y is a very simple example
(and exactly the same undesirable phenomenon arises in imperative languages) but
you can imagine all sorts of confusion that would ensue if we were provided with
runIO.
The second thing that perhaps should be said is in response to a reader who casts
a
lazy eye over an expression such as
undefined >> return 0 :: IO Int
Does this code raise an error or return zero? The answer is: an error. IO is
strict in
the sense that IO actions are performed in order, even though subsequent actions
may take no heed of their results.
______