functionalscript 0.0.216 → 0.0.220
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/btree/index.js +4 -4
- package/btree/test.js +1 -1
- package/function/index.js +6 -2
- package/json/index.js +25 -25
- package/json/test.js +2 -1
- package/map/index.js +5 -3
- package/module-manager/index.js +2 -6
- package/object/index.js +0 -5
- package/package.json +2 -2
- package/sequence/README.md +16 -1
- package/sequence/array/index.js +0 -19
- package/sequence/asyncIterable/index.js +1 -1
- package/sequence/index.js +203 -236
- package/sequence/iterable/index.js +1 -1
- package/sequence/iterable/test.js +2 -2
- package/sequence/operator/index.js +1 -1
- package/sequence/test.js +124 -57
- package/test.js +3 -3
- package/sequence/operator/README.md +0 -41
package/README.md
CHANGED
|
@@ -13,6 +13,12 @@ Create a new FunctionalScript repository on GitHub [here](https://github.com/fun
|
|
|
13
13
|
One of the main challenges is how to make a pure functional language when ES6 TCO is not supported by Chrome and Firefox.
|
|
14
14
|
A workaround for this problem is to use `let` for renaming objects.
|
|
15
15
|
|
|
16
|
+
## Install FunctionalScript As A Library
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
npm install -S github:functionalscript/functionalscript
|
|
20
|
+
```
|
|
21
|
+
|
|
16
22
|
## JSON
|
|
17
23
|
|
|
18
24
|
```js
|
package/btree/index.js
CHANGED
|
@@ -309,20 +309,20 @@ const replaceVisitor = {
|
|
|
309
309
|
const values = node => () => {
|
|
310
310
|
const f = () => {
|
|
311
311
|
switch (node.length) {
|
|
312
|
-
case 1: case 2: { return
|
|
312
|
+
case 1: case 2: { return node }
|
|
313
313
|
case 3: {
|
|
314
314
|
return seq.concat(
|
|
315
315
|
values(node[0]),
|
|
316
|
-
|
|
316
|
+
[node[1]],
|
|
317
317
|
values(node[2])
|
|
318
318
|
)
|
|
319
319
|
}
|
|
320
320
|
default: {
|
|
321
321
|
return seq.concat(
|
|
322
322
|
values(node[0]),
|
|
323
|
-
|
|
323
|
+
[node[1]],
|
|
324
324
|
values(node[2]),
|
|
325
|
-
|
|
325
|
+
[node[3]],
|
|
326
326
|
values(node[4])
|
|
327
327
|
)
|
|
328
328
|
}
|
package/btree/test.js
CHANGED
package/function/index.js
CHANGED
|
@@ -4,8 +4,12 @@
|
|
|
4
4
|
* @typedef {(_: I) => O} Func
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Postfix Compose function.
|
|
9
|
+
*
|
|
10
|
+
* @type {<I, X>(g: Func<I, X>) => <O>(f: Func<X, O>) => Func<I, O>}
|
|
11
|
+
*/
|
|
12
|
+
const compose = g => f => x => f(g(x))
|
|
9
13
|
|
|
10
14
|
/** @type {<T>(value: T) => T} */
|
|
11
15
|
const identity = value => value
|
package/json/index.js
CHANGED
|
@@ -14,53 +14,50 @@ const { compose } = require('../function')
|
|
|
14
14
|
|
|
15
15
|
/** @typedef {Object|boolean|string|number|null|Array} Json */
|
|
16
16
|
|
|
17
|
-
/** @type {(value: Json) => (path:
|
|
17
|
+
/** @type {(value: Json) => (path: seq.Sequence<string>) => (src: Json|undefined) => Json} */
|
|
18
18
|
const addProperty = value => {
|
|
19
19
|
/** @type {(path: seq.Sequence<string>) => (src: Json|undefined) => Json} */
|
|
20
20
|
const f = path => src => {
|
|
21
21
|
const result = seq.next(path)
|
|
22
22
|
if (result === undefined) { return value }
|
|
23
23
|
const srcObject = (src === undefined || src === null || typeof src !== 'object' || src instanceof Array) ? {} : src
|
|
24
|
-
const
|
|
25
|
-
return { ...srcObject, [
|
|
26
|
-
}
|
|
27
|
-
return
|
|
24
|
+
const { first, tail } = result
|
|
25
|
+
return { ...srcObject, [first]: f(tail)(object.at(first)(srcObject)) }
|
|
26
|
+
}
|
|
27
|
+
return f
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/** @type {(_: string) => seq.Sequence<string>} */
|
|
31
|
-
const stringSerialize = input =>
|
|
31
|
+
const stringSerialize = input => [JSON.stringify(input)]
|
|
32
32
|
|
|
33
33
|
/** @type {(_: number) => seq.Sequence<string>} */
|
|
34
|
-
const numberSerialize = input =>
|
|
34
|
+
const numberSerialize = input => [JSON.stringify(input)]
|
|
35
35
|
|
|
36
|
-
const nullSerialize =
|
|
36
|
+
const nullSerialize = ['null']
|
|
37
37
|
|
|
38
|
-
const trueSerialize =
|
|
38
|
+
const trueSerialize = ['true']
|
|
39
39
|
|
|
40
|
-
const falseSerialize =
|
|
40
|
+
const falseSerialize = ['false']
|
|
41
41
|
|
|
42
42
|
/** @type {(_: boolean) => seq.Sequence<string>} */
|
|
43
43
|
const boolSerialize = value => value ? trueSerialize : falseSerialize
|
|
44
44
|
|
|
45
|
-
const colon =
|
|
46
|
-
const comma =
|
|
45
|
+
const colon = [':']
|
|
46
|
+
const comma = [',']
|
|
47
47
|
|
|
48
48
|
/** @type {op.Scan<seq.Sequence<string>, seq.Sequence<string>>} */
|
|
49
49
|
const commaValue = a => [seq.concat(comma, a), commaValue]
|
|
50
50
|
|
|
51
|
-
/** @type {
|
|
52
|
-
const
|
|
51
|
+
/** @type {seq.FoldOperator<seq.Sequence<string>>} */
|
|
52
|
+
const joinOp = a => b => seq.concat(a, comma, b)
|
|
53
53
|
|
|
54
|
-
/** @type {seq.
|
|
55
|
-
const join =
|
|
56
|
-
const _0 = seq.scan(joinScan)(input)
|
|
57
|
-
return seq.flat(_0)
|
|
58
|
-
}
|
|
54
|
+
/** @type {(input: seq.Sequence<seq.Sequence<string>>) => seq.Sequence<string>} */
|
|
55
|
+
const join = seq.fold(joinOp)([])
|
|
59
56
|
|
|
60
57
|
/** @type {(open: string) => (close: string) => (input: seq.Sequence<seq.Sequence<string>>) => seq.Sequence<string>} */
|
|
61
58
|
const list = open => close => {
|
|
62
|
-
const seqOpen =
|
|
63
|
-
const seqClose =
|
|
59
|
+
const seqOpen = [open]
|
|
60
|
+
const seqClose = [close]
|
|
64
61
|
return input => seq.concat(seqOpen, join(input), seqClose)
|
|
65
62
|
}
|
|
66
63
|
|
|
@@ -83,15 +80,15 @@ const serialize = sort => {
|
|
|
83
80
|
f(v))
|
|
84
81
|
/** @type {(object: Object) => seq.Sequence<string>} */
|
|
85
82
|
const objectSerialize = input => {
|
|
86
|
-
const entries =
|
|
83
|
+
const entries = Object.entries(input)
|
|
87
84
|
const sortedEntries = sort(entries)
|
|
85
|
+
const _ = seq.toArray(sortedEntries)
|
|
88
86
|
const serializedEntries = seq.map(propertySerialize)(sortedEntries)
|
|
89
87
|
return objectList(serializedEntries)
|
|
90
88
|
}
|
|
91
89
|
/** @type {(input: Array) => seq.Sequence<string>} */
|
|
92
90
|
const arraySerialize = input => {
|
|
93
|
-
const
|
|
94
|
-
const serializedEntries = seq.map(f)(sequence)
|
|
91
|
+
const serializedEntries = seq.map(f)(input)
|
|
95
92
|
return arrayList(serializedEntries)
|
|
96
93
|
}
|
|
97
94
|
/** @type {(value: Json) => seq.Sequence < string >} */
|
|
@@ -118,7 +115,10 @@ const serialize = sort => {
|
|
|
118
115
|
*
|
|
119
116
|
* @type {(mapEntries: MapEntries) => (value: Json) => string}
|
|
120
117
|
*/
|
|
121
|
-
const stringify = sort => value =>
|
|
118
|
+
const stringify = sort => value => {
|
|
119
|
+
const _s = serialize(sort)(value)
|
|
120
|
+
return seq.join('')(_s)
|
|
121
|
+
}
|
|
122
122
|
|
|
123
123
|
/** @type {(value: string) => Json} */
|
|
124
124
|
const parse = value => JSON.parse(value)
|
package/json/test.js
CHANGED
|
@@ -5,7 +5,8 @@ const { identity } = require('../function')
|
|
|
5
5
|
if (json.addProperty("Hello")([])({}) !== "Hello") { throw 'error' }
|
|
6
6
|
|
|
7
7
|
{
|
|
8
|
-
const
|
|
8
|
+
const r = json.addProperty("Hello")(['a'])({})
|
|
9
|
+
const x = json.stringify(sort)(r)
|
|
9
10
|
if (x !== '{"a":"Hello"}') { throw x }
|
|
10
11
|
}
|
|
11
12
|
|
package/map/index.js
CHANGED
|
@@ -56,14 +56,14 @@ const create = root => ({
|
|
|
56
56
|
* @type {{
|
|
57
57
|
* readonly get: (name: string) => undefined
|
|
58
58
|
* readonly set: (name: string) => <T>(value: T) => Map<T>
|
|
59
|
-
* readonly entries:
|
|
59
|
+
* readonly entries: []
|
|
60
60
|
* readonly root: undefined
|
|
61
61
|
* }}
|
|
62
62
|
*/
|
|
63
63
|
const empty = {
|
|
64
64
|
get: () => undefined,
|
|
65
65
|
set: name => value => create([[name, value]]),
|
|
66
|
-
entries:
|
|
66
|
+
entries: [],
|
|
67
67
|
root: undefined
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -73,7 +73,9 @@ const setOperator = map => ([k, v]) => map.set(k)(v)
|
|
|
73
73
|
/** @type {<T>(entries: seq.Sequence<Entry<T>>) => Map<T>} */
|
|
74
74
|
const fromEntries = entries => {
|
|
75
75
|
/** @typedef {typeof entries extends seq.Sequence<Entry<infer T>> ? T : never} T */
|
|
76
|
-
|
|
76
|
+
/** @type {Map<T>} */
|
|
77
|
+
const init = empty
|
|
78
|
+
return seq.reduce(setOperator)(init)(entries)
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
module.exports = {
|
package/module-manager/index.js
CHANGED
|
@@ -82,15 +82,11 @@ const internal = pack => {
|
|
|
82
82
|
const externalOrInternal = p =>
|
|
83
83
|
p === undefined ? () => undefined : (typeof p === 'function' ? external(p) : internal(p))
|
|
84
84
|
|
|
85
|
-
/** @type {(_: Dependencies) => (
|
|
85
|
+
/** @type {(_: Dependencies) => (path: Path) => Module|undefined} */
|
|
86
86
|
const external = packages => {
|
|
87
87
|
/** @type {(_: readonly [string, Path]) => Module|undefined} */
|
|
88
88
|
const defined = ([first, tail]) => externalOrInternal(packages(first))(tail)
|
|
89
|
-
|
|
90
|
-
const sf = splitFirst
|
|
91
|
-
return compose
|
|
92
|
-
(option.map(defined))
|
|
93
|
-
(sf)
|
|
89
|
+
return path => option.map(defined)(splitFirst(path))
|
|
94
90
|
}
|
|
95
91
|
|
|
96
92
|
/** @type {(_: Location) => (_: string) => Module|undefined} */
|
package/object/index.js
CHANGED
|
@@ -14,9 +14,6 @@ const map = require('../map')
|
|
|
14
14
|
* @typedef {readonly[string, T]} Entry
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
/** @type {<T>(object: Map<T>) => seq.Sequence<Entry<T>>} */
|
|
18
|
-
const entries = object => array.sequence(Object.entries(object))
|
|
19
|
-
|
|
20
17
|
/** @type {(name: string) => <T>(object: Map<T>) => T|undefined} */
|
|
21
18
|
const at = name => object => Object.getOwnPropertyDescriptor(object, name)?.value
|
|
22
19
|
|
|
@@ -24,8 +21,6 @@ const at = name => object => Object.getOwnPropertyDescriptor(object, name)?.valu
|
|
|
24
21
|
const sort = entries => map.fromEntries(entries).entries
|
|
25
22
|
|
|
26
23
|
module.exports = {
|
|
27
|
-
/** @readonly */
|
|
28
|
-
entries,
|
|
29
24
|
/** @readonly */
|
|
30
25
|
at,
|
|
31
26
|
/** @readonly */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "functionalscript",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.220",
|
|
4
4
|
"description": "FunctionalScript is a functional subset of JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://github.com/functionalscript/functionalscript#readme",
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@types/node": "^16.11.
|
|
22
|
+
"@types/node": "^16.11.11",
|
|
23
23
|
"typescript": "^4.5.2"
|
|
24
24
|
}
|
|
25
25
|
}
|
package/sequence/README.md
CHANGED
|
@@ -2,11 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
Sequence types:
|
|
4
4
|
|
|
5
|
+
- Sequence
|
|
5
6
|
- Array
|
|
6
|
-
- List
|
|
7
7
|
- Iterable
|
|
8
8
|
- AsyncIterable
|
|
9
9
|
|
|
10
|
+
# Sequence Types
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
type Sequence<T> =
|
|
14
|
+
readonly T[] |
|
|
15
|
+
Thunk<T> |
|
|
16
|
+
|
|
17
|
+
type Thunk<T> = () => Node<T>
|
|
18
|
+
|
|
19
|
+
type Node<T> =
|
|
20
|
+
undefined |
|
|
21
|
+
{ readonly first: T, readonly tail: Sequence<T> } |
|
|
22
|
+
readonly[Sequence<T>, Sequence<T>]
|
|
23
|
+
```
|
|
24
|
+
|
|
10
25
|
## Functions
|
|
11
26
|
|
|
12
27
|
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
|
package/sequence/array/index.js
CHANGED
|
@@ -95,21 +95,6 @@ const splitLast = a => {
|
|
|
95
95
|
/** @type {(index: number) => <T>(a: Array<T>) => readonly[T]|undefined} */
|
|
96
96
|
const at = index => a => index < a.length ? [a[index]] : undefined
|
|
97
97
|
|
|
98
|
-
/** @type {<T>(array: Array<T>) => seq.Sequence<T>} */
|
|
99
|
-
const sequence = a => {
|
|
100
|
-
/** @typedef {typeof a extends Array<infer T> ? T : never} T */
|
|
101
|
-
/** @type {(index: number) => seq.Sequence<T>} */
|
|
102
|
-
const seq = index => () => {
|
|
103
|
-
const result = at(index)(a)
|
|
104
|
-
if (result === undefined) { return undefined }
|
|
105
|
-
return [result[0], seq(index + 1)]
|
|
106
|
-
}
|
|
107
|
-
return seq(0)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** @type {<T>(list: seq.Sequence<T>) => readonly T[]} */
|
|
111
|
-
const fromSequence = input => Array.from(seq.iterable(input))
|
|
112
|
-
|
|
113
98
|
module.exports = {
|
|
114
99
|
/** @readonly */
|
|
115
100
|
at,
|
|
@@ -125,8 +110,4 @@ module.exports = {
|
|
|
125
110
|
splitFirst,
|
|
126
111
|
/** @readonly */
|
|
127
112
|
splitLast,
|
|
128
|
-
/** @readonly */
|
|
129
|
-
sequence,
|
|
130
|
-
/** @readonly */
|
|
131
|
-
fromSequence,
|
|
132
113
|
}
|
package/sequence/index.js
CHANGED
|
@@ -1,348 +1,315 @@
|
|
|
1
|
-
const seqOp = require('./operator')
|
|
2
1
|
const { compose } = require('../function')
|
|
2
|
+
const { logicalNot, strictEqual, addition } = require('../function/operator')
|
|
3
3
|
const op = require('../function/operator')
|
|
4
|
-
const { logicalNot, strictEqual } = require('../function/operator')
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* @template T
|
|
8
|
-
* @typedef {
|
|
7
|
+
* @typedef { readonly T[] | Thunk<T> } Sequence<T>
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* @template T
|
|
13
|
-
* @typedef {
|
|
12
|
+
* @typedef { () => Node<T> } Thunk
|
|
14
13
|
*/
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
|
-
* Use `next` function to get `first` and `tail` of the list.
|
|
18
|
-
*
|
|
19
|
-
* Please note that the sequence also contains `Concat<T>. We need this as
|
|
20
|
-
* a workaround because modern JavaScript implementations don't support
|
|
21
|
-
* ES6 TCO (Tail Call Optimization). Without this workaround, we may have
|
|
22
|
-
* a stack overflow if a list contains a lot of concateneted lists.
|
|
23
|
-
*
|
|
24
16
|
* @template T
|
|
25
|
-
* @typedef {
|
|
17
|
+
* @typedef { Result<T> | Concat<T> } Node<T>
|
|
26
18
|
*/
|
|
27
19
|
|
|
28
20
|
/**
|
|
29
21
|
* @template T
|
|
30
|
-
* @typedef {
|
|
22
|
+
* @typedef { readonly[Sequence<T>, Sequence<T>] } Concat<T>
|
|
31
23
|
*/
|
|
32
24
|
|
|
33
25
|
/**
|
|
34
26
|
* @template T
|
|
35
|
-
* @typedef {readonly
|
|
27
|
+
* @typedef { undefined | { readonly first: T, readonly tail: Sequence<T> } } Result
|
|
36
28
|
*/
|
|
37
29
|
|
|
38
30
|
const empty = () => undefined
|
|
39
31
|
|
|
40
|
-
/** @type {<T>(
|
|
41
|
-
const
|
|
32
|
+
/** @type {<T>(array: readonly T[]) => Result<T>} */
|
|
33
|
+
const fromArray = array => {
|
|
34
|
+
/** @typedef {typeof array extends readonly(infer T)[] ? T : never} T */
|
|
35
|
+
/** @type {(index: number) => Result<T>} */
|
|
36
|
+
const at = index => {
|
|
37
|
+
if (array.length <= index) { return undefined }
|
|
38
|
+
return { first: array[index], tail: () => at(index + 1) }
|
|
39
|
+
}
|
|
40
|
+
return at(0)
|
|
41
|
+
}
|
|
42
42
|
|
|
43
|
-
/** @type {<
|
|
44
|
-
const
|
|
43
|
+
/** @type {<T>(sequence: Sequence<T>) => Node<T>} */
|
|
44
|
+
const node = sequence => sequence instanceof Array ? fromArray(sequence) : sequence()
|
|
45
45
|
|
|
46
|
-
/** @type {<T>(
|
|
47
|
-
const
|
|
48
|
-
|
|
46
|
+
/** @type {<T>(concat: Concat<T>) => Sequence<T>} */
|
|
47
|
+
const concatNext = ([a, b]) => {
|
|
48
|
+
const result = node(a)
|
|
49
|
+
if (result === undefined) {
|
|
50
|
+
return b
|
|
51
|
+
} else if (result instanceof Array) {
|
|
52
|
+
const [aa, ab] = result
|
|
53
|
+
return () => [aa, () => [ab, b]]
|
|
54
|
+
} else {
|
|
55
|
+
const { first, tail } = result
|
|
56
|
+
return () => ({ first, tail: () => [tail, b] })
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** @type {<T>(sequence: Sequence<T>) => Result<T>} */
|
|
61
|
+
const next = sequence => {
|
|
62
|
+
let i = sequence
|
|
49
63
|
while (true) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
const n = node(i)
|
|
65
|
+
if (!(n instanceof Array)) { return n }
|
|
66
|
+
i = concatNext(n)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @type {<T>(sequence: Sequence<T>) => Iterable<T>} */
|
|
71
|
+
const iterable = sequence => ({
|
|
72
|
+
*[Symbol.iterator]() {
|
|
73
|
+
let i = sequence
|
|
74
|
+
while (true) {
|
|
75
|
+
if (i instanceof Array) { return yield *i }
|
|
76
|
+
const n = node(i)
|
|
77
|
+
if (n === undefined) { return }
|
|
78
|
+
if (n instanceof Array) {
|
|
79
|
+
i = concatNext(n)
|
|
80
|
+
} else {
|
|
81
|
+
const { first, tail } = n
|
|
82
|
+
yield first
|
|
83
|
+
i = tail
|
|
56
84
|
}
|
|
57
|
-
i = b
|
|
58
|
-
} else {
|
|
59
|
-
i = norm(a)(b)
|
|
60
85
|
}
|
|
61
86
|
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
/** @type {<T>(sequence: Sequence<T>) => readonly T[]} */
|
|
90
|
+
const toArray = sequence => {
|
|
91
|
+
if (sequence instanceof Array) { return sequence }
|
|
92
|
+
return Array.from(iterable(sequence))
|
|
62
93
|
}
|
|
63
94
|
|
|
64
|
-
/** @type {<T>(
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
if (
|
|
68
|
-
|
|
95
|
+
/** @type {<T>(sequence: Sequence<Sequence<T>>) => Thunk<T>} */
|
|
96
|
+
const flat = sequence => () => {
|
|
97
|
+
const n = next(sequence)
|
|
98
|
+
if (n === undefined) { return undefined }
|
|
99
|
+
const { first, tail } = n
|
|
100
|
+
return [first, flat(tail)]
|
|
69
101
|
}
|
|
70
102
|
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
* @template R
|
|
74
|
-
* @typedef {(list: Sequence<T>) => Sequence<R>} SequenceMap
|
|
75
|
-
*/
|
|
103
|
+
/** @type {<T>(...array: readonly Sequence<T>[]) => Thunk<T>} */
|
|
104
|
+
const concat = (...array) => flat(array)
|
|
76
105
|
|
|
77
|
-
/**
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
106
|
+
/** @type {<I, O>(f: (value: I) => O) => (input: Sequence<I>) => Thunk<O>} */
|
|
107
|
+
const map = f => sequence => () => {
|
|
108
|
+
const n = next(sequence)
|
|
109
|
+
if (n === undefined) { return undefined }
|
|
110
|
+
const { first, tail } = n
|
|
111
|
+
return { first: f(first), tail: map(f)(tail) }
|
|
112
|
+
}
|
|
82
113
|
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
while (i !== 0) {
|
|
94
|
-
i = i - 1
|
|
95
|
-
iResult = sequence(array[i])(iResult)
|
|
96
|
-
}
|
|
97
|
-
return iResult
|
|
114
|
+
/** @type {<I, O>(f: (value: I) => Sequence<O>) => (input: Sequence<I>) => Thunk<O>} */
|
|
115
|
+
const flatMap = f => sequence => flat(map(f)(sequence))
|
|
116
|
+
|
|
117
|
+
/** @type {<T>(f: (value: T) => boolean) => (input: Sequence<T>) => Thunk<T>} */
|
|
118
|
+
const filter = f => sequence => () => {
|
|
119
|
+
const n = next(sequence)
|
|
120
|
+
if (n === undefined) { return undefined }
|
|
121
|
+
const { first, tail } = n
|
|
122
|
+
const fTail = filter(f)(tail)
|
|
123
|
+
return f(first) ? { first, tail: fTail } : fTail()
|
|
98
124
|
}
|
|
99
125
|
|
|
100
|
-
/** @type {<
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
result = [array[i], result]
|
|
109
|
-
}
|
|
110
|
-
return result
|
|
126
|
+
/** @type {<I, O>(f: (value: I) => O|undefined) => (input: Sequence<I>) => Thunk<O>} */
|
|
127
|
+
const filterMap = f => sequence => () => {
|
|
128
|
+
const n = next(sequence)
|
|
129
|
+
if (n === undefined) { return undefined }
|
|
130
|
+
const { first, tail } = n
|
|
131
|
+
const fFirst = f(first)
|
|
132
|
+
const fTail = filterMap(f)(tail)
|
|
133
|
+
return fFirst === undefined ? fTail() : { first: fFirst, tail: fTail }
|
|
111
134
|
}
|
|
112
135
|
|
|
113
|
-
/** @type {(
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
return f(0)
|
|
136
|
+
/** @type {<T>(f: (value: T) => boolean) => (input: Sequence<T>) => Thunk<T>} */
|
|
137
|
+
const takeWhile = f => input => () => {
|
|
138
|
+
const result = next(input)
|
|
139
|
+
if (result === undefined) { return undefined }
|
|
140
|
+
const { first, tail } = result
|
|
141
|
+
if (!f(first)) { return undefined }
|
|
142
|
+
return { first, tail: takeWhile(f)(result.tail) }
|
|
121
143
|
}
|
|
122
144
|
|
|
123
|
-
/** @type {<T
|
|
124
|
-
const
|
|
145
|
+
/** @type {<T>(f: (value: T) => boolean) => (input: Sequence<T>) => Thunk<T>} */
|
|
146
|
+
const dropWhile = f => input => () => {
|
|
125
147
|
let i = input
|
|
126
|
-
while (true) {
|
|
148
|
+
while (true) {
|
|
127
149
|
const result = next(i)
|
|
128
150
|
if (result === undefined) { return undefined }
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
if (firstResult !== undefined) {
|
|
132
|
-
return norm(firstResult)(flatMap(f)(tail))
|
|
133
|
-
}
|
|
151
|
+
const { first, tail } = result
|
|
152
|
+
if (!f(first)) return result
|
|
134
153
|
i = tail
|
|
135
154
|
}
|
|
136
155
|
}
|
|
137
156
|
|
|
138
|
-
/** @type {<T>(
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
/** @type {<T, R>(f: (value: T) => R) => SequenceMap<T, R>} */
|
|
142
|
-
const map = f => flatMap(i => list(f(i)))
|
|
143
|
-
|
|
144
|
-
/** @type {<T>(f: (value: T) => boolean) => SequenceMap<T, T>} */
|
|
145
|
-
const filter = f => flatMap(i => f(i) ? list(i) : empty)
|
|
146
|
-
|
|
147
|
-
/** @type {<T, R>(f: (value: T) => R|undefined) => (value: T) => Sequence<R>} */
|
|
148
|
-
const filterMapFunc = f => i => {
|
|
149
|
-
const result = f(i)
|
|
150
|
-
if (result === undefined) { return empty }
|
|
151
|
-
return list(result)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/** @type {<T, R>(f: (value: T) => R|undefined) => SequenceMap<T, R>} */
|
|
155
|
-
const filterMap = f => flatMap(filterMapFunc(f))
|
|
156
|
-
|
|
157
|
-
/** @type {<T, R>(s: seqOp.Scan<T, R>) => SequenceMap<T, R>} */
|
|
158
|
-
const scan = s => input => () => {
|
|
157
|
+
/** @type {<D>(def: D) => <T>(input: Sequence<T>) => D|T} */
|
|
158
|
+
const first = def => input => {
|
|
159
159
|
const result = next(input)
|
|
160
|
-
if (result === undefined) {
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
const [first, tail] = result
|
|
164
|
-
const [newFirst, newS] = s(first)
|
|
165
|
-
return [newFirst, scan(newS)(tail)]
|
|
160
|
+
if (result === undefined) { return def }
|
|
161
|
+
return result.first
|
|
166
162
|
}
|
|
167
163
|
|
|
168
|
-
/** @type {<T
|
|
169
|
-
const exclusiveScan = ([first, s]) => input => () => [first, scan(s)(input)]
|
|
170
|
-
|
|
171
|
-
/** @type {<T>(def: T) => (input: Sequence<T>) => T} */
|
|
164
|
+
/** @type {<D>(def: D) => <T>(input: Sequence<T>) => D|T} */
|
|
172
165
|
const last = def => input => {
|
|
166
|
+
/** @typedef {typeof input extends Sequence<infer T> ? T : never} T */
|
|
167
|
+
/** @type {(typeof def)|T} */
|
|
173
168
|
let r = def
|
|
174
|
-
let i = input
|
|
169
|
+
let i = input
|
|
175
170
|
while (true) {
|
|
176
171
|
const result = next(i)
|
|
177
172
|
if (result === undefined) {
|
|
178
173
|
return r
|
|
179
174
|
}
|
|
180
|
-
r = result
|
|
181
|
-
i = result
|
|
175
|
+
r = result.first
|
|
176
|
+
i = result.tail
|
|
182
177
|
}
|
|
183
178
|
}
|
|
184
179
|
|
|
185
|
-
/** @type {<
|
|
186
|
-
const
|
|
180
|
+
/** @type {<D>(def: D) => <T>(f: (value: T) => boolean) => (sequence: Sequence<T>) => D|T} */
|
|
181
|
+
const find = def => f => input => first(def)(filter(f)(input))
|
|
187
182
|
|
|
188
|
-
/** @type {<T
|
|
189
|
-
const
|
|
183
|
+
/** @type {<T>(f: (value: T) => boolean) => (sequence: Sequence<T>) => boolean} */
|
|
184
|
+
const some = f => input => find(false)(x => x)(map(f)(input))
|
|
190
185
|
|
|
191
|
-
|
|
186
|
+
/** @type {<T>(f: (value: T) => boolean) => (sequence: Sequence<T>) => boolean} */
|
|
187
|
+
const every = f => input => !some(compose(f)(logicalNot))(input)
|
|
192
188
|
|
|
193
|
-
|
|
189
|
+
/** @type {<T>(value: T) => (sequence: Sequence<T>) => boolean} */
|
|
190
|
+
const includes = value => some(strictEqual(value))
|
|
194
191
|
|
|
195
|
-
|
|
192
|
+
/** @type {(count: number) => Thunk<number>} */
|
|
193
|
+
const countdown = count => () => {
|
|
194
|
+
if (count <= 0) { return undefined }
|
|
195
|
+
const first = count - 1
|
|
196
|
+
return { first, tail: countdown(first) }
|
|
197
|
+
}
|
|
196
198
|
|
|
197
|
-
/**
|
|
198
|
-
|
|
199
|
+
/**
|
|
200
|
+
* @template T,A
|
|
201
|
+
* @typedef {(value: T) => ScanState<T, A>} ScanFunc
|
|
202
|
+
*/
|
|
199
203
|
|
|
200
|
-
/**
|
|
201
|
-
|
|
204
|
+
/**
|
|
205
|
+
* @template T,A
|
|
206
|
+
* @typedef {readonly[A, ScanFunc<T, A>]} ScanState
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
/** @type {<T,A>(operator: ScanFunc<T, A>) => (input: Sequence<T>) => Thunk<A>} */
|
|
210
|
+
const scan = operator => input => () => {
|
|
202
211
|
const result = next(input)
|
|
203
|
-
if (result === undefined
|
|
204
|
-
|
|
212
|
+
if (result === undefined) { return undefined }
|
|
213
|
+
const { first, tail } = result
|
|
214
|
+
const r = operator(first)
|
|
215
|
+
return { first: r[0], tail: scan(r[1])(tail) }
|
|
205
216
|
}
|
|
206
217
|
|
|
207
|
-
/** @type {(
|
|
208
|
-
const
|
|
209
|
-
let iN = n
|
|
210
|
-
let iInput = input
|
|
211
|
-
while (true) {
|
|
212
|
-
const result = next(iInput)
|
|
213
|
-
if (iN <= 0 || result === undefined) { return result }
|
|
214
|
-
iN = iN - 1
|
|
215
|
-
iInput = result[1]
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
+
/** @type {<T,A>(operator: ScanFunc<T, A>) => <D>(def: D)=> (input: Sequence<T>) => D|A} */
|
|
219
|
+
const scanReduce = operator => def => input => last(def)(scan(operator)(input))
|
|
218
220
|
|
|
219
|
-
/**
|
|
220
|
-
|
|
221
|
+
/**
|
|
222
|
+
* @template T,A
|
|
223
|
+
* @typedef {(prior: A) => (value: T) => A} ReduceOperator
|
|
224
|
+
*/
|
|
221
225
|
|
|
222
|
-
/** @type {<T>(
|
|
223
|
-
const
|
|
226
|
+
/** @type {<T,A>(operator: ReduceOperator<T, A>) => (init: A) => ScanState<T, A>} */
|
|
227
|
+
const scanState = operator => init => [init, scanFunc(operator)(init)]
|
|
224
228
|
|
|
225
|
-
/** @type {<T>(
|
|
226
|
-
const
|
|
229
|
+
/** @type {<T,A>(operator: ReduceOperator<T, A>) => (init: A) => ScanFunc<T, A>} */
|
|
230
|
+
const scanFunc = operator => init => value => {
|
|
231
|
+
const result = operator(init)(value)
|
|
232
|
+
return scanState(operator)(result)
|
|
233
|
+
}
|
|
227
234
|
|
|
228
|
-
/** @type {<T>(
|
|
229
|
-
const
|
|
235
|
+
/** @type {<T,A>(operator: ReduceOperator<T, A>) => (init: A) => (input: Sequence<T>) => A} */
|
|
236
|
+
const reduce = operator => init => scanReduce(scanFunc(operator)(init))(init)
|
|
230
237
|
|
|
231
|
-
/**
|
|
232
|
-
|
|
238
|
+
/**
|
|
239
|
+
* @template T
|
|
240
|
+
* @typedef {ReduceOperator<T, T>} FoldOperator
|
|
241
|
+
*/
|
|
233
242
|
|
|
234
|
-
/** @type {<T>(
|
|
235
|
-
const
|
|
236
|
-
*[Symbol.iterator]() {
|
|
237
|
-
let i = list
|
|
238
|
-
while (true) {
|
|
239
|
-
const result = next(i)
|
|
240
|
-
if (result === undefined) { return }
|
|
241
|
-
yield result[0]
|
|
242
|
-
i = result[1]
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
})
|
|
243
|
+
/** @type {<T>(operator: FoldOperator<T>) => <D>(def: D) => (input: Sequence<T>) => D|T} */
|
|
244
|
+
const fold = operator => def => scanReduce(scanState(operator))(def)
|
|
246
245
|
|
|
247
|
-
|
|
248
|
-
const asyncIterable = list => ({
|
|
249
|
-
async *[Symbol.asyncIterator]() {
|
|
250
|
-
let i = list
|
|
251
|
-
while (true) {
|
|
252
|
-
const result = next(i)
|
|
253
|
-
if (result === undefined) { return }
|
|
254
|
-
yield result[0]
|
|
255
|
-
i = result[1]
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
})
|
|
246
|
+
const sum = fold(addition)(0)
|
|
259
247
|
|
|
260
|
-
/** @type {
|
|
261
|
-
const
|
|
262
|
-
const resultA = next(a)
|
|
263
|
-
if (resultA === undefined) { return undefined }
|
|
264
|
-
const resultB = next(b)
|
|
265
|
-
if (resultB === undefined) { return undefined }
|
|
266
|
-
return [[resultA[0], resultB[0]], zip(resultA[1])(resultB[1])]
|
|
267
|
-
}
|
|
248
|
+
/** @type {(separator: string) => (input: Sequence<string>) => string} */
|
|
249
|
+
const join = separator => fold(op.join(separator))('')
|
|
268
250
|
|
|
269
|
-
/**
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
iSource = tail
|
|
280
|
-
}
|
|
281
|
-
}
|
|
251
|
+
/**
|
|
252
|
+
* @template T
|
|
253
|
+
* @typedef {readonly[number, T]} Entry
|
|
254
|
+
*/
|
|
255
|
+
|
|
256
|
+
/** @type {(index: number) => <T>(value: T) => ScanState<T, Entry<T>>} */
|
|
257
|
+
const entryOp = index => value => [[index, value], entryOp(index + 1)]
|
|
258
|
+
|
|
259
|
+
/** @type {<T>(input: Sequence<T>) => Thunk<Entry<T>>} */
|
|
260
|
+
const entries = scan(entryOp(0))
|
|
282
261
|
|
|
283
262
|
module.exports = {
|
|
284
263
|
/** @readonly */
|
|
285
|
-
|
|
264
|
+
empty,
|
|
286
265
|
/** @readonly */
|
|
287
|
-
|
|
266
|
+
iterable,
|
|
288
267
|
/** @readonly */
|
|
289
|
-
|
|
268
|
+
next,
|
|
290
269
|
/** @readonly */
|
|
291
|
-
|
|
270
|
+
toArray,
|
|
292
271
|
/** @readonly */
|
|
293
|
-
|
|
272
|
+
flat,
|
|
294
273
|
/** @readonly */
|
|
295
|
-
|
|
274
|
+
last,
|
|
296
275
|
/** @readonly */
|
|
297
|
-
|
|
276
|
+
concat,
|
|
298
277
|
/** @readonly */
|
|
299
278
|
first,
|
|
300
279
|
/** @readonly */
|
|
301
|
-
|
|
302
|
-
/** @readonly */
|
|
303
|
-
asyncIterable,
|
|
280
|
+
map,
|
|
304
281
|
/** @readonly */
|
|
305
282
|
flatMap,
|
|
306
283
|
/** @readonly */
|
|
307
|
-
flat,
|
|
308
|
-
/** @readonly */
|
|
309
|
-
map,
|
|
310
|
-
/** @readonly */
|
|
311
284
|
filter,
|
|
312
285
|
/** @readonly */
|
|
313
|
-
|
|
314
|
-
/** @readonly */
|
|
315
|
-
scan,
|
|
316
|
-
/** @readonly */
|
|
317
|
-
exclusiveScan,
|
|
318
|
-
/** @readonly */
|
|
319
|
-
last,
|
|
286
|
+
find,
|
|
320
287
|
/** @readonly */
|
|
321
|
-
|
|
288
|
+
some,
|
|
322
289
|
/** @readonly */
|
|
323
|
-
|
|
290
|
+
every,
|
|
324
291
|
/** @readonly */
|
|
325
|
-
|
|
292
|
+
includes,
|
|
326
293
|
/** @readonly */
|
|
327
|
-
|
|
294
|
+
takeWhile,
|
|
328
295
|
/** @readonly */
|
|
329
|
-
|
|
296
|
+
dropWhile,
|
|
330
297
|
/** @readonly */
|
|
331
|
-
|
|
298
|
+
scanFunc,
|
|
332
299
|
/** @readonly */
|
|
333
|
-
|
|
300
|
+
scanState,
|
|
334
301
|
/** @readonly */
|
|
335
|
-
|
|
302
|
+
scan,
|
|
336
303
|
/** @readonly */
|
|
337
|
-
|
|
304
|
+
reduce,
|
|
338
305
|
/** @readonly */
|
|
339
|
-
|
|
306
|
+
fold,
|
|
340
307
|
/** @readonly */
|
|
341
|
-
|
|
308
|
+
sum,
|
|
342
309
|
/** @readonly */
|
|
343
|
-
|
|
310
|
+
join,
|
|
344
311
|
/** @readonly */
|
|
345
|
-
|
|
312
|
+
entries,
|
|
346
313
|
/** @readonly */
|
|
347
|
-
|
|
348
|
-
}
|
|
314
|
+
countdown,
|
|
315
|
+
}
|
|
@@ -39,7 +39,7 @@ const sum = reduce(seq.sum)
|
|
|
39
39
|
|
|
40
40
|
const length = reduce(seq.length)
|
|
41
41
|
|
|
42
|
-
const join = compose(
|
|
42
|
+
const join = compose(seq.join)(reduce)
|
|
43
43
|
|
|
44
44
|
/** @type {<T, R>(f: (value: T) => Iterable<R>) => (c: Iterable<T>) => Iterable<R>} */
|
|
45
45
|
const flatMap = f => c => ({
|
|
@@ -34,8 +34,8 @@ const { compose } = require('../../function')
|
|
|
34
34
|
const file = _ => 'x'
|
|
35
35
|
/** @type {(_: string) => string|undefined} */
|
|
36
36
|
const x = p => compose
|
|
37
|
-
(i.
|
|
38
|
-
(i.
|
|
37
|
+
(i.map(x => file(x())))
|
|
38
|
+
(i.find(x => x !== undefined))
|
|
39
39
|
([() => p, () => `${p}.js`, () => `${p}/index.js`])
|
|
40
40
|
if (x('index.js') !== 'x') { throw 'error' }
|
|
41
41
|
}
|
|
@@ -35,7 +35,7 @@ const scan = operator => {
|
|
|
35
35
|
return [result, f(result)]
|
|
36
36
|
}
|
|
37
37
|
return f
|
|
38
|
-
}
|
|
38
|
+
}
|
|
39
39
|
|
|
40
40
|
/** @type {<R, T>(operator: op.ReduceOperator<R, T>) => (first: R) => ExclusiveScan<T, R>} */
|
|
41
41
|
const exclusiveScan = operator => first => [first, scan(operator)(first)]
|
package/sequence/test.js
CHANGED
|
@@ -1,75 +1,142 @@
|
|
|
1
|
-
const
|
|
2
|
-
const { empty, next, list, flatMap, concat, exclusiveScan, find, every, some, first, drop, map, generate } = require('.')
|
|
3
|
-
const { sum } = require('./operator')
|
|
4
|
-
const array = require('./array')
|
|
1
|
+
const _ = require('.')
|
|
5
2
|
const json = require('../json')
|
|
6
|
-
const {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
3
|
+
const { sort } = require('../object')
|
|
4
|
+
const { addition } = require('../function/operator')
|
|
5
|
+
|
|
6
|
+
/** @type {(sequence: _.Sequence<json.Json>) => string} */
|
|
7
|
+
const stringify = sequence => json.stringify(sort)(_.toArray(sequence))
|
|
8
|
+
|
|
9
|
+
{
|
|
10
|
+
const s = stringify([1, 2, 3])
|
|
11
|
+
if (s !== '[1,2,3]') { throw s }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
{
|
|
15
|
+
const result = stringify(_.countdown(10))
|
|
16
|
+
if (result !== '[9,8,7,6,5,4,3,2,1,0]') { throw result }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
{
|
|
20
|
+
const result = stringify(_.concat([1, 2, 3], [4, 5], [6], [], [7, 8, 9]))
|
|
21
|
+
if (result !== '[1,2,3,4,5,6,7,8,9]') { throw result }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
{
|
|
25
|
+
const result = _.concat([1], [2])
|
|
26
|
+
const x = _.next(result)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
{
|
|
30
|
+
const result = stringify(_.flatMap(x => [x, x * 2, x * 3])([0, 1, 2, 3]))
|
|
31
|
+
if (result !== '[0,0,0,1,2,3,2,4,6,3,6,9]') { throw result }
|
|
17
32
|
}
|
|
18
33
|
|
|
19
34
|
{
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
const result = stringify(_.takeWhile(x => x < 6)([1, 2, 3, 4, 5, 6, 7, 8, 9]))
|
|
36
|
+
if (result !== '[1,2,3,4,5]') { throw result }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
{
|
|
40
|
+
const result = _.find(undefined)(x => x % 2 === 0)([1, 2, 3, 4])
|
|
41
|
+
if (result !== 2) { throw result }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
{
|
|
45
|
+
const result = stringify(_.takeWhile(x => x < 10)([1, 2, 3, 4, 5, 10, 11]))
|
|
46
|
+
if (result !== '[1,2,3,4,5]') { throw result }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
{
|
|
50
|
+
const result = stringify(_.dropWhile(x => x < 10)([1, 2, 3, 4, 5, 10, 11]))
|
|
51
|
+
if (result !== '[10,11]') { throw result }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
{
|
|
55
|
+
const op = _.scanState(addition)
|
|
56
|
+
const result = stringify(_.scan(op)([2, 3, 4, 5]))
|
|
57
|
+
if (result !== '[2,5,9,14]') { throw result }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
{
|
|
61
|
+
const result = _.sum([2, 3, 4, 5])
|
|
62
|
+
if (result !== 14) { throw result }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
{
|
|
66
|
+
const result = _.fold(addition)(undefined)([2, 3, 4, 5])
|
|
67
|
+
if (result !== 14) { throw result }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
{
|
|
71
|
+
const result = _.fold(addition)(undefined)([])
|
|
72
|
+
if (result !== undefined) { throw result }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
{
|
|
76
|
+
const result = _.join('/')([])
|
|
77
|
+
if (result !== '') { throw result }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
{
|
|
81
|
+
const result = _.join('/')([''])
|
|
82
|
+
if (result !== '') { throw result }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
{
|
|
86
|
+
const result = stringify(_.entries([]))
|
|
87
|
+
if (result !== '[]') { throw result }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
{
|
|
91
|
+
const result = stringify(_.entries(['hello', 'world']))
|
|
92
|
+
if (result !== '[[0,"hello"],[1,"world"]]') { throw result }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// stress tests
|
|
96
|
+
|
|
97
|
+
const stress = () => {
|
|
31
98
|
{
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const r = next(x)
|
|
39
|
-
// print(x)
|
|
99
|
+
// 200_000_000 is too much
|
|
100
|
+
const n = 100_000_000
|
|
101
|
+
const result = _.toArray(_.countdown(n))
|
|
102
|
+
if (result.length !== n) { throw result.length }
|
|
103
|
+
const first = _.first(undefined)(result)
|
|
104
|
+
if (first !== n - 1) { throw first }
|
|
40
105
|
}
|
|
106
|
+
|
|
41
107
|
{
|
|
42
|
-
|
|
43
|
-
let
|
|
44
|
-
|
|
45
|
-
|
|
108
|
+
/** @type {_.Sequence<number>} */
|
|
109
|
+
let sequence = []
|
|
110
|
+
// 2_000_000 is too much
|
|
111
|
+
for (let i = 0; i < 1_000_000; ++i) {
|
|
112
|
+
sequence = _.concat(sequence, [i])
|
|
113
|
+
}
|
|
114
|
+
const r = _.toArray(sequence)
|
|
46
115
|
}
|
|
116
|
+
|
|
47
117
|
{
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
118
|
+
/** @type {_.Sequence<number>} */
|
|
119
|
+
let sequence = []
|
|
120
|
+
// 5_000_000 is too much
|
|
121
|
+
for (let i = 0; i < 2_000_000; ++i) {
|
|
122
|
+
sequence = _.concat(sequence, [i])
|
|
51
123
|
}
|
|
52
|
-
const
|
|
53
|
-
// print(x)
|
|
124
|
+
const a = _.next(sequence)
|
|
54
125
|
}
|
|
126
|
+
|
|
55
127
|
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
128
|
+
/** @type {_.Sequence<number>} */
|
|
129
|
+
let sequence = []
|
|
130
|
+
// 10_000_000 is too much
|
|
131
|
+
for (let i = 0; i < 5_000_000; ++i) {
|
|
132
|
+
sequence = _.concat([i], sequence)
|
|
59
133
|
}
|
|
60
|
-
const
|
|
61
|
-
// print(x)
|
|
134
|
+
const a = _.next(sequence)
|
|
62
135
|
}
|
|
63
136
|
}
|
|
64
137
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
138
|
+
//stress()
|
|
139
|
+
|
|
140
|
+
module.exports = {
|
|
69
141
|
|
|
70
|
-
{
|
|
71
|
-
const r = seq.reverse(seq.list(1, 2, 3, 4))
|
|
72
|
-
const s = array.fromSequence(r)
|
|
73
|
-
const j = json.stringify(identity)(s)
|
|
74
|
-
if (j !== '[4,3,2,1]') { throw j }
|
|
75
142
|
}
|
package/test.js
CHANGED
|
@@ -82,14 +82,14 @@ const assert_if = c => { if (c) { throw 'assert_if' } }
|
|
|
82
82
|
constructor: undefined
|
|
83
83
|
}
|
|
84
84
|
const c = o['constructor']
|
|
85
|
-
console.log(c)
|
|
85
|
+
//console.log(c)
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
{
|
|
89
89
|
/** @type {any} */
|
|
90
90
|
const b = '42'
|
|
91
91
|
const r = Number(b)
|
|
92
|
-
console.log(r)
|
|
92
|
+
//console.log(r)
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
{
|
|
@@ -106,7 +106,7 @@ const assert_if = c => { if (c) { throw 'assert_if' } }
|
|
|
106
106
|
//const c = o['propertyIsEnumerable']
|
|
107
107
|
//const c = o['toString']
|
|
108
108
|
const c = o['valueOf']
|
|
109
|
-
console.log(c)
|
|
109
|
+
//console.log(c)
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
{
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# Sequence Operators
|
|
2
|
-
|
|
3
|
-
## A `FlatMap` Operator
|
|
4
|
-
|
|
5
|
-
`flatMap = combine(flat)(map)`
|
|
6
|
-
|
|
7
|
-
## A `Scan` Operator
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
type Scan<A, T> = (accumulator: A) => (value: T) => A
|
|
11
|
-
type ExclusiveScan<A, T> = [first, Scan<A, T>]
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
`scan`
|
|
15
|
-
|
|
16
|
-
`reduce = last(first)(scan)`
|
|
17
|
-
|
|
18
|
-
An alternative definition of a scan:
|
|
19
|
-
|
|
20
|
-
```ts
|
|
21
|
-
type Scan2<A, T> = (value: T) => [A, Scan2<A, T>]
|
|
22
|
-
type ExclusiveScan2<A, T> = [first, Scan2<A, T>]
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
`takeWhile`, `find` can use `scan`. Optimization: if a `Scan2` part is `emptyScan` then we can stop searching.
|
|
26
|
-
|
|
27
|
-
## A Universal Operator `FlatScan`
|
|
28
|
-
|
|
29
|
-
```ts
|
|
30
|
-
type FlatScan<A, T> = (value: T) => FlatScanSequence<A, T>
|
|
31
|
-
type FlatScanSequence<A, T> = () => FlatScanResult<A, T>
|
|
32
|
-
type FlatScanResult<A, T> =
|
|
33
|
-
['value', A, FlatScanSequence<A, T>] |
|
|
34
|
-
['novalue', FlatScan<A, T>]
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Optimization: if result is `empty`, then we can stop.
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
const empty = ['novalue', () => empty]
|
|
41
|
-
```
|