/
ListAlg.test.mts
66 lines (55 loc) · 1.89 KB
/
ListAlg.test.mts
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
import { Algebra } from "../Algebra.mjs"
import { Merge } from "../Merge.mjs"
describe('ListAlg', () => {
// TODO: need to find a convenient way to represent Higher Kinded Types
// See TypeScript issue <https://github.com/microsoft/TypeScript/issues/1213>
interface ListAlg<T> extends Algebra {
Nil(): T
Cons(head: any, tail: T): T
}
abstract class ListData<T> { }
class Nil<T> extends ListData<T> { }
class Cons<T> extends ListData<T> {
constructor(readonly head: any, readonly tail: ListData<T>) { super() }
}
class ListFactory<T> implements ListAlg<ListData<T>> {
Nil(): Nil<T> {
return new Nil()
}
Cons(head: any, tail: ListData<T>): Cons<T> {
return new Cons(head, tail)
}
}
interface ILengthable { length(): number }
class Lengthable implements ListAlg<ILengthable> {
Nil(): ILengthable {
return { length() { return 0 } }
}
Cons(head: any, tail: ILengthable) {
return { length() { return 1 + tail.length() } }
}
}
interface IConcatable { concat(other: IConcatable): this }
class Concatable implements ListAlg<IConcatable> {
Nil(): IConcatable {
return {
concat(other) { return other }
}
}
Cons(head: any, tail: IConcatable): IConcatable {
const family = this
return {
concat(other) { return family.Cons(head, tail.concat(other)) }
}
}
}
test('Merge', () => {
class List extends Merge(ListFactory, Lengthable, Concatable) { }
const { Nil, Cons } = new List()
const xs = Cons(1, Cons(2, Cons(3, Nil())))
expect(xs.length()).toBe(3)
const ys = Cons(4, Cons(5, Cons(6, Nil())))
const zs = xs.concat(ys)
expect(zs.length()).toBe(6)
})
})