Your easy guide to Monads, Applicatives, & Functors

What’s a functor?

A functor is a list.

Created with Blender and Gifcurry.
map(function)(list) = [function(element) | element <- list]add1(input) = input + 1map(add1)([1,2,3])-- [2,3,4]

A functor is a function.

Created with Blender and Gifcurry.
map(function1)(function2)(input) = function1(function2(input))

sub2(input) = input - 2
map(add1)(sub2)(1)
-- 0

A functor is a promise.

Created with Blender and Gifcurry.
map(function)(promise) = fmap(function)(promise)promise <- async(return 11)wait(map(sub2)(promise))
-- 9

A functor is an effect that you lawfully define map for.

Created with Blender and Gifcurry.
map
:: (input -> output) -- Takes a function.
-> effect(input ) -- Takes input inside an effect.
-> effect( output) -- Returns output inside an effect.
  • takes a function input -> output ,
  • takes input for that function stuck inside an effect effect(input) ,
  • passes the input to the function,
  • collects the function’s output,
  • and returns the output stuck inside the effect effect(output) .

Wait, what do you mean by “lawfully define?”

How many functor laws are there?

Okay, what’s the first law?

Created with Blender and Gifcurry.
list = [1,2,3]identity(input) = inputlist == map(identity)(list)
-- True

Hmm, what’s the second and last law?

Created with Blender and Gifcurry.
times3(input) = input * 3composition(input) = add1(sub2(times3(input)))composition(1)
-- 2
list = [1,2,3]map(composition)(list) == map(add1)(map(sub2)(map(times3)(list)))
-- True

So functor is an interface?

What do you mean by effect?

I still don’t get functor.

Can map accept a function with more than one parameter?

add(left)(right) = left + rightmap(add)([1,2,3])
-- [ add(1),
-- add(2),
-- add(3) ]
[ add(1),
add(2),
add(3) ]

What are applicative functors?

An applicative functor is a list.

Created with Blender and Gifcurry.
pure(input) = [input]apply(functions)(list) =
[ element | function <- functions,
element <- map(function)(list)
]
apply(pure(add1))([1,2,3])
-- [2,3,4]
apply(apply(pure(add))([1,2,3]))([4,5,6])
-- [5,6,7,6,7,8,7,8,9]
apply(map(add)([1,2,3]))([4,5,6])
-- [5,6,7,6,7,8,7,8,9]
Created with Blender and Gifcurry.
apply(pure(add))([1,2,3])
-- [ add(1),
-- add(2),
-- add(3) ]
map(add)([1,2,3])
-- [ add(1),
-- add(2),
-- add(3) ]
[   ] `apply` [1,2,3] `apply` [4,5,6]
-- []
[add] `apply` [ ] `apply` [4,5,6]
-- []
[add] `apply` [1,2,3] `apply` [ ]
-- []
if_a_then_b_else_c(a)(b)(c) = if a then b else cpure(if_a_then_b_else_c) `apply` x `apply` y `apply` z
if_x_then_y_else_z([True])(["y result"])(["z result"])
-- ["y result"]
if_x_then_y_else_z([False])(["y result"])(["z result"])
-- ["z result"]
if_x_then_y_else_z([True])(["y result"])(["z result"])
-- ["y result"]
  • [True]
  • ["y result"]
  • and ["z result"]
if_x_then_y_else_z([True])(["y result"])([])
-- []
if_x_then_y_else_z(x)(y)(z) =
apply(pure(\ a -> if a then y else z))(x)
if_x_then_y_else_z([True])(["y result"])([])
-- [["y result"]]

An applicative functor is a function.

Created with Blender and Gifcurry.
pure(function)(ignore) = functionpure(add1)(Nothing)(0)
-- 1
apply(wrapped_function)(function)(input) =
map(wrapped_function(input))(function)(input)
apply(pure(add1))(add1)(1)
-- 3
(pure(add) `apply` add1 `apply` add1)(1)
-- 4
pure(add1)("Ignore this?")(1)
-- 2
pure(add1)(concat ["Ignore"," this?"])(1)
-- 2
add_x_y_z(x)(y)(z) = add(add(x)(y))(z)add_x_y_z(2)(2)(2)
-- 6
(pure(add_x_y_z)
`apply` add1
`apply` add1
`apply` add1)(1)
-- 6
  • 1 gets added to 1 making x equal 2
  • 1 gets added to 1 making y equal 2
  • 1 gets added to 1 making z equal 2
  • and finally x, y, and z get added together for a total of 6.
apply(pure(add_x_y_z))(add1)(1)(2)(3)
-- 7
add_x_y_z(add1)(2)(3)
-- 7
add1(input) = 1 + input
sub2(input) = 2 - input
sub3(input) = 3 - input

(pure(add_x_y_z) `apply` add1 `apply` sub2 `apply` sub3)(4)
-- 2
add1(4) + sub2(4) + sub3(4)
-- 2
(1 + 4) + (2 - 4) + (3 - 4)
-- 2
first  = \ (a,b,c) -> a                                                               
second = \ (a,b,c) -> b
third = \ (a,b,c) -> c
new_add_x_y_z =
pure(add_x_y_z) `apply` first `apply` second `apply` third
new_add_x_y_z(1,2,3)
-- 6
extract_then_add_x_y_z(t) =
add_x_y_z(first(t))(second(t))(third(t))
extract_then_add_x_y_z(1,2,3)
-- 6
first  = \ (a,b) -> a
second = \ (a,b) -> b
new_add_x_y_z = pure(add_x_y_z) `apply` first `apply` secondnew_add_x_y_z(1,2)(3)
-- 6

An applicative functor is a functor that you lawfully define pure and apply for.

Created with Blender and Gifcurry.
pure
:: input -- Takes some input.
-> effect(input) -- Returns it inside an effect.
apply
:: effect(input -> output) -- Takes a function in an effect.
-> effect(input ) -- Takes input in an effect.
-> effect( output) -- Returns output in an effect.
  • takes a function inside an effect effect(input -> output),
  • takes input for that function inside an effect effect(input),
  • applies the function to the input,
  • and returns the function’s output inside the effect effect(output) .
map
:: (input -> output) -- Takes a function.
-> effect(input ) -- Takes input inside an effect.
-> effect( output) -- Returns output inside an effect.
apply
:: effect(input -> output) -- Takes a function in an effect.
-> effect(input ) -- Takes input in an effect.
-> effect( output) -- Returns output in an effect.
map   ::  (i -> o) -> e(i) -> e(o)
apply :: e(i -> o) -> e(i) -> e(o)
-- ^ The big difference.

Lawfully, meaning applicative functor has laws too?

So applicative functor is an interface too?

I still don’t get applicative functor.

What do you mean by independent effects?

x = [1,2]
y = [3,4]
z = [5,6]
pure(add) `apply` (pure(add) `apply` x `apply` y) `apply` z
-- [9,10,10,11,10,11,11,12]
[ 1+3+5=09,
1+3+6=10,
1+4+5=10,
1+4+6=11,
2+3+5=10,
2+3+6=11,
2+4+5=11,
2+4+6=12 ]
add1_inside_effect(input) = pure(input + 1)
pure(add1_inside_an_effect) `apply` x                    
-- [[2],[3]]
pure(add) `apply` (pure(add1_inside_an_effect) `apply` x)
-- error!

What is a monad?

A monad is a list.

Created with Blender and Gifcurry.
join(nested_lists) =
[element | list <- nested_lists, element <- list]
join([[1], [2, 3], [4, 5, 6]])
-- [1, 2, 3, 4, 5, 6]
join(apply(pure(\ x -> [x + 1, 1]), [1,2,3]))
-- [2, 1, 3, 1, 4, 1]
join(
apply(
pure(\ x -> [2, x * 3, x * 4]))(
join(
apply(
pure(\ x -> [x + 1, 1]))(
[1,2,3]))))
-- [2,6,8,2,3,4,2,9,12,2,3,4,2,12,16,2,3,4]
if_x_then_y_else_z =
pure(\ a -> if a then y else z) `apply` x
if_x_then_y_else_z([True])(["y result"])([])
-- [["y result"]]
if_x_then_y_else_z(x)(y)(z) =
join(pure(\ a -> if a then y else z) `apply` x)
if_x_then_y_else_z(
[True,False,False,True])(
["y result"])(
["z result"])
-- [ "y result", True so return y.
-- "z result", False so return z.
-- "z result", False so return z.
-- "y result" ] True so return y.

A monad is a function.

Created with Blender and Gifcurry.
join(wrapped_function)(input) =
wrapped_function(input)(input)
join(apply(pure(add))(add1))(1)
-- 3
add(add(1)(1))(1)
-- 3
add(2)(1)
-- 3
(1 + 1) + 1
-- 3
divide(numerator)(denominator) = numerator / denominatorjoin(
apply(
pure(divide))(
join(
apply(
pure(add))(
add1))))(2)
-- 2.5
divide(
add(
add(
1)(
2))(
2))(
2)
-- 2.5
divide(
add(
3)(
2))(
2)
-- 2.5
divide(
5)(
2)
-- 2.5
(((1 + 2) + 2) / 2)
-- 2.5
  • The first function takes only one parameter.
  • It receives the input and returns some output.
  • The second function takes two parameters.
  • It receives the output from function one, the input, and returns some output.
  • The third function takes two parameters.
  • It receives the output from function two, the input, and returns some output.
  • The fourth function…and on and on for how ever many functions you sequenced.

A monad is an applicative functor that you lawfully define join for.

Created with Blender and Gifcurry.
join
:: effect(effect(data))
-> effect(data)

So monads are just applicative functors with join added to the interface?

How many laws are there for monad?

What about return and bind?

bind(effect)(function) = join(apply(pure(function))(effect))[1,2,3] `bind` \ x -> [x + 2]
-- [3,4,5]
[1,2,3] `bind` \ x -> [x + 2] `bind` \ y -> [y * 3]
-- [9,12,15]
[1,2,3] `bind` \ x -> pure(x + 2) `bind` \ y -> [y * 3, 0]
-- [9,0,12,0,15,0]
(add1 `bind` add `bind` divide)(2)
-- 2.5
bind(effect)(function) = join(map(function)(effect))[1,2,3] `bind` \ x -> pure(x + 2) `bind` \ y -> [y * 3, 0] 
-- [9,0,12,0,15,0]
return = pure

Can you summarize them for me?

Created with Blender and Gifcurry.
  • Functor lifts or upgrades a function, allowing it to operate on a single effect, leaving the effect intact after it’s done. It requires a lawful map definition.
  • Applicative functor builds on or generalizes functor, allowing you to sequence multiple independent effects. It requires a lawful pure and apply definition.
  • Monad builds on or generalizes applicative functor, allowing you to sequence independent and/or dependent effects. It requires a lawful join definition.
map   ::  (i ->   o ) -> e(i) -> e(o) -- Functor
apply :: e(i -> o ) -> e(i) -> e(o) -- Applicative
bind :: (i -> e(o)) -> e(i) -> e(o) -- Monad
^ ^

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store