-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathio.ts
More file actions
78 lines (57 loc) · 1.57 KB
/
io.ts
File metadata and controls
78 lines (57 loc) · 1.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
'use strict';
export {};
class IO<T> {
private readonly effect: () => T;
private constructor(effect: () => T) {
this.effect = effect;
}
static of<T>(effect: () => T): IO<T> {
return new IO(effect);
}
map<U>(fn: (value: T) => U): IO<U> {
return new IO(() => fn(this.effect()));
}
chain<U>(fn: (value: T) => IO<U>): IO<U> {
return new IO(() => fn(this.effect()).run());
}
run(): T {
return this.effect();
}
}
class Monad<T> {
private readonly value: T;
private constructor(value: T) {
this.value = value;
}
static of<T>(value: T): Monad<T> {
return new Monad(value);
}
map<U>(fn: (value: T) => U): Monad<U> {
const v = structuredClone(this.value) as T;
return Monad.of(fn(v));
}
chain<R>(fn: (value: T) => R): R {
const v = structuredClone(this.value) as T;
return fn(v);
}
ap<A, B>(this: Monad<(arg: A) => B>, container: Monad<A>): Monad<B> {
return container.map(this.value);
}
}
type Point = { x: number; y: number };
const move =
(d: Point) =>
(p: Point): Point => ({ x: p.x + d.x, y: p.y + d.y });
const clone = ({ x, y }: Point): Point => ({ x, y });
const toString = ({ x, y }: Point): IO<string> => IO.of(() => `(${x}, ${y})`);
// Usage
const input = IO.of(() => Monad.of<Point>({ x: 10, y: 20 }));
input
.chain((monad) => monad.chain(toString))
.map(console.log)
.run();
const c0 = input.chain((m) => IO.of(() => m.map(clone)));
const c1 = c0.chain((m) => IO.of(() => Monad.of(move({ x: -5, y: 10 })).ap(m)));
c1.chain((m) => m.chain(toString))
.map(console.log)
.run();