Skip to content

Commit 697d8eb

Browse files
committed
monad & monoid
1 parent 955c386 commit 697d8eb

File tree

9 files changed

+358
-0
lines changed

9 files changed

+358
-0
lines changed

monad/0_the_pattern.linq

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<Query Kind="Program" />
2+
3+
// smart function (glue+container)
4+
// - smart is abstract - handle: io/null/state/try/flatten
5+
// - glue is also a container/wrapper/package
6+
// how do we unpack the value from the container to pass to the next function
7+
8+
// async/await/Task is a monad => await to unpack the value
9+
// .SelectMany is a monad (flatten) => List<T>.SelectMany(Func<T, List<T>> f).SelectMany(Func<T, List<T>> f)
10+
// wrapping try catch func => unpack the value after exception happens
11+
// logging/state accumilation func => log application is seperate from the function
12+
13+
void Main()
14+
{
15+
string foo = null;
16+
//var foo = "Hello World";
17+
18+
if (foo != null)
19+
{
20+
foo = foo.Trim();
21+
}
22+
if (foo != null)
23+
{
24+
foo = foo.Substring(0, 5);
25+
}
26+
int n = foo == null ? 0 : foo.Length;
27+
28+
n = n * n;
29+
30+
n.Dump();
31+
32+
33+
int nn = foo?.Trim()?.Substring(0, 5)?.Length ?? 0;
34+
nn = nn * nn;
35+
36+
nn.Dump();
37+
}

monad/1_first_step.linq

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<Query Kind="Program" />
2+
3+
void Main()
4+
{
5+
string foo = null;
6+
//var foo = "Hello World";
7+
8+
foo = ifNotNull(foo, str => str.Trim());
9+
foo = ifNotNull(foo, str => str.Substring(0, 5));
10+
int n = ifNotNull(foo, foo => foo.Length);
11+
12+
n = n * n;
13+
14+
n.Dump();
15+
}
16+
17+
public R ifNotNull<T, R>(T v, Func<T, R> fn) => v == null ? default : fn(v);
18+
19+
20+
// composition/chaining: ifNotNull(a, f)
21+
// .then((x) => ifNotNull(x, g))
22+
// .then((x) => ifNotNull(x, j))
23+
// ^^^ this is ugly
24+
25+
26+
// we want:
27+
// f(a).then(g).then(j) => ...
28+
// or a.then(f).then(g).then(j) => ...
29+
30+
// in haskell 'then' can be an operator - it's called 'bind' and looks like >>=
31+
// a >>= f >>= g >>= j => ...

monad/2_second_step.linq

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<Query Kind="Program" />
2+
3+
void Main()
4+
{
5+
string foo = null;
6+
//string foo = "Hello World";
7+
8+
foo.bind(x => x.Trim().nil())
9+
.bind(x => x.Substring(0, 5).nil())
10+
.bind(x => x.Length.nil())
11+
.bind(x => (x * x).nil())
12+
.Value
13+
.Dump();
14+
}
15+
16+
public struct nil<T>
17+
{
18+
public nil(T value) => Value = value;
19+
20+
public T Value { get; set; }
21+
public bool HasValue => Value != null;
22+
}
23+
24+
public static class ext
25+
{
26+
public static nil<T> nil<T>(this T value) => new nil<T>(value);
27+
28+
// a.then(f).then(g).then(j)
29+
public static nil<C> bind<B, C>(this B b, Func<B, nil<C>> fn)
30+
=> b.nil().bind(fn);
31+
32+
// f(a).then(g).then(j)
33+
public static nil<C> bind<B, C>(this nil<B> b, Func<B, nil<C>> fn)
34+
=> b.HasValue ? fn(b.Value) : new nil<C>();
35+
}
36+
37+
// f1 : a -> container<b>
38+
// f2 : b -> container<c>
39+
40+
// f3 : a -> container<c>
41+
42+
// f3 = a -> f1(a).bind(f2) === a -> a.bind(f1).bind(f2)
43+
// f3 = a -> a >> smart transfer >> f1 >> smart transfer >> f2
44+
45+
// bind in haskell >>=
46+
// >>= : smart transfer
47+
// f3 = a >>= f1 >>= f2
48+
// (left) >>= (right) = right(smart_transfer(left))
49+
50+

monad/3_nothing.linq

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<Query Kind="Program" />
2+
3+
void Main()
4+
{
5+
Func<string, nil<int>> doTheThing = str => str.bind(trim).bind(sub(0, 5)).bind(len).bind(sqr);
6+
7+
string foo = "Hello World";
8+
9+
apiCall().bind(doTheThing).Value.Dump();
10+
}
11+
12+
public nil<string> apiCall() {
13+
// doo fancy IO
14+
return nil<string>.Nothing;
15+
}
16+
17+
Func<string, nil<string>> trim = x => x.Trim().ret();
18+
Func<int, int, Func<string, nil<string>>> sub = (int s, int l) => x => x.Substring(s, l).ret();
19+
Func<string, nil<int>> len = x => x.Length.ret();
20+
Func<int, nil<int>> sqr = x => (x * x).ret();
21+
22+
public class nil<T>
23+
{
24+
public nil(T value) => Value = value;
25+
protected nil(){}
26+
27+
public T Value { get; set; }
28+
public virtual bool HasValue => true;
29+
public static nil<T> Nothing = new nothing<T>();
30+
}
31+
32+
public class nothing<T> : nil<T>
33+
{
34+
public override bool HasValue => false;
35+
}
36+
`
37+
public static class ext
38+
{
39+
public static nil<T> ret<T>(this T value) => new nil<T>(value);
40+
41+
// a.then(f).then(g).then(j)
42+
public static nil<C> bind<B, C>(this B b, Func<B, nil<C>> fn)
43+
=> b.ret().bind(fn);
44+
45+
// f(a).then(g).then(j)
46+
public static nil<C> bind<B, C>(this nil<B> b, Func<B, nil<C>> fn)
47+
=> b.HasValue ? fn(b.Value) : nil<C>.Nothing;
48+
}

monad/rules.linq

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<Query Kind="Program" />
2+
3+
void Main()
4+
{
5+
// left identity: monad construction doesn't change function behaviour
6+
("Hello".bind(trim).Value == trim("Hello").Value).Dump();
7+
8+
// right identity: re-wrapping doesn't change the value
9+
var helloMon = "Hello".ret();
10+
(helloMon.bind(hello => new nil<string>(hello)).Value == helloMon.Value).Dump();
11+
12+
// associativity
13+
("Hello".bind(trim).bind(sub(0, 5)).Value ==
14+
"Hello".bind(v => trim(v).bind(sub(0, 5))).Value).Dump();
15+
}
16+
17+
Func<string, nil<string>> trim = x => x.Trim().ret();
18+
Func<int, int, Func<string, nil<string>>> sub =
19+
(int s, int l) => x => x.Substring(s, l).ret();
20+
Func<string, nil<int>> len = x => x.Length.ret();
21+
Func<int, nil<int>> sqr = x => (x * x).ret();
22+
23+
public class nil<T>
24+
{
25+
public nil(T value) => Value = value;
26+
protected nil() { }
27+
28+
public T Value { get; set; }
29+
public virtual bool HasValue => true;
30+
public static nil<T> Nothing = new nothing<T>();
31+
}
32+
33+
public class nothing<T> : nil<T>
34+
{
35+
public override bool HasValue => false;
36+
}
37+
38+
public static class ext
39+
{
40+
public static nil<T> ret<T>(this T value) => new nil<T>(value);
41+
42+
// a.then(f).then(g).then(j)
43+
public static nil<C> bind<B, C>(this B b, Func<B, nil<C>> fn)
44+
=> b.ret().bind(fn);
45+
46+
// f(a).then(g).then(j)
47+
public static nil<C> bind<B, C>(this nil<B> b, Func<B, nil<C>> fn)
48+
=> b.HasValue ? fn(b.Value) : nil<C>.Nothing;
49+
}

monoid/associativity.linq

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<Query Kind="Program" />
2+
3+
void Main()
4+
{
5+
// rules for RULE1
6+
// associativity - doesn't matter how you group
7+
var add_sub_1 = add5 + (sub3 + add10);
8+
var add_sub_2 = (add5 + sub3) + add10;
9+
add_sub_1.run(5).Dump();
10+
add_sub_2.run(5).Dump();
11+
12+
var mul_div_1 = mul3 + (div7 + mul5) + (sub3 + mul3);
13+
var mul_div_2 = mul3 + div7 + (mul5 + sub3) + mul3;
14+
mul_div_1.run(77).Dump();
15+
mul_div_2.run(77).Dump();
16+
17+
// special member (abstract 0)
18+
((div7 + identity).run(5) == div7.run(5)).Dump();
19+
((identity + div7).run(5) == div7.run(5)).Dump();
20+
}
21+
22+
public static int Add10(int a) => a + 10;
23+
24+
// collection of things
25+
static Function<int> identity = new(a => a);
26+
static Function<int> add5 = new(a => a + 5);
27+
static Function<int> add10 = new(Add10);
28+
static Function<int> sub3 = new(a => a - 3);
29+
static Function<int> mul3 = new(a => a * 3);
30+
31+
32+
static Function<int> mul5 = new(a => a * 5);
33+
static Function<int> div7 = new(a => a / 7);
34+
35+
public struct Function<T>
36+
{
37+
public Func<T, T> run;
38+
39+
public Function(Func<T, T> fn) => run = fn;
40+
41+
// RULE1 : rule for combining the things
42+
public static Function<T> operator +(
43+
Function<T> left,
44+
Function<T> right
45+
)
46+
=> new(x => left.run(right.run(x)));
47+
}

monoid/how_to_implement.linq

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<Query Kind="Program" />
2+
3+
void Main()
4+
{
5+
// a: _ -> int
6+
// 5: _ -> int
7+
// 10: _ -> int
8+
Add15 add15 = (int a) => a + 5 + 10;
9+
add15(5).Dump();
10+
11+
add15 = (int a) => Add5(a) + 10;
12+
add15(5).Dump();
13+
14+
add15 = (int a) => Add10(Add5(a));
15+
add15(5).Dump();
16+
}
17+
18+
public delegate int Add15(int a);
19+
20+
// add5: int -> int
21+
public int Add5(int a){
22+
return a + 5;
23+
}
24+
25+
// add10: int -> int
26+
public int Add10(int a)
27+
{
28+
return a + 10;
29+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<Query Kind="Program" />
2+
3+
void Main()
4+
{
5+
var add15 = add5 + add10;
6+
7+
add15.run(5).Dump();
8+
}
9+
10+
public static int Add10(int a) => a + 10;
11+
12+
static Function<int> add5 = new(a => a + 5);
13+
static Function<int> add10 = new(Add10);
14+
15+
public struct Function<T>
16+
{
17+
public Func<T, T> run;
18+
19+
public Function(Func<T, T> fn) => run = fn;
20+
21+
// then is a monoid
22+
public Function<T> Then(Function<T> next)
23+
{
24+
var runCopy = run;
25+
return new(x => runCopy(next.run(x)));
26+
}
27+
28+
// + is a monoid
29+
public static Function<T> operator +(
30+
Function<T> left,
31+
Function<T> right
32+
)
33+
=> new(x => left.run(right.run(x)));
34+
}
35+
36+
public static class FunctionExt{
37+
38+
// then is a monoid
39+
public static Function<T> Then<T>(this Function<T> @this, Function<T> next) {
40+
return new(x => @this.run(next.run(x)));
41+
}
42+
}

monoid/what_is_a_monoid.linq

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Query Kind="Program" />
2+
3+
void Main()
4+
{
5+
int a = 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1; // + is monoid
6+
bool b = true || true || true || false || true; // || is monoid
7+
bool c = true && true && false; // && is monoid
8+
}
9+
10+
// add5: int -> int
11+
public int Add5(int a) => a + 5;
12+
13+
// add10: int -> int
14+
public int Add10(int a) => a + 10;
15+
16+
// add15: int -> int
17+
// ???
18+
// composed functions/(variables) are a monoid
19+
20+
21+
22+
23+
// Task + Task + Task
24+
// CloudFunction + CloudFunction + CloudFunction
25+
// VM + VM + VM

0 commit comments

Comments
 (0)