A short introduction to monad which doesn't involve math.
Code samples are written in C# language for demonstration purpose.
The monad concept is generic in any programming language.
References:
https://weblogs.asp.net/dixin/Tags/Category%20Theory
https://davesquared.net/categories/functional-programming/
https://fsharpforfunandprofit.com/posts/elevated-world/
https://www.ahnfelt.net/monads-forget-about-bind/
https://mikhail.io/tags/functional-programming/
http://adit.io/posts/2013-06-10-three-useful-monads.html
http://learnyouahaskell.com/a-fistful-of-monads
https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/
https://fsharpforfunandprofit.com/posts/elevated-world/#series-toc
https://kubuszok.com/2018/different-ways-to-understand-a-monad/
https://mikehadlow.blogspot.com/2011/01/monads-in-c1-introduction.html
6. NULLABLE<T> - HOLDS EITHER T OR NULL
Nullable<int> a = new Nullable<int>(1);
Nullable<int> b = new Nullable<int>(2);
Nullable<int> c = a + b;
Nullable<int> c =
a.HasValue && b.HasValue
? new Nullable<int>(a.Value + b.Value)
: new Nullable<int>(null);
7. IENUMERABLE<T> - SEQUENCE
IEnumerable<int> a = new[]{1};
IEnumerable<int> b = new[]{2};
IEnumerable<int> c = from x in a from y in b select x + y;
IEnumerable<int> c = a.SelectMany(x => b, (x, y) => x+y);
9. SPECIALIZED SOLUTION
● Nullable - C♯ 2: arithmetic, “??” operator
●IEnumerable/IObservable - C♯ 3: LINQ
●Task - C♯ 5: async / await
●My cool class - No language support :-/
10. WHAT IF WE HAD SAME SYNTAX
do
a <- f()
b <- g()
return a + b
11. IS IT POSSIBLE ?
var a = f();
var b = g();
return from x in a from y in b select x + y;
12. ● With following abilities:
○ Can be created (constructor or special method)
○ Make projection with internal value producing a new
Monad<U> (Map).
○ Can flatten Monad of Monad into Monad (Join)
● Immutable, without side effects.
● Monad contract doesn’t require to provide the held value !
MONAD IS A GENERIC TYPE M OVER T
15. WHAT IS THE SIMPLEST MONAD ?
● ✅ Generic type: class Identity<T>
● ✅ Can be created: new Identity<T>(value)
● ✅ Projection: Map(func, value) ≡ func(value)
● ✅ Flattening:
Join(Identity<Identity<T>> value) ≡ Identity<T>(value)
16. CAN IENUMERABLE<T> BE A MONAD ?
● ✅ Generic type
● ✅ Can be created: new[]{...}, yield return value
● ✅ Projection: myIEnumerable.Select(value ⇒ value.ToString())
[1, 2…] ⇒ [“1”, “2”…]
● ✅ Flattening:
myIEnumerableOfIEnumerables.SelectMany(value ⇒ value)
[[1, 2], [3, 4]…] ⇒ [1, 2, 3, 4…]
18. COMBINING ALL TOGETHER
● Join: (mm: Monad<Monad<T>>) ⇒ Monad<T>
● Map: (monad: Monad<T>, projFunc: T ⇒ U) ⇒ Monad<U>
● Bind: (monad: Monad<T>, bindFunc: T ⇒ Monad<U>) ⇒ Monad<U>
● Bind ≡ monad.Map(value ⇒ bindFunc(value)).Join()
● Join ≡ monadOfMonad.Bind(monad ⇒ monad)
● Map ≡ monad.Bind(value ⇒ new Monad(projFunc(value)))
19. ● return ⇔ Monad constructor
● q >=> w ≡ m ⇒ q(m.Bind(w))
IS THAT ALL ?
●3 monad laws
○ Left identity: return >=> g ≡ g
○ Right identity: f >=> return ≡ f
○ Associativity: (f >=> g) >=> h ≡ f >=> (g >=> h)
20. LEFT IDENTITY
Monad constructor bound with a function is just a function
called with the value stored in the monad
// Given
T value
Func<T, Monad<U>> valueToMonad // T ⇒Monad<U>
// Then
new Monad<T>(value).Bind(valueToMonad) ≡ valueToMonad(value)
22. // Given
Monad<T> monad
// Then
monad.Bind(x ⇒ new Monad<T>(x)) ≡ monad
RIGHT IDENTITY
Binding monadic value to the same monad type doesn’t
change the original value.
24. // Given
Monad<T> m
Func<T, Monad<U>> f // T ⇒Monad<U>
Func<U, Monad<V>> g // U ⇒Monad<V>
// Then
m.Bind(f).Bind(g) ≡ m.Bind(a ⇒ f(a).Bind(g))
ASSOCIATIVITY
The order in which Bind operations are
composed does not matter
30. MORE MONADS
● Maybe (Nullable, Optional)
● Logging: Log while calculating value
● State: Preserve state between functions call
● Ordering: See next slide
● Parser: See next slide
31. COMPUTATION ORDER - PROBLEM
Task<int> x = Task.Run(() => { Write(1); return 1; });
Task<int> y = Task.Run(() => { Write(2); return 2; });
Task<int> z = Task.Run(() => { Write(3); return 3; });
return x + y + z;
33. MONAD DEFINES ORDER
int x = f();
int y = g();
int z = h();
return x + y + z;
f().Bind(x =>
g().Bind(y =>
h().Bind(z =>
x + y + z)));
Monad<int> f();
Monad<int> g();
Monad<int> h();
34. DOES LINQ USE IENUMERABLE AS A MONAD ?
What you see
from x in a
from y in b
from z in c
select x + y + z;
What is the truth
a.SelectMany(
x => b,
(x, y) => new {x, y})
.SelectMany(
@t => c,
(@t, z) => @t.x + @t.y + z);
What you think
a.SelectMany(x =>
b.SelectMany(y =>
c.SelectMany(z =>
yield return x + y + z)));
35. MONAIDIC PARSER SAMPLE
static readonly Parser<Expression> Function =
from name in Parse.Letter.AtLeastOnce().Text()
from lparen in Parse.Char('(')
from expr in Parse.Ref(() => Expr).DelimitedBy(Parse.Char(',').Token())
from rparen in Parse.Char(')')
select CallFunction(name, expr.ToArray());
static readonly Parser<Expression> Constant =
Parse.Decimal.Select(x => Expression.Constant(double.Parse(x)))
.Named("number");
static readonly Parser<Expression> Factor =
(from lparen in Parse.Char('(')
from expr in Parse.Ref(() => Expr)
from rparen in Parse.Char(')')
select expr).Named("expression").XOr(Constant).XOr(Function);
36. SUMMARY
● Monad concept is simple
● Monads hold value but doesn’t give it to you
● Allows writing generic code
● Easier to test, easier to maintain
● Language support is an advantage