Files
2026-10-04 15:24:17 +01:00

2.0 KiB

Monad Revision

You can think of a monad as a container for a data type

If M is a monad, that means an element of M: M_a is some sort of container where a is any datatype

One of the purposes of the do notation is to operate on the whole data structure by specifying operations that must apply to each of the elements in the data structure, without having to specify the whole structure.


M_a=\{x_1, x_2, x_3,...\}
do x <- m
	let y = x ** 2 + 7
	return y

This extracts an element of type a from m, squares and adds 7, and returns the new values as the data structure. Now M is


M_b=\{y_1, y_2, y_3, \ldots\}\\or\\M=\{x_1^2+7, x_2^2+7, x_3^2+7, \ldots\}

The above can be written as a functor

fmap (\x -> x**2+7) m

Monads have more functionality than functors though

If x is an element of a or x :: a

x :: a
return x
-- we can also write
pure x

Monads can have containers within containers

Assume we have a function makeBlob that maps every element of a to an element of M_b

makeBlob :: a -> Mb
makeBlob x1 = do x <- m
				 y <- makeBlob x
				 return y
-- this can be done instead with the bind operator
m >>= makeBlob
(>>=) :: Ma -> (a -> Mb) -> Mb

The IO Monad

square :: Int -> Int
square x = x*x

getInt :: IO Int
getInt = do putStrLn "Enter a number: "
			s <- getLine -- getLine :: IO String
			return (read s :: Int) --read :: String -> Int

squareIO :: IO Int
squareIO = do x <- getInt
			  let y <- square x
			  return y
-- as squareIO :: IO Int, returning y prints it out

squareIO :: IO () -- unit type, with only one element, also called ()
squareIO = do x <- getInt
			  let y <- square x
			  putStrLn("The square " ++ (show x) ++ " is " (show y))
			  return () --return unit type
-- in this case we dont even need return () as
-- putStrLn :: IO ()

--recursively asks for list unless 0 entered
getList :: IO [Int]
getList = do x <- getInt
			 if x == 0 then return []
			 		   else do
			 				xs <- getList
			 				return (x:xs)