Monad
CATEGORY THEORY
A monad is just a monoid in the category of
endofunctors, what's the problem?
SIMPLE ENGLISH
A monad is just a design pattern in
functional programming
BUT I DON’T USE FUNCTIONAL
PROGRAMMING
Function without side-effects
Functional composition
No shared state
Immutable objects
FUNCTIONS COMPOSITION
14
Calculate
length
"QQOME
QQSTRING"
Replace
"S" with
"QQ"
"SOME
STRING"
ToUpper
"some
string"
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);
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);
TASK<T> - ASYNCHRONOUS
COMPUTATION
Task<int> a = Task.Run(() ⇒ 1);
Task<int> b = Task.Run(() ⇒ 2);
Task<int> c =
a.ContinueWith(x ⇒
b.ContinueWith(y ⇒
x.Result + y.Result)).Unwrap();
ContinueWith(Task<TResult> ⇒ TNewResult): Task<TNewResult>
Unwrap(Task<Task<TResult>>): Task<TResult>
SPECIALIZED SOLUTION
● Nullable - C♯ 2: arithmetic, “??” operator
●IEnumerable/IObservable - C♯ 3: LINQ
●Task - C♯ 5: async / await
●My cool class - No language support :-/
WHAT IF WE HAD SAME SYNTAX
do
a <- f()
b <- g()
return a + b
IS IT POSSIBLE ?
var a = f();
var b = g();
return from x in a from y in b select x + y;
● 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
MONAD FUNCTIONS: FLATTENING
Join: (monadOfMonad: Monad<Monad<T>>) ⇒ Monad<T>
MONAD FUNCTIONS: PROJECTION
Map: (monad: Monad<T>, projFunc: T ⇒ U) ⇒ Monad<U>
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)
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…]
MONAD FUNCTIONS: BINDING
Bind: (monad: Monad<T>, bindFunc: T ⇒ Monad<U>) ⇒ Monad<U>
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)))
● 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)
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)
LEFT IDENTITY
Monad constructor bound with function is just a function
called with the value stored in the monad
// 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.
RIGHT IDENTITY
Binding monadic value to the same monad type doesn’t
change the original value.
// 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
ASSOCIATIVITY
The order in which Bind operations are
composed does not matter
public static IEnumerable<TResult> SelectMany<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, IEnumerable<TResult>> selector)
Flatten = SelectMany(x ⇒ x)
public static IEnumerable<TResult> Select<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
public static Monad<TResult> Bind<TSource, TResult>(
this Monad<TSource> source,
Func<TSource, Monad<TResult>> selector)
Join = Bind(x ⇒ x)
public static Monad<TResult> Map<TSource, TResult>(
this Monad<TSource> source,
Func<TSource, TResult> selector)
SO, IS IENUMERABLE<T> A MONAD ?
new Monad<T>(value).Bind(valueToMonad) ≡ valueToMonad(value)
[1].SelectMany(value ⇒ [value.ToString()]) ≡
[1].Select(value => [value.ToString()]).Flatten() ≡ [[“1”]].Flatten() ≡ [“1”]
(value ⇒ [value.ToString()])(1) ≡ [“1”]
IENUMERABLE<T> - LEFT IDENTITY
monad.Bind(x ⇒ new Monad<T>(x)) ≡ monad
[1, 2…].SelectMany(value ⇒ [value]) ≡ [1, 2…]
[1, 2…].Select(value => [value]).Flatten() ≡
[[1], [2]..].Flatten() ≡ [1, 2…]
IENUMERABLE<T> - RIGHT IDENTITY
m.Bind(f).Bind(g) ≡ m.Bind(a ⇒ f(a).Bind(g))
[1, 2…].SelectMany(value ⇒ [value + 1])
.SelectMany(value ⇒ [value.ToString()])
[1, 2…].SelectMany(
value => ([value + 1]) .SelectMany(value ⇒ [value.ToString()])
) ≡ [“2”, “3”…]
IENUMERABLE<T> - ASSOCIATIVITY
[2, 3…]
[2] [“2”], [3]… , [“3”]…
[“2”, “3”…]
MORE MONADS
● Maybe (Nullable, Optional)
● Logging: Log while calculating value
● State: Preserve state between functions call
● Ordering: See next slide
● Parser: See next slide
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;
COMPUTATION ORDER - SOLUTION
Task.Run(() => { Write(1); return 1; }).Bind(x =>
Task.Run(() => { Write(2); return 2; }).Bind(y =>
Task.Run(() => { Write(3); return 3; }).Bind(z =>
x + y + z)))
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();
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)));
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);
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
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
Monad

Monad

  • 1.
  • 2.
    CATEGORY THEORY A monadis just a monoid in the category of endofunctors, what's the problem?
  • 3.
    SIMPLE ENGLISH A monadis just a design pattern in functional programming
  • 4.
    BUT I DON’TUSE FUNCTIONAL PROGRAMMING Function without side-effects Functional composition No shared state Immutable objects
  • 5.
  • 6.
    NULLABLE<T> - HOLDSEITHER 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);
  • 8.
    TASK<T> - ASYNCHRONOUS COMPUTATION Task<int>a = Task.Run(() ⇒ 1); Task<int> b = Task.Run(() ⇒ 2); Task<int> c = a.ContinueWith(x ⇒ b.ContinueWith(y ⇒ x.Result + y.Result)).Unwrap(); ContinueWith(Task<TResult> ⇒ TNewResult): Task<TNewResult> Unwrap(Task<Task<TResult>>): Task<TResult>
  • 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 WEHAD 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 followingabilities: ○ 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
  • 13.
    MONAD FUNCTIONS: FLATTENING Join:(monadOfMonad: Monad<Monad<T>>) ⇒ Monad<T>
  • 14.
    MONAD FUNCTIONS: PROJECTION Map:(monad: Monad<T>, projFunc: T ⇒ U) ⇒ Monad<U>
  • 15.
    WHAT IS THESIMPLEST 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> BEA 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…]
  • 17.
    MONAD FUNCTIONS: BINDING Bind:(monad: Monad<T>, bindFunc: T ⇒ Monad<U>) ⇒ Monad<U>
  • 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 constructorbound 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)
  • 21.
    LEFT IDENTITY Monad constructorbound with function is just a function called with the value stored in the monad
  • 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.
  • 23.
    RIGHT IDENTITY Binding monadicvalue 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
  • 25.
    ASSOCIATIVITY The order inwhich Bind operations are composed does not matter
  • 26.
    public static IEnumerable<TResult>SelectMany<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector) Flatten = SelectMany(x ⇒ x) public static IEnumerable<TResult> Select<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, TResult> selector) public static Monad<TResult> Bind<TSource, TResult>( this Monad<TSource> source, Func<TSource, Monad<TResult>> selector) Join = Bind(x ⇒ x) public static Monad<TResult> Map<TSource, TResult>( this Monad<TSource> source, Func<TSource, TResult> selector) SO, IS IENUMERABLE<T> A MONAD ?
  • 27.
    new Monad<T>(value).Bind(valueToMonad) ≡valueToMonad(value) [1].SelectMany(value ⇒ [value.ToString()]) ≡ [1].Select(value => [value.ToString()]).Flatten() ≡ [[“1”]].Flatten() ≡ [“1”] (value ⇒ [value.ToString()])(1) ≡ [“1”] IENUMERABLE<T> - LEFT IDENTITY
  • 28.
    monad.Bind(x ⇒ newMonad<T>(x)) ≡ monad [1, 2…].SelectMany(value ⇒ [value]) ≡ [1, 2…] [1, 2…].Select(value => [value]).Flatten() ≡ [[1], [2]..].Flatten() ≡ [1, 2…] IENUMERABLE<T> - RIGHT IDENTITY
  • 29.
    m.Bind(f).Bind(g) ≡ m.Bind(a⇒ f(a).Bind(g)) [1, 2…].SelectMany(value ⇒ [value + 1]) .SelectMany(value ⇒ [value.ToString()]) [1, 2…].SelectMany( value => ([value + 1]) .SelectMany(value ⇒ [value.ToString()]) ) ≡ [“2”, “3”…] IENUMERABLE<T> - ASSOCIATIVITY [2, 3…] [2] [“2”], [3]… , [“3”]… [“2”, “3”…]
  • 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;
  • 32.
    COMPUTATION ORDER -SOLUTION Task.Run(() => { Write(1); return 1; }).Bind(x => Task.Run(() => { Write(2); return 2; }).Bind(y => Task.Run(() => { Write(3); return 3; }).Bind(z => x + y + z)))
  • 33.
    MONAD DEFINES ORDER intx = 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 USEIENUMERABLE 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 staticreadonly 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 conceptis 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
  • 37.