functionalscript 0.0.211 → 0.0.216
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/.github/FUNDING.yml +12 -0
- package/function/index.js +2 -2
- package/json/test.js +4 -4
- package/package.json +1 -1
- package/sequence/index.js +28 -9
- package/sequence/operator/README.md +41 -0
- package/sequence/test.js +34 -24
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# These are supported funding model platforms
|
|
2
|
+
|
|
3
|
+
github: [sergey-shandar] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
|
4
|
+
patreon: # Replace with a single Patreon username
|
|
5
|
+
open_collective: # Replace with a single Open Collective username
|
|
6
|
+
ko_fi: # Replace with a single Ko-fi username
|
|
7
|
+
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
8
|
+
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
9
|
+
liberapay: # Replace with a single Liberapay username
|
|
10
|
+
issuehunt: # Replace with a single IssueHunt username
|
|
11
|
+
otechie: # Replace with a single Otechie username
|
|
12
|
+
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
package/function/index.js
CHANGED
package/json/test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const json = require('.')
|
|
2
2
|
const { sort } = require('../object')
|
|
3
|
-
const {
|
|
3
|
+
const { identity } = require('../function')
|
|
4
4
|
|
|
5
5
|
if (json.addProperty("Hello")([])({}) !== "Hello") { throw 'error' }
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ if (json.addProperty("Hello")([])({}) !== "Hello") { throw 'error' }
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
{
|
|
13
|
-
const x = json.stringify(
|
|
13
|
+
const x = json.stringify(identity)(json.addProperty("Hello")(['a'])({}))
|
|
14
14
|
if (x !== '{"a":"Hello"}') { throw x }
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -20,7 +20,7 @@ if (json.addProperty("Hello")([])({}) !== "Hello") { throw 'error' }
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
{
|
|
23
|
-
const x = json.stringify(
|
|
23
|
+
const x = json.stringify(identity)(json.addProperty("Hello")(['a'])({ c: [], b: 12 }))
|
|
24
24
|
if (x !== '{"c":[],"b":12,"a":"Hello"}') { throw x }
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -34,6 +34,6 @@ if (json.addProperty("Hello")([])({}) !== "Hello") { throw 'error' }
|
|
|
34
34
|
{
|
|
35
35
|
const _0 = { a: { y: [24] }, c: [], b: 12 }
|
|
36
36
|
const _1 = json.addProperty("Hello")(['a', 'x'])(_0)
|
|
37
|
-
const _2 = json.stringify(
|
|
37
|
+
const _2 = json.stringify(identity)(_1)
|
|
38
38
|
if (_2 !== '{"a":{"y":[24],"x":"Hello"},"c":[],"b":12}') { throw _2 }
|
|
39
39
|
}
|
package/package.json
CHANGED
package/sequence/index.js
CHANGED
|
@@ -18,7 +18,7 @@ const { logicalNot, strictEqual } = require('../function/operator')
|
|
|
18
18
|
*
|
|
19
19
|
* Please note that the sequence also contains `Concat<T>. We need this as
|
|
20
20
|
* a workaround because modern JavaScript implementations don't support
|
|
21
|
-
* ES6 TCO (Tail Call Optimization). Without this
|
|
21
|
+
* ES6 TCO (Tail Call Optimization). Without this workaround, we may have
|
|
22
22
|
* a stack overflow if a list contains a lot of concateneted lists.
|
|
23
23
|
*
|
|
24
24
|
* @template T
|
|
@@ -37,6 +37,9 @@ const { logicalNot, strictEqual } = require('../function/operator')
|
|
|
37
37
|
|
|
38
38
|
const empty = () => undefined
|
|
39
39
|
|
|
40
|
+
/** @type {<T>(first: T) => (tail: Sequence<T>) => Sequence<T>} */
|
|
41
|
+
const sequence = first => tail => () => [first, tail]
|
|
42
|
+
|
|
40
43
|
/** @type {<F, T>(a: readonly[F, Sequence<T>]) => (b: Sequence<T>) => readonly[F, Sequence<T>]} */
|
|
41
44
|
const norm = ([a0, a1]) => b => [a0, [a1, b]]
|
|
42
45
|
|
|
@@ -78,7 +81,7 @@ const first = input => {
|
|
|
78
81
|
*/
|
|
79
82
|
|
|
80
83
|
/**
|
|
81
|
-
* Note: the operation is not lazy. It
|
|
84
|
+
* Note: the operation is not lazy. It traverses the given array and creates a linked list.
|
|
82
85
|
*
|
|
83
86
|
* @type {<T>(...array: readonly T[]) => Sequence<T>}
|
|
84
87
|
*/
|
|
@@ -86,14 +89,12 @@ const list = (...array) => {
|
|
|
86
89
|
/** @typedef {typeof array extends readonly(infer T)[] ? T : never} T */
|
|
87
90
|
let i = array.length
|
|
88
91
|
/** @type {Sequence<T>} */
|
|
89
|
-
let
|
|
92
|
+
let iResult = empty
|
|
90
93
|
while (i !== 0) {
|
|
91
94
|
i = i - 1
|
|
92
|
-
|
|
93
|
-
const listResult = [array[i], result]
|
|
94
|
-
result = () => listResult
|
|
95
|
+
iResult = sequence(array[i])(iResult)
|
|
95
96
|
}
|
|
96
|
-
return
|
|
97
|
+
return iResult
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
/** @type {<T>(...array: readonly Sequence<T>[]) => Sequence<T>} */
|
|
@@ -184,8 +185,8 @@ const last = def => input => {
|
|
|
184
185
|
/** @type {<T, R>(s: seqOp.ExclusiveScan<T, R>) => (input: Sequence<T>) => R} */
|
|
185
186
|
const reduce = ([first, s]) => input => last(first)(scan(s)(input))
|
|
186
187
|
|
|
187
|
-
/** @type {<T, R>(
|
|
188
|
-
const fold =
|
|
188
|
+
/** @type {<T, R>(operator: op.ReduceOperator<R, T>) => (first: R) => (input: Sequence<T>) => R} */
|
|
189
|
+
const fold = operator => first => reduce(seqOp.exclusiveScan(operator)(first))
|
|
189
190
|
|
|
190
191
|
const entries = scan(seqOp.entries)
|
|
191
192
|
|
|
@@ -265,6 +266,20 @@ const zip = a => b => () => {
|
|
|
265
266
|
return [[resultA[0], resultB[0]], zip(resultA[1])(resultB[1])]
|
|
266
267
|
}
|
|
267
268
|
|
|
269
|
+
/** @type {<T>(s: Sequence<T>) => Sequence<T>} */
|
|
270
|
+
const reverse = s => {
|
|
271
|
+
/** @type {typeof s} */
|
|
272
|
+
let iResult = empty
|
|
273
|
+
let iSource = s
|
|
274
|
+
while (true) {
|
|
275
|
+
const result = next(iSource)
|
|
276
|
+
if (result === undefined) { return iResult }
|
|
277
|
+
const [first, tail] = result
|
|
278
|
+
iResult = sequence(first)(iResult)
|
|
279
|
+
iSource = tail
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
268
283
|
module.exports = {
|
|
269
284
|
/** @readonly */
|
|
270
285
|
next,
|
|
@@ -273,6 +288,8 @@ module.exports = {
|
|
|
273
288
|
/** @readonly */
|
|
274
289
|
empty,
|
|
275
290
|
/** @readonly */
|
|
291
|
+
sequence,
|
|
292
|
+
/** @readonly */
|
|
276
293
|
at,
|
|
277
294
|
/** @readonly */
|
|
278
295
|
concat,
|
|
@@ -326,4 +343,6 @@ module.exports = {
|
|
|
326
343
|
includes,
|
|
327
344
|
/** @readonly */
|
|
328
345
|
zip,
|
|
346
|
+
/** @readonly */
|
|
347
|
+
reverse,
|
|
329
348
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
```
|
package/sequence/test.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
const seq = require('.')
|
|
2
|
+
const { empty, next, list, flatMap, concat, exclusiveScan, find, every, some, first, drop, map, generate } = require('.')
|
|
2
3
|
const { sum } = require('./operator')
|
|
3
4
|
const array = require('./array')
|
|
5
|
+
const json = require('../json')
|
|
6
|
+
const { identity } = require('../function')
|
|
4
7
|
|
|
5
8
|
/** @type {<T>(input: seq.Sequence<T>) => void} */
|
|
6
9
|
const print = input => {
|
|
7
10
|
let i = input
|
|
8
11
|
while (true) {
|
|
9
|
-
const result =
|
|
12
|
+
const result = next(i)
|
|
10
13
|
if (result === undefined) { return }
|
|
11
14
|
console.log(result[0])
|
|
12
15
|
i = result[1]
|
|
@@ -14,47 +17,47 @@ const print = input => {
|
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
{
|
|
17
|
-
const big =
|
|
18
|
-
const list0 =
|
|
19
|
-
const list1 =
|
|
20
|
-
const list2 =
|
|
21
|
-
const list3 =
|
|
22
|
-
const r =
|
|
23
|
-
if (
|
|
24
|
-
if (
|
|
25
|
-
if (
|
|
26
|
-
if (
|
|
27
|
-
if (
|
|
20
|
+
const big = list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 42, 60)
|
|
21
|
+
const list0 = list(0, 1, 2, 3)
|
|
22
|
+
const list1 = flatMap(x => list(x, x * 2, x * 3))(list0)
|
|
23
|
+
const list2 = concat(list0, list0)
|
|
24
|
+
const list3 = exclusiveScan(sum)(list0)
|
|
25
|
+
const r = find(x => x === 42)(big)
|
|
26
|
+
if (every(x => x > 0)(big) !== true) { throw 'x' }
|
|
27
|
+
if (every(x => x < 20)(big) !== false) { throw 'x' }
|
|
28
|
+
if (some(x => x > 100)(big) !== false) { throw 'x' }
|
|
29
|
+
if (some(x => x > 50)(big) !== true) { throw 'x' }
|
|
30
|
+
if (first(drop(16)(big)) !== 42) { throw 'drop' }
|
|
28
31
|
{
|
|
29
|
-
const a =
|
|
32
|
+
const a = map(generate)(generate(100_000))
|
|
30
33
|
const ar = array.fromSequence(a)
|
|
31
|
-
// This operation
|
|
34
|
+
// This operation uses a lot of stack because `...`
|
|
32
35
|
// puts array items on a stack.
|
|
33
36
|
// Use `array.sequence` instead
|
|
34
|
-
const x =
|
|
35
|
-
const r =
|
|
37
|
+
const x = concat(...ar)
|
|
38
|
+
const r = next(x)
|
|
36
39
|
// print(x)
|
|
37
40
|
}
|
|
38
41
|
{
|
|
39
|
-
const a = array.fromSequence(
|
|
40
|
-
let x =
|
|
41
|
-
const r =
|
|
42
|
+
const a = array.fromSequence(generate(1_000_000))
|
|
43
|
+
let x = concat(array.sequence(a), big)
|
|
44
|
+
const r = next(x)
|
|
42
45
|
// print(x)
|
|
43
46
|
}
|
|
44
47
|
{
|
|
45
48
|
let x = big
|
|
46
49
|
for (let i = 0; i < 1_000_000; ++i) {
|
|
47
|
-
x =
|
|
50
|
+
x = concat(empty, x)
|
|
48
51
|
}
|
|
49
|
-
const r =
|
|
52
|
+
const r = next(x)
|
|
50
53
|
// print(x)
|
|
51
|
-
}
|
|
54
|
+
}
|
|
52
55
|
{
|
|
53
56
|
let x = big
|
|
54
57
|
for (let i = 0; i < 1_000_000; ++i) {
|
|
55
|
-
x =
|
|
58
|
+
x = concat(x, list(i))
|
|
56
59
|
}
|
|
57
|
-
const r =
|
|
60
|
+
const r = next(x)
|
|
58
61
|
// print(x)
|
|
59
62
|
}
|
|
60
63
|
}
|
|
@@ -63,3 +66,10 @@ const print = input => {
|
|
|
63
66
|
const x = seq.join(':')(seq.list("1", "2", "3", "4", "5", "6"))
|
|
64
67
|
if (x !== "1:2:3:4:5:6") { throw x }
|
|
65
68
|
}
|
|
69
|
+
|
|
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
|
+
}
|