Code examples demonstrating Functional Programming concepts, with JavaScript and Haskell.
Part 1 can be found here - http://www.slideshare.net/calvinchengx/functional-programming-part01
Source code can be found here - http://github.com/calvinchengx/learnhaskell
Let me know if you spot any errors! Thank you! :-)
2. WHAT
• Pure versus Impure (exercise)
• Currying (exercise)
• Map, Filter, Reduce (exercise)
• Functors,Applicative Functors, Monads (exercise)
are we diving into?
4. RECOGNIZE
• Functions that don’t change anything out-of-scope
and don’t depend on anything out-of-scope are
called “pure”
• A pure function always gives the same result given
the same parameters; independent of program/
system state
pure functions
8. PURE OR IMPURE?
function
getQueryVariable(variable)
{
var
query
=
window.location.search.substring(1);
var
vars
=
query.split('&');
for
(var
i
=
0;
i
<
vars.length;
i++)
{
var
pair
=
vars[i].split('=');
if
(decodeURIComponent(pair[0])
===
variable)
{
return
decodeURIComponent(pair[1]);
}
}
}
14. CURRYING
var
people
=
[
{
name:
'Calvin'
},
{
name:
'John'
},
{
name:
'Thomas'
}
];
//
function
with
hardcoded
key
'name'
to
retrieve
list
of
names
function
getPersonName(obj)
{
return
obj['name'];
}
//
mapping
to
the
hardcoded
function
var
names
=
people.map(getPersonName);
console.log(names);
JavaScript
15. CURRYING
//
Specify
the
key
on-‐the-‐fly
by
using
a
generic
function.
function
getByKey(key,
obj)
{
return
function(obj)
{
return
obj[key];
};
}
JavaScript
16. CURRYING
function
getByKey(key,
obj)
{
return
function(obj)
{
return
obj[key];
};
}
var
getByKeyPartial
=
getByKey('name');
console.log(getByKeyPartial);
var
names2
=
people.map(getByKeyPartial);
console.log(names2);
[Function]
curried function or partial function
JavaScript
17. CURRYING
function
getByKey(key,
obj)
{
return
function(obj)
{
return
obj[key];
};
}
var
names3
=
people.map(getByKey('name'));
console.log(names3);
[Function]
curried function or partial function
equivalent to:
getByKey(‘name’)(obj) in this context
JavaScript
18. CURRYING
module
Main
where
f
::
Integer
f
=
max
4
5
fPartial
::
(Ord
a,
Num
a)
=>
a
-‐>
a
fPartial
=
max
4
main
::
IO
()
main
=
do
print
f
let
g
=
fPartial
10
print
g
Haskell
19. CURRYING
people
::
[(String,
String)]
people
=
[("name",
"Calvin")
,("wat",
"John")
,("name",
"Thomas")
]
Haskell
20. CURRYING
{-‐
generic
filterByKey
function
that
accepts
a
string
as
keyname
and
the
data
structure
shown
previously
-‐}
filterByKey
::
Eq
a
=>
a
-‐>
[(a,
t)]
-‐>
[t]
filterByKey
_
[]
=
[]
filterByKey
p
((k,
v):xs)
|
p
==
k
=
v
:
filterByKey
p
xs
|
otherwise
=
filterByKey
p
xs
filterByName
::
[(String,
t)]
-‐>
[t]
filterByName
=
filterByKey
"name"
Haskell
[Function]
curried function or partial function
21. CURRYING
main
::
IO
()
main
=
do
let
names2
=
filterByKey
"name"
people
print
names2
let
names3
=
filterByName
people
print
names3
Haskell
[Function]
curried function or partial function
23. MAP, FILTER, REDUCE
//
map,
reduce
and
filter
are
built-‐in
as
methods
of
the
//
Array
class
in
JS
var
aList
=
[0,
1,
2];
var
newList
=
aList.map(function
(i)
{
return
i
+
1;
});
console.log(newList);
console.log(aList);
JavaScript map
25. MAP, FILTER, REDUCE
//
map,
reduce
and
filter
are
built-‐in
as
methods
of
the
//
Array
class
in
JS
var
aList
=
[0,
1,
2];
var
lessThanTwo
=
aList.filter(function
(i)
{
return
i
<
2;
});
console.log(lessThanTwo);
console.log(aList);
JavaScript filter
27. MAP, FILTER, REDUCE
//
map,
reduce
and
filter
are
built-‐in
as
methods
of
the
//
Array
class
in
JS
var
aList
=
[0,
1,
2];
var
reduceToSum
=
aList.reduce(function
(a,
b)
{
return
a
+
b;
});
console.log(reduceToSum);
console.log(aList);
reduce
30. FUNCTORS
function
addOne(value)
{
return
value
+
1;
}
console.log(addOne(10));
//
11
function
addTwo(value)
{
return
value
+
2;
}
console.log(addTwo(10));
//
12
JavaScript
31. FUNCTORS
//
A
not-‐quite-‐there
Functor
function
aFunctor(value,
fn)
{
return
fn(value);
}
console.log(aFunctor(10,
addOne));
//
11,
works
as
expected
console.log(aFunctor([1,
2,
3],
addOne));
//
'1,2,31'
is
returned,
which
is
not
what
we
want
JavaScript
32. FUNCTORS
function
betterFunctor(value,
fn)
{
if
(typeof
value
===
'number')
{
return
fn(value);
}
else
{
var
map
=
Array.prototype.map;
return
map.call(value,
fn);
}
}
JavaScript
34. FUNCTORS
//
JavaScript's
Array's
map
method
is
a
functor!
:-‐)
var
map
=
Array.prototype.map;
console.log(map.call([1,
2,
3],
addOne));
//
[2,
3,
4]
is
what
we
expected.
console.log([].map.call([1,
2,
3],
addOne));
//
This
works
too
JavaScript
35. FUNCTORS
addOne
::
Num
a
=>
a
-‐>
a
addOne
a
=
a
+
1
addTwo
::
Num
a
=>
a
-‐>
a
addTwo
a
=
a
+
2
result
::
Num
b
=>
[b]
-‐>
[b]
result
xs
=
map
addOne
xs
Haskell
36. COMPARISON
Functors vs Applicative Functors vs Monads
Functors Applicatives Monads
example
(+3) [2]
== [5]
Just(+3)
<*>
Just 2
== Just 5
Just 4
>>=
makeHalf
== Just 2
examples
functions
map, fmap, <*> <*>, <$>, liftA2 >>=, liftM
brings function
operator in
execute operationgeneralized
map
does both <*> & <$>
apply fn to
wrapped value
apply wrapped fn to
wrapped value
apply fn that returns
a wrapped value
to a wrapped value
38. MAYBE FUNCTORS
• Captures null check
• Value inside may or may not be there
• Maybe has two subclasses - ‘Just’ or ‘Nothing’ (Haskell)
• Also referred to as Option with subclasses ‘Some’ or
‘None’ (Scala)
• Also available in Swift, e.g. Optional, enum Either<NSError,
User>
39. MAYBE FUNCTORS
var
aList
=
[1,
2,
3];
function
compose(f,
g)
{
return
function
(x)
{
return
f(g(x));
};
}
function
addOne(value)
{
return
value
+
1;
}
function
addTwo(value)
{
return
value
+
2;
}
JavaScript
41. MAYBE FUNCTORS
console.log(mayBe(undefined,
compose(addOne,
addTwo)));
//
returns
expected
result
undefined
console.log(mayBe(mayBe(undefined,
addTwo),
addOne));
//
returns
expected
result
undefined
console.log(mayBe(1,
compose(addOne,
addTwo)));
//
returns
expected
result
4
console.log(mayBe(mayBe(1,
addTwo),
addOne));
//
returns
expected
result
4
JavaScript
42. MAYBE FUNCTORS
addOne
::
Num
a
=>
a
-‐>
a
addOne
a
=
a
+
1
addTwo
::
Num
a
=>
a
-‐>
a
addTwo
a
=
a
+
2
composedFn
::
Integer
-‐>
Integer
composedFn
=
addOne
.
addTwo
res
::
[Integer]
res
=
map
composedFn
aList
Haskell
43. MAYBE FUNCTORS
main
::
IO
()
main
=
do
print
res
let
res2
=
fmap
composedFn
Nothing
print
res2
let
res3
=
fmap
composedFn
(Just
1)
print
res3
Haskell
49. APPLICATIVES
//
Usage
var
four
=
some(4);
var
six
=
some(6);
console.log(four.toString());
console.log(six.toString());
function
add(a,
b)
{
return
a
+
b;
}
var
result
=
functor.applyFunctor(functor.map(curry(add,
2),
four),
six);
console.log(result.toString());
//
some(10)
JavaScript
55. MONAD
“A monad is just a monoid in the category of
endofunctors, what's the problem?”
- James Iry (Brief, Incomplete and Mostly Wrong
History of Programming Languages)
59. MONAD
So what’s the big deal?
When data is mutable, parallel thread execution of your code on
multiple CPUs leads to race conditions
Can’t do parallel thread execution on your multi-core machine
idle resource
If we insist on parallel thread execution without locks
unpredictable results, i.e. errors
62. MONAD
f (x)
x = g(x)
x = h(x)
x = i(x)
return x
x = 3
x = 5
x = 10
x = -3
x = -3
63. f (x)
x = g(x)
x = h(x)
x = i(x)
return x
f (x)
x = g(x)
x = h(x)
x = i(x)
return x
MONAD
x = 3
x = 5
x = 17
x = 4 x = -9
x = 7
x = 12
CPU #1 CPU #2
x = 4 x = -9
Shared
Memory, x
64. MONAD
As you can see, the same function results in indeterminate
results depending on the whims of the parallel OS-
controlled POSIX threads (“pthreads”)
We expect that when we write the code (“computation
logic”) for f(x), when given x = 3, f(x) will always result in
-3 but because x is mutable in traditional languages, this is
not the case if we try to make our program run on
multiple CPUs.
65. MONAD
Imperative languages that we are familiar with solve
this problem with
• locks (semaphores) on POSIX threads; or
• green threads (concurrent threads) and
asynchronous I/O
66. MONAD
Haskell has everything + more performant approaches:
• sparks (super-duper lightweight green threads)
• green threads
• semaphores
• immutable variables by default,“side effects” (mutability)
achieved via monads
68. MONAD
Haskell
f :: Num a => a -> a
f x = let
x1 = x + 2
x2 = x1 + 5
x3 = x2 - 13
in x3
f :: Num a => a -> a
f x = let
x = x + 2
x = x + 5
x = x - 13
in x
error:
variables in haskell
are immutable values!
no side effects!
69. MONAD
So, now, we know the Why?
avoid mutable state while chaining a group of
functions together (or executing a sequence of logic)
So, how can a monad achieve the magic illustrated in
the previous 2 slides?
72. MONAD
//
lift:
takes
in
a
function
that
returns
a
normal
value
and
changes
it
in
a
monad
Maybe.lift
=
function(func)
{
return
function(value)
{
return
new
Maybe(func(value));
};
};
//
Usage
var
addOne
=
function(value)
{
return
value
+
1;
};
//
we
can
use
this
with
bind
var
maybeAddOne
=
Maybe.lift(addOne);
JavaScript
73. MONAD
//
lift2
use
closures
to
get
values
from
the
two
monads
//
before
running
it
through
function,
handling
the
undefined
cases
Maybe.lift2
=
function(func)
{
return
function(M1,
M2)
{
return
new
Maybe(M1.bind(function(value1){
return
M2.bind(function(value2)
{
return
func(value1,
value2);
});
}));
};
};
JavaScript
74. MONAD
var
add
=
function(a,
b)
{return
a
+
b;};
var
m1
=
new
Maybe(1);
var
m2
=
new
Maybe(2);
var
m3
=
new
Maybe(undefined);
var
liftM2Add
=
Maybe.lift2(add);
liftM2Add(m1,
m2).ret();
//3
liftM2Add(m3,
m2).ret();
//undefined
liftM2Add(m1,
m3).ret();
//undefined
JavaScript
75. MONAD
a
::
Maybe
Integer
a
=
Just
1
f
::
Integer
-‐>
Maybe
Integer
f
=
x
-‐>
Just
(x
+
1)
main
::
IO
()
main
=
do
let
ans
=
a
>>=
f
print
ans
{-‐
we
expect
to
get
Just
2
-‐}
Haskell
76. MONAD
• type container
• return
• bind
• We pass in function(s) to operate values inside it
• and get a returned value