functionalscript 0.0.181 → 0.0.187

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 CHANGED
@@ -10,6 +10,9 @@ Try FunctionalScript [here](https://functionalscript.com/).
10
10
 
11
11
  Create a new FunctionalScript repository on GitHub [here](https://github.com/functionalscript/template/generate).
12
12
 
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
+ A workaround for this problem is to use `let` for renaming objects.
15
+
13
16
  ## JSON
14
17
 
15
18
  ```js
package/btree/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  const { index3, index5 } = require('../cmp')
2
- const list = require('../sequence/list')
3
- const { pipe } = require('../function')
2
+ const seq = require('../sequence')
4
3
 
5
4
  /**
6
5
  * @template T
@@ -306,66 +305,38 @@ const replaceVisitor = {
306
305
  notFound: notFoundGet,
307
306
  }
308
307
 
309
- /** @type {<T>(_: Node<T>) => Iterable<T>} */
310
- const values = node => ({
311
- *[Symbol.iterator]() {
312
- switch (node.length) {
313
- case 1: case 2: {
314
- yield* node
315
- return
316
- }
317
- case 3: {
318
- yield* values(node[0])
319
- yield node[1]
320
- yield* values(node[2])
321
- return
322
- }
323
- default: {
324
- yield* values(node[0])
325
- yield node[1]
326
- yield* values(node[2])
327
- yield node[3]
328
- yield* values(node[4])
329
- return
330
- }
331
- }
332
- }
333
- })
308
+ /** @type {<T>(...array: readonly seq.Sequence<T>[]) => seq.Sequence<T>} */
309
+ const flatArray = (...array) => seq.flat(seq.fromArray(array))
334
310
 
335
- /** @type {<T>(...array: readonly list.List<T>[]) => list.List<T>} */
336
- const flatArray = (...array) => list.flat(list.fromArray(array))
337
-
338
- /** @type {<T>(node: Node<T>) => list.List<T>} */
311
+ /** @type {<T>(node: Node<T>) => seq.Sequence<T>} */
339
312
  const valuesList = node => () => {
340
313
  const f = () => {
341
314
  switch (node.length) {
342
- case 1: case 2: { return list.fromArray(node) }
315
+ case 1: case 2: { return seq.fromArray(node) }
343
316
  case 3: {
344
317
  return flatArray(
345
318
  valuesList(node[0]),
346
- list.one(node[1]),
319
+ seq.one(node[1]),
347
320
  valuesList(node[2])
348
321
  )
349
322
  }
350
323
  default: {
351
324
  return flatArray(
352
325
  valuesList(node[0]),
353
- list.one(node[1]),
326
+ seq.one(node[1]),
354
327
  valuesList(node[2]),
355
- list.one(node[3]),
328
+ seq.one(node[3]),
356
329
  valuesList(node[4])
357
330
  )
358
331
  }
359
332
  }
360
333
  }
361
- return list.get(f())
334
+ return seq.next(f())
362
335
  }
363
336
 
364
337
  module.exports = {
365
338
  /** @readonly */
366
339
  valuesList,
367
- /** @readonly */
368
- values,
369
340
  /**
370
341
  * @readonly
371
342
  * @type { <T>(cmp: Cmp<T>) => (node: Node<T>) => T|undefined }
package/btree/test.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const btree = require('.')
2
2
  const { setVisitor, valuesList } = btree
3
3
  const { cmp } = require('../cmp')
4
- const list = require('../sequence/list')
4
+ const list = require('../sequence')
5
5
 
6
6
  /** @type {(node: btree.Node<string>) => (value: string) => btree.Node<string>} */
7
7
  const set = node => value => {
@@ -21,12 +21,10 @@ const test = () => {
21
21
  node = set(node)('f')
22
22
  //
23
23
  {
24
- /** @type {import('../sequence/list').Result<string>} */
25
- let result = list.get(valuesList(node))
24
+ /** @type {import('../sequence').Result<string>} */
25
+ let result = list.next(valuesList(node))
26
26
  while (result !== undefined) {
27
- const t = result[0]
28
- console.log(t)
29
- result = list.get(result[1])
27
+ result = list.next(result[1])
30
28
  }
31
29
  }
32
30
  }
@@ -1,18 +1,41 @@
1
+ /**
2
+ * @template A
3
+ * @template B
4
+ * @template R
5
+ * @typedef {(a: A) => (b: B) => R} BinaryOperator
6
+ */
7
+
1
8
  /**
2
9
  * @template R
3
10
  * @template T
4
- * @typedef {(prior: R) => (value: T) => R} BinaryOperator
11
+ * @typedef {BinaryOperator<R, T, R>} ReduceOperator
5
12
  */
6
13
 
7
- /** @type {(separator: string) => BinaryOperator<string, string>} */
14
+ /** @type {(separator: string) => ReduceOperator<string, string>} */
8
15
  const join = separator => prior => value => `${prior}${separator}${value}`
9
16
 
10
17
  /** @type {(sum: number) => (value: number) => number} */
11
18
  const addition = a => b => a + b
12
19
 
20
+ /**
21
+ * @template T
22
+ * @template R
23
+ * @typedef {(value: T) => R} UnaryOperator
24
+ */
25
+
26
+ /** @type {(value: boolean) => boolean} */
27
+ const logicalNot = v => !v
28
+
29
+ /** @type {<T>(a: T) => (b: T) => boolean} */
30
+ const strictEqual = a => b => a === b
31
+
13
32
  module.exports = {
14
33
  /** @readonly */
15
34
  join,
16
35
  /** @readonly */
17
36
  addition,
37
+ /** @readonly */
38
+ strictEqual,
39
+ /** @readonly */
40
+ logicalNot,
18
41
  }
package/json/index.js ADDED
@@ -0,0 +1,88 @@
1
+ const seq = require('../sequence')
2
+ const map = require('../map')
3
+ const op = require('../sequence/operator')
4
+ const { todo } = require('../dev')
5
+
6
+ /**
7
+ * @typedef {{
8
+ * readonly [k in string]: Json
9
+ * }} Object
10
+ */
11
+
12
+ /** @typedef {readonly Json[]} Array */
13
+
14
+ /** @typedef {Object|boolean|string|number|null|Array} Json */
15
+
16
+ /** @type {(value: Json) => (path: readonly string[]) => (src: Json|undefined) => Json} */
17
+ const addProperty = value => {
18
+ /** @type {(path: seq.Sequence<string>) => (src: Json|undefined) => Json} */
19
+ const f = path => src => {
20
+ const result = seq.next(path)
21
+ if (result === undefined) { return value }
22
+ const srcObject = (src === undefined || src === null || typeof src !== 'object' || src instanceof Array) ? {} : src
23
+ const [name, tail] = result
24
+ return { ...srcObject, [name]: f(tail)(srcObject[name]) }
25
+ }
26
+ return path => f(seq.fromArray(path))
27
+ }
28
+
29
+ /** @type {(kv: readonly[string, seq.Sequence<string>]) => seq.Sequence<string>} */
30
+ const property = ([k, v]) => {
31
+ let r = seq.one(JSON.stringify(k))
32
+ r = seq.concat(r)(seq.one(':'))
33
+ return seq.concat(r)(v)
34
+ }
35
+
36
+ /** @type {op.Scan<seq.Sequence<string>, seq.Sequence<string>>} */
37
+ const commaValue = a => [seq.concat(seq.one(','))(a), commaValue]
38
+
39
+ /** @type {op.Scan<seq.Sequence<string>, seq.Sequence<string>>} */
40
+ const joinScan = value => [value, commaValue]
41
+
42
+ /** @type {seq.SequenceMap<seq.Sequence<string>, seq.Sequence<string>>} */
43
+ const join = seq.scan(joinScan)
44
+
45
+ /** @type {(object: Object) => seq.Sequence<string>} */
46
+ const objectStringify = object => {
47
+ /** @type {map.Map<seq.Sequence<string>>} */
48
+ let m = map.empty
49
+ for (const [k, v] of Object.entries(object)) {
50
+ m = m.set(k)(stringSeq(v))
51
+ }
52
+ const properties = join(seq.map(property)(m.entries))
53
+ const result = seq.concat(seq.one('{'))(seq.flat(properties))
54
+ return seq.concat(result)(seq.one('}'))
55
+ }
56
+
57
+ /** @type {(array: Array) => seq.Sequence<string>} */
58
+ const arrayStringify = array => {
59
+ let a = seq.flat(join(seq.map(stringSeq)(seq.fromArray(array))))
60
+ const s = seq.concat(seq.one('['))(a)
61
+ return seq.concat(s)(seq.one(']'))
62
+ }
63
+
64
+ /** @type {(value: Json) => seq.Sequence<string>} */
65
+ const stringSeq = value => {
66
+ const x = typeof value
67
+ switch (typeof value) {
68
+ case 'boolean': { return seq.one(value ? "true" : "false") }
69
+ // Note: we shouldn't use JSON.stringify since it has non determenistic behavior.
70
+ // In particular: property order could be different.
71
+ case 'number': case 'string': { return seq.one(JSON.stringify(value)) }
72
+ default: {
73
+ if (value === null) { return seq.one("null") }
74
+ if (value instanceof Array) { return arrayStringify(value) }
75
+ return objectStringify(value)
76
+ }
77
+ }
78
+ }
79
+
80
+ /** @type {(value: Json) => string} */
81
+ const stringify = value => seq.join('')(stringSeq(value))
82
+
83
+ module.exports = {
84
+ /** @readonly */
85
+ addProperty,
86
+ /** @readonly */
87
+ stringify,
88
+ }
package/json/test.js ADDED
@@ -0,0 +1,13 @@
1
+ const json = require('.')
2
+
3
+ if (json.addProperty("Hello")([])({}) !== "Hello") { throw 'error' }
4
+
5
+ {
6
+ const x = json.stringify(json.addProperty("Hello")(['a'])({}))
7
+ if (x !== '{"a":"Hello"}') { throw x }
8
+ }
9
+
10
+ {
11
+ const x = json.stringify(json.addProperty("Hello")(['a'])({c:[],b:12}))
12
+ if (x !== '{"a":"Hello","b":12,"c":[]}') { throw x }
13
+ }
package/map/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const option = require("../option")
2
- const { getVisitor, setVisitor, values } = require("../btree")
2
+ const { getVisitor, setVisitor, valuesList } = require("../btree")
3
3
  const { cmp } = require("../cmp")
4
+ const list = require("../sequence")
4
5
 
5
6
  /** @typedef {import("../cmp").Sign} Sign */
6
7
 
@@ -29,7 +30,7 @@ const { cmp } = require("../cmp")
29
30
  * @typedef {{
30
31
  * readonly get: (name: string) => T|undefined
31
32
  * readonly set: (name: string) => (value: T) => Map<T>
32
- * readonly entries: () => Iterable<Entry<T>>
33
+ * readonly entries: list.Sequence<Entry<T>>
33
34
  * readonly root: undefined|TNode<Entry<T>>
34
35
  * }} Map
35
36
  */
@@ -46,7 +47,7 @@ const create = root => ({
46
47
  if ('overflow' in result) { return create(result.overflow) }
47
48
  throw ''
48
49
  },
49
- entries: () => values(root),
50
+ entries: valuesList(root),
50
51
  root,
51
52
  })
52
53
 
@@ -54,14 +55,14 @@ const create = root => ({
54
55
  * @type {{
55
56
  * readonly get: (name: string) => undefined
56
57
  * readonly set: (name: string) => <T>(value: T) => Map<T>
57
- * readonly entries: () => readonly []
58
+ * readonly entries: () => undefined
58
59
  * readonly root: undefined
59
60
  * }}
60
61
  */
61
62
  const empty = {
62
63
  get: () => undefined,
63
64
  set: name => value => create([[name, value]]),
64
- entries: () => [],
65
+ entries: list.empty,
65
66
  root: undefined
66
67
  }
67
68
 
package/map/test.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const { empty } = require('.')
2
+ const list = require('../sequence')
2
3
 
3
4
  {
4
5
  let m = empty.set('a')(1)
@@ -44,7 +45,7 @@ const { empty } = require('.')
44
45
  if (m.get('x') !== 12) { throw 'error' }
45
46
  if (m.get('y') !== 44) { throw 'error' }
46
47
  if (m.get('a') !== undefined) { throw 'error' }
47
- const entries = Array.from(m.entries())
48
+ const entries = Array.from(list.iterable(m.entries))
48
49
  if (entries.length !== 2) { throw 'error' }
49
50
  }
50
51
 
@@ -3,7 +3,7 @@ const { pipe } = require('../function')
3
3
  const option = require('../option')
4
4
  const { head, last, splitLast, splitFirst } = array
5
5
  const iter = require('../sequence/iterable')
6
- const seq = require('../sequence')
6
+ const seq = require('../sequence/operator')
7
7
 
8
8
  /**
9
9
  * @template T
@@ -39,8 +39,8 @@ const seq = require('../sequence')
39
39
 
40
40
  /** @typedef {(_: string) => undefined|Package|Dependencies} Dependencies */
41
41
 
42
- /** @type {seq.InclusiveScan<string, undefined|Path>} */
43
- const pathNormReduce = seq.inclusiveScan
42
+ /** @type {seq.ExclusiveScan<string, undefined|Path>} */
43
+ const pathNormReduce = seq.exclusiveScan
44
44
  (path => item =>
45
45
  path === undefined ?
46
46
  undefined :
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.0.181",
3
+ "version": "0.0.187",
4
4
  "description": "FunctionalScript is a functional subset of JavaScript",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,5 +1,5 @@
1
1
  const { pipe } = require('../../function')
2
- const seq = require('..')
2
+ const seq = require('../operator')
3
3
 
4
4
  /**
5
5
  * @template T
@@ -55,10 +55,10 @@ const scan = s => c => ({
55
55
  }
56
56
  })
57
57
 
58
- /** @type {<T, R>(s: seq.InclusiveScan<T, R>) => (c: AsyncOrSyncIterable<T>) => AsyncIterable<R>} */
59
- const inclusiveScan = ([first, s]) => c => concat([first])(scan(s)(c))
58
+ /** @type {<T, R>(s: seq.ExclusiveScan<T, R>) => (c: AsyncOrSyncIterable<T>) => AsyncIterable<R>} */
59
+ const exclusiveScan = ([first, s]) => c => concat([first])(scan(s)(c))
60
60
 
61
- /** @type {<T, R>(is: seq.InclusiveScan<T, R>) => (c: AsyncOrSyncIterable<T>) => Promise<R>} */
61
+ /** @type {<T, R>(is: seq.ExclusiveScan<T, R>) => (c: AsyncOrSyncIterable<T>) => Promise<R>} */
62
62
  const reduce = ([first, s]) => async c => {
63
63
  let next = first
64
64
  for await (const i of scan(s)(c)) {
@@ -105,7 +105,7 @@ module.exports = {
105
105
  /** @readonly */
106
106
  scan,
107
107
  /** @readonly */
108
- inclusiveScan,
108
+ exclusiveScan,
109
109
  /** @readonly */
110
110
  takeWhile,
111
111
  }
package/sequence/index.js CHANGED
@@ -1,93 +1,304 @@
1
- const op = require('../function/operator')
2
- const { id } = require('../function')
1
+ const array = require('./array')
2
+ const seqOp = require('./operator')
3
+ const { pipe } = require('../function')
4
+ const { logicalNot, strictEqual } = require('../function/operator')
3
5
 
4
6
  /**
5
- * @template T0
6
- * @template T1
7
- * @typedef {import('./array').Tuple2<T0, T1>} Tuple2
7
+ * @template T
8
+ * @typedef {() => Result<T>} SequenceFn
8
9
  */
9
10
 
10
11
  /**
11
12
  * @template T
12
- * @template R
13
- * @typedef {Tuple2<R, Scan<T, R>>} ScanResult
13
+ * @typedef {readonly [Sequence<T>, Sequence<T>]} Concat
14
14
  */
15
15
 
16
16
  /**
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 wotkaround we may have
22
+ * a stack overflow if a list contains a lot of concateneted lists.
23
+ *
17
24
  * @template T
18
- * @template R
19
- * @typedef {(value: T) => ScanResult<T, R>} Scan
25
+ * @typedef { SequenceFn<T> | Concat<T>} Sequence
20
26
  */
21
27
 
22
28
  /**
23
29
  * @template T
24
- * @template R
25
- * @typedef {Tuple2<R, Scan<T, R>>} InclusiveScan
30
+ * @typedef {FirstAndTail<T>|undefined} Result<T>
26
31
  */
27
32
 
28
- /** @type {<R, T>(operator: op.BinaryOperator<R, T>) => (prior: R) => Scan<T, R>} */
29
- const scan = operator => {
30
- /** @typedef {typeof operator extends op.BinaryOperator<infer R, infer T> ? [R, T] : never} RT */
31
- /** @typedef {RT[0]} R */
32
- /** @typedef {RT[1]} T */
33
- /** @type {(prior: R) => Scan<T, R>} */
34
- const f = prior => value => {
35
- const result = operator(prior)(value)
36
- return [result, f(result)]
37
- }
38
- return f
39
- }
40
-
41
- /** @type {<R, T>(operator: op.BinaryOperator<R, T>) => (first: R) => InclusiveScan<T, R>} */
42
- const inclusiveScan = operator => first => [first, scan(operator)(first)]
43
-
44
- /**
33
+ /**
45
34
  * @template T
46
- * @typedef {Tuple2<number, T>} Entry
35
+ * @typedef {array.Tuple2<T, Sequence<T>>} FirstAndTail
47
36
  */
48
37
 
49
- /** @type {(index: number) => <T>(value: T) => ScanResult<T, Entry<T>>} */
50
- const createEntries = index => value => [[index, value], createEntries(index + 1)]
51
-
52
- const entries = createEntries(0)
38
+ const empty = () => undefined
53
39
 
54
- /** @type {(separator: string) => InclusiveScan<string, string>} */
55
- const join = separator => ['', value => [value, scan(op.join(separator))(value)]]
40
+ /** @type {<F, T>(a: readonly[F, Sequence<T>]) => (b: Sequence<T>) => readonly[F, Sequence<T>]} */
41
+ const norm = ([a0, a1]) => b => [a0, [a1, b]]
56
42
 
57
- const sum = inclusiveScan(op.addition)(0)
43
+ /** @type {<T>(input: Sequence<T>) => Result<T>} */
44
+ const next = input => {
45
+ let i = input
46
+ while (true) {
47
+ if (typeof i === 'function') { return i() }
48
+ const [a, b] = i
49
+ if (typeof a === 'function') {
50
+ const result = a()
51
+ if (result !== undefined) {
52
+ return norm(result)(b)
53
+ }
54
+ i = b
55
+ } else {
56
+ i = norm(a)(b)
57
+ }
58
+ }
59
+ }
58
60
 
59
- /** @type {(a: number) => () => number} */
60
- const counter = a => () => a + 1
61
+ /** @type {<T>(input: Sequence<T>) => T|undefined} */
62
+ const first = input => {
63
+ const result = next(input)
64
+ if (result === undefined) { return undefined }
65
+ return result[0]
66
+ }
61
67
 
62
- const length = inclusiveScan(counter)(0)
68
+ /**
69
+ * @template T
70
+ * @template R
71
+ * @typedef {(list: Sequence<T>) => Sequence<R>} SequenceMap
72
+ */
63
73
 
64
74
  /**
65
75
  * @template T
66
76
  * @template R
67
- * @typedef {(value: T) => R} Func
77
+ * @typedef {(list: Sequence<T>) => R} SequenceReduce
68
78
  */
69
79
 
70
- /** @type {<T, R, X>(flatMap: (f: Func<T, readonly[R]>) => X) => (f: Func<T, R>) =>X} */
71
- const map = flatMap => f => flatMap(x => [f(x)])
80
+ /** @type {<T>(first: T) => Sequence<T>} */
81
+ const one = first => () => [first, empty]
82
+
83
+ /** @type {<T>(array: array.Array<T>) => Sequence<T>} */
84
+ const fromArray = a => {
85
+ /** @typedef {typeof a extends array.Array<infer T> ? T : never} T */
86
+ /** @type {(index: number) => Sequence<T>} */
87
+ const at = index => () => {
88
+ const result = array.at(index)(a)
89
+ if (result === undefined) { return undefined }
90
+ return [result[0], at(index + 1)]
91
+ }
92
+ return at(0)
93
+ }
94
+
95
+ /** @type {<T>(a: Sequence<T>) => SequenceMap<T, T>} */
96
+ const concat = a => b => [a, b]
97
+
98
+ /** @type {<T, R>(f: (value: T) => Sequence<R>) => SequenceMap<T, R>} */
99
+ const flatMap = f => input => () => {
100
+ let i = input
101
+ while (true) {
102
+ const result = next(i)
103
+ if (result === undefined) { return undefined }
104
+ const [first, tail] = result
105
+ const firstResult = next(f(first))
106
+ if (firstResult !== undefined) {
107
+ return norm(firstResult)(flatMap(f)(tail))
108
+ }
109
+ i = tail
110
+ }
111
+ }
112
+
113
+ /** @type {<T>(list: Sequence<Sequence<T>>) => Sequence<T>} */
114
+ const flat = flatMap(i => i)
115
+
116
+ /** @type {<T, R>(f: (value: T) => R) => SequenceMap<T, R>} */
117
+ const map = f => flatMap(i => one(f(i)))
118
+
119
+ /** @type {<T>(f: (value: T) => boolean) => SequenceMap<T, T>} */
120
+ const filter = f => flatMap(i => f(i) ? one(i) : empty)
121
+
122
+ /** @type {<T, R>(f: (value: T) => R|undefined) => (value: T) => Sequence<R>} */
123
+ const filterMapFunc = f => i => {
124
+ const result = f(i)
125
+ if (result === undefined) { return empty }
126
+ return one(result)
127
+ }
128
+
129
+ /** @type {<T, R>(f: (value: T) => R|undefined) => SequenceMap<T, R>} */
130
+ const filterMap = f => flatMap(filterMapFunc(f))
131
+
132
+ /** @type {<T, R>(s: seqOp.Scan<T, R>) => SequenceMap<T, R>} */
133
+ const scan = s => input => () => {
134
+ const result = next(input)
135
+ if (result === undefined) {
136
+ return result
137
+ }
138
+ const [first, tail] = result
139
+ const [newFirst, newS] = s(first)
140
+ return [newFirst, scan(newS)(tail)]
141
+ }
142
+
143
+ /** @type {<T, R>(s: seqOp.ExclusiveScan<T, R>) => SequenceMap<T, R>} */
144
+ const exclusiveScan = ([first, s]) => input => () => [first, scan(s)(input)]
145
+
146
+ /** @type {<T>(def: T) => (input: Sequence<T>) => T} */
147
+ const last = def => input => {
148
+ let r = def
149
+ let i = input
150
+ while (true) {
151
+ const result = next(i)
152
+ if (result === undefined) {
153
+ return r
154
+ }
155
+ r = result[0]
156
+ i = result[1]
157
+ }
158
+ }
159
+
160
+ /** @type {<T, R>(s: seqOp.ExclusiveScan<T, R>) => (input: Sequence<T>) => R} */
161
+ const reduce = ([first, s]) => input => last(first)(scan(s)(input))
162
+
163
+ const entries = scan(seqOp.entries)
164
+
165
+ const sum = reduce(seqOp.sum)
166
+
167
+ const length = reduce(seqOp.length)
168
+
169
+ const join = pipe(seqOp.join)(reduce)
170
+
171
+ /** @type {<T>(f: (value: T) => boolean) => SequenceMap<T, T>} */
172
+ const takeWhile = f => input => () => {
173
+ const result = next(input)
174
+ if (result === undefined || !f(result[0])) { return undefined }
175
+ return result
176
+ }
177
+
178
+ /** @type {(n: number) => <T>(input: Sequence<T>) => Sequence<T>} */
179
+ const drop = n => input => () => {
180
+ let iN = n
181
+ let iInput = input
182
+ while (true) {
183
+ const result = next(iInput)
184
+ if (iN <= 0 || result === undefined) { return result }
185
+ iN = iN - 1
186
+ iInput = result[1]
187
+ }
188
+ }
189
+
190
+ /** @type {(n: number) => <T>(input: Sequence<T>) => T|undefined} */
191
+ const at = n => input => first(drop(n)(input))
192
+
193
+ /** @type {<T>(f: (value: T) => boolean) => SequenceReduce<T, T|undefined>} */
194
+ const find = f => input => first(filter(f)(input))
195
+
196
+ /** @type {<T>(f: (value: T) => boolean) => SequenceReduce<T, boolean>} */
197
+ const some = f => input => find(x => x)(map(f)(input)) !== undefined
198
+
199
+ /** @type {<T>(value: T) => SequenceReduce<T, boolean>} */
200
+ const includes = value => some(strictEqual(value))
201
+
202
+ /** @type {<T>(f: (value: T) => boolean) => SequenceReduce<T, boolean>} */
203
+ const every = f => input => !some(pipe(f)(logicalNot))(input)
204
+
205
+ /** @type {<T>(list: Sequence<T>) => Iterable<T>} */
206
+ const iterable = list => ({
207
+ *[Symbol.iterator]() {
208
+ let i = list
209
+ while (true) {
210
+ const result = next(i)
211
+ if (result === undefined) { return }
212
+ yield result[0]
213
+ i = result[1]
214
+ }
215
+ }
216
+ })
217
+
218
+ /** @type {<T>(list: Sequence<T>) => AsyncIterable<T>} */
219
+ const asyncIterable = list => ({
220
+ async *[Symbol.asyncIterator]() {
221
+ let i = list
222
+ while (true) {
223
+ const result = next(i)
224
+ if (result === undefined) { return }
225
+ yield result[0]
226
+ i = result[1]
227
+ }
228
+ }
229
+ })
72
230
 
73
- /** @type {<T, X>(flatMap: (f: Func<T, readonly[T]|[]>) => X) => (f: Func<T, boolean>) =>X} */
74
- const filter = flatMap => f => flatMap(x => f(x) ? [x] : [])
231
+ /** @type {<A>(a: Sequence<A>) => <B>(b: Sequence<B>) => Sequence<array.Tuple2<A, B>>} */
232
+ const zip = a => b => () => {
233
+ const resultA = next(a)
234
+ if (resultA === undefined) { return undefined }
235
+ const resultB = next(b)
236
+ if (resultB === undefined) { return undefined }
237
+ return [[resultA[0], resultB[0]], zip(resultA[1])(resultB[1])]
238
+ }
239
+
240
+ /** @type {<T>(list: Sequence<T>) => readonly T[]} */
241
+ const toArray = input => Array.from(iterable(input))
75
242
 
76
243
  module.exports = {
77
244
  /** @readonly */
78
- inclusiveScan,
245
+ next,
246
+ /** @readonly */
247
+ one,
248
+ /** @readonly */
249
+ empty,
250
+ /** @readonly */
251
+ at,
252
+ /** @readonly */
253
+ concat,
254
+ /** @readonly */
255
+ first,
256
+ /** @readonly */
257
+ fromArray,
258
+ /** @readonly */
259
+ toArray,
260
+ /** @readonly */
261
+ iterable,
262
+ /** @readonly */
263
+ asyncIterable,
264
+ /** @readonly */
265
+ flatMap,
266
+ /** @readonly */
267
+ flat,
268
+ /** @readonly */
269
+ map,
270
+ /** @readonly */
271
+ filter,
272
+ /** @readonly */
273
+ filterMap,
79
274
  /** @readonly */
80
275
  scan,
81
276
  /** @readonly */
82
- join,
277
+ exclusiveScan,
278
+ /** @readonly */
279
+ last,
280
+ /** @readonly */
281
+ reduce,
282
+ /** @readonly */
283
+ entries,
83
284
  /** @readonly */
84
285
  sum,
85
286
  /** @readonly */
287
+ join,
288
+ /** @readonly */
86
289
  length,
87
290
  /** @readonly */
88
- entries,
291
+ drop,
89
292
  /** @readonly */
90
- map,
293
+ find,
91
294
  /** @readonly */
92
- filter,
93
- }
295
+ takeWhile,
296
+ /** @readonly */
297
+ some,
298
+ /** @readonly */
299
+ every,
300
+ /** @readonly */
301
+ includes,
302
+ /** @readonly */
303
+ zip,
304
+ }
@@ -1,5 +1,5 @@
1
1
  const { pipe } = require('../../function')
2
- const seq = require('..')
2
+ const seq = require('../operator')
3
3
 
4
4
  /** @type {<T>(a: Iterable<T>) => (b: Iterable<T>) => Iterable<T>} */
5
5
  const concat = a => b => ({
@@ -21,10 +21,10 @@ const scan = s => c => ({
21
21
  }
22
22
  })
23
23
 
24
- /** @type {<T, R>(s: seq.InclusiveScan<T, R>) => (c: Iterable<T>) => Iterable<R>} */
25
- const inclusiveScan = ([first, s]) => c => concat([first])(scan(s)(c))
24
+ /** @type {<T, R>(s: seq.ExclusiveScan<T, R>) => (c: Iterable<T>) => Iterable<R>} */
25
+ const exclusiveScan = ([first, s]) => c => concat([first])(scan(s)(c))
26
26
 
27
- /** @type {<T, R>(s: seq.InclusiveScan<T, R>) => (c: Iterable<T>) => R} */
27
+ /** @type {<T, R>(s: seq.ExclusiveScan<T, R>) => (c: Iterable<T>) => R} */
28
28
  const reduce = ([first, s]) => c => {
29
29
  let next = first
30
30
  for (const i of scan(s)(c)) {
@@ -93,7 +93,7 @@ module.exports = {
93
93
  /** @readonly */
94
94
  scan,
95
95
  /** @readonly */
96
- inclusiveScan,
96
+ exclusiveScan,
97
97
  /** @readonly */
98
98
  flatMap,
99
99
  /** @readonly */
@@ -0,0 +1,92 @@
1
+ const op = require('../../function/operator')
2
+
3
+ /**
4
+ * @template T0
5
+ * @template T1
6
+ * @typedef {import('../array').Tuple2<T0, T1>} Tuple2
7
+ */
8
+
9
+ /**
10
+ * @template T
11
+ * @template R
12
+ * @typedef {Tuple2<R, Scan<T, R>>} ScanResult
13
+ */
14
+
15
+ /**
16
+ * @template T
17
+ * @template R
18
+ * @typedef {(value: T) => ScanResult<T, R>} Scan
19
+ */
20
+
21
+ /**
22
+ * @template T
23
+ * @template R
24
+ * @typedef {Tuple2<R, Scan<T, R>>} ExclusiveScan
25
+ */
26
+
27
+ /** @type {<R, T>(operator: op.ReduceOperator<R, T>) => (prior: R) => Scan<T, R>} */
28
+ const scan = operator => {
29
+ /** @typedef {typeof operator extends op.ReduceOperator<infer R, infer T> ? [R, T] : never} RT */
30
+ /** @typedef {RT[0]} R */
31
+ /** @typedef {RT[1]} T */
32
+ /** @type {(prior: R) => Scan<T, R>} */
33
+ const f = prior => value => {
34
+ const result = operator(prior)(value)
35
+ return [result, f(result)]
36
+ }
37
+ return f
38
+ }
39
+
40
+ /** @type {<R, T>(operator: op.ReduceOperator<R, T>) => (first: R) => ExclusiveScan<T, R>} */
41
+ const exclusiveScan = operator => first => [first, scan(operator)(first)]
42
+
43
+ /**
44
+ * @template T
45
+ * @typedef {Tuple2<number, T>} Entry
46
+ */
47
+
48
+ /** @type {(index: number) => <T>(value: T) => ScanResult<T, Entry<T>>} */
49
+ const createEntries = index => value => [[index, value], createEntries(index + 1)]
50
+
51
+ const entries = createEntries(0)
52
+
53
+ /** @type {(separator: string) => ExclusiveScan<string, string>} */
54
+ const join = separator => ['', value => [value, scan(op.join(separator))(value)]]
55
+
56
+ const sum = exclusiveScan(op.addition)(0)
57
+
58
+ /** @type {(a: number) => () => number} */
59
+ const counter = a => () => a + 1
60
+
61
+ const length = exclusiveScan(counter)(0)
62
+
63
+ /**
64
+ * @template T
65
+ * @template R
66
+ * @typedef {(value: T) => R} Func
67
+ */
68
+
69
+ /** @type {<T, R, X>(flatMap: (f: Func<T, readonly[R]>) => X) => (f: Func<T, R>) =>X} */
70
+ const map = flatMap => f => flatMap(x => [f(x)])
71
+
72
+ /** @type {<T, X>(flatMap: (f: Func<T, readonly[T]|[]>) => X) => (f: Func<T, boolean>) =>X} */
73
+ const filter = flatMap => f => flatMap(x => f(x) ? [x] : [])
74
+
75
+ module.exports = {
76
+ /** @readonly */
77
+ exclusiveScan,
78
+ /** @readonly */
79
+ scan,
80
+ /** @readonly */
81
+ join,
82
+ /** @readonly */
83
+ sum,
84
+ /** @readonly */
85
+ length,
86
+ /** @readonly */
87
+ entries,
88
+ /** @readonly */
89
+ map,
90
+ /** @readonly */
91
+ filter,
92
+ }
@@ -0,0 +1,49 @@
1
+ const seq = require('.')
2
+ const { sum } = require('./operator')
3
+ const op = require('./operator')
4
+
5
+ /** @type {<T>(input: seq.Sequence<T>) => void} */
6
+ const print = input => {
7
+ let i = input
8
+ while (true) {
9
+ const result = seq.next(i)
10
+ if (result === undefined) { return }
11
+ // console.log(result[0])
12
+ i = result[1]
13
+ }
14
+ }
15
+
16
+ {
17
+ const big = seq.fromArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 42, 60])
18
+ const list0 = seq.fromArray([0, 1, 2, 3])
19
+ const list1 = seq.flatMap(x => seq.fromArray([x, x * 2, x * 3]))(list0)
20
+ const list2 = seq.concat(list0)(list0)
21
+ const list3 = seq.exclusiveScan(sum)(list0)
22
+ const r = seq.find(x => x === 42)(big)
23
+ if (seq.every(x => x > 0)(big) !== true) { throw 'x'}
24
+ if (seq.every(x => x < 20)(big) !== false) { throw 'x' }
25
+ if (seq.some(x => x > 100)(big) !== false) { throw 'x' }
26
+ if (seq.some(x => x > 50)(big) !== true) { throw 'x' }
27
+ if (seq.first(seq.drop(16)(big)) !== 42) { throw 'drop'}
28
+ {
29
+ let x = big
30
+ for (let i = 0; i < 1_000_000; ++i) {
31
+ x = seq.concat(seq.empty)(x)
32
+ }
33
+ const r = seq.next(x)
34
+ // print(x)
35
+ }
36
+ {
37
+ let x = big
38
+ for (let i = 0; i < 1_000_000; ++i) {
39
+ x = seq.concat(x)(seq.one(i))
40
+ }
41
+ const r = seq.next(x)
42
+ // print(x)
43
+ }
44
+ }
45
+
46
+ {
47
+ const x = seq.join(':')(seq.fromArray(["1", "2", "3", "4", "5", "6"]))
48
+ if (x !== "1:2:3:4:5:6") { throw x }
49
+ }
package/test.js CHANGED
@@ -1,10 +1,11 @@
1
1
  const i = require('./')
2
2
 
3
- require('./sequence/list/test')
3
+ require('./sequence/test')
4
4
  require('./btree/test')
5
5
  require('./sequence/iterable/test')
6
6
  require('./sequence/asyncIterable/test')
7
7
  require('./module-manager/test')
8
+ require('./json/test')
8
9
 
9
10
  /** @type {() => never} */
10
11
  const assert = () => { throw 'assert' }
@@ -1,222 +0,0 @@
1
- const array = require('../array')
2
- const option = require('../../option')
3
- const base = require('..')
4
- const { pipe } = require('../../function')
5
- const { todo } = require('../../dev')
6
-
7
- /**
8
- * @template T
9
- * @typedef {() => Result<T>} ListFunc
10
- */
11
-
12
- /**
13
- * We need this workaround because modern JavaScript implementations
14
- * don't support ES6 TCO (Tail Call Optimization)
15
- *
16
- * Without this wotkaround we may have a stack overflow if a list
17
- * contains a lot of concateneted lists.
18
- *
19
- * @template T
20
- * @typedef {readonly [List<T>, List<T>]} Concat
21
- */
22
-
23
- /**
24
- * @template T
25
- * @typedef { ListFunc<T> | Concat<T>} List
26
- */
27
-
28
- /**
29
- * @template T
30
- * @typedef {FirstAndTail<T>|undefined} Result<T>
31
- */
32
-
33
- /**
34
- * @template T
35
- * @typedef {array.Tuple2<T, List<T>>} FirstAndTail
36
- */
37
-
38
- const empty = () => undefined
39
-
40
- /** @type {<F, T>(a: readonly[F, List<T>]) => (b: List<T>) => readonly[F, List<T>]} */
41
- const norm = ([a0, a1]) => b => [a0, [a1, b]]
42
-
43
- /** @type {<T>(list: List<T>) => Result<T>} */
44
- const get = list => {
45
- let i = list
46
- while (true) {
47
- if (typeof i === 'function') { return i() }
48
- const [a, b] = i
49
- if (typeof a === 'function') {
50
- const result = a()
51
- if (result !== undefined) {
52
- return norm(result)(b)
53
- }
54
- i = b
55
- } else {
56
- i = norm(a)(b)
57
- }
58
- }
59
- }
60
-
61
- /**
62
- * @template T
63
- * @template R
64
- * @typedef {(list: List<T>) => List<R>} ListMap
65
- */
66
-
67
- /** @type {<T>(first: T) => List<T>} */
68
- const one = first => () => [first, empty]
69
-
70
- /** @type {<T>(array: array.Array<T>) => List<T>} */
71
- const fromArray = a => {
72
- /** @typedef {typeof a extends array.Array<infer T> ? T : never} T */
73
- /** @type {(index: number) => List<T>} */
74
- const at = index => () => {
75
- const result = array.at(index)(a)
76
- if (result === undefined) { return undefined }
77
- return [result[0], at(index + 1)]
78
- }
79
- return at(0)
80
- }
81
-
82
- /** @type {<T>(list0: List<T>) => ListMap<T, T>} */
83
- const concat = a => b => [a, b]
84
-
85
- /** @type {<T, R>(f: (value: T) => List<R>) => ListMap<T, R>} */
86
- const flatMap = f => input => () => {
87
- let i = input
88
- while (true) {
89
- const result = get(i)
90
- if (result === undefined) { return undefined }
91
- const [first, tail] = result
92
- const firstResult = get(f(first))
93
- if (firstResult !== undefined) {
94
- const [firstFirst, firstTail] = firstResult
95
- return [firstFirst, concat(firstTail)(flatMap(f)(tail))]
96
- }
97
- i = tail
98
- }
99
- }
100
-
101
- /** @type {<T>(list: List<List<T>>) => List<T>} */
102
- const flat = flatMap(i => i)
103
-
104
- /** @type {<T, R>(f: (value: T) => R) => ListMap<T, R>} */
105
- const map = f => flatMap(i => one(f(i)))
106
-
107
- /** @type {<T>(f: (value: T) => boolean) => ListMap<T, T>} */
108
- const filter = f => flatMap(i => f(i) ? one(i) : empty)
109
-
110
- /** @type {<T, R>(s: base.Scan<T, R>) => ListMap<T, R>} */
111
- const scan = s => input => () => {
112
- /** @typedef {typeof s extends base.Scan<infer T, infer R> ? [T, R] : never} TR */
113
- /** @typedef {TR[0]} T */
114
- /** @typedef {TR[1]} R */
115
- /** @type {(firstAndTail: FirstAndTail<T>) => Result<R>} */
116
- const defined = ([first, tail]) => {
117
- const [newFirst, newS] = s(first)
118
- return [newFirst, scan(newS)(tail)]
119
- }
120
- return option.map(defined)(get(input))
121
- }
122
-
123
- /** @type {<T, R>(s: base.InclusiveScan<T, R>) => ListMap<T, R>} */
124
- const inclusiveScan = ([first, s]) => input => () => [first, scan(s)(input)]
125
-
126
- /** @type {<T>(def: T) => (input: List<T>) => T} */
127
- const last = def => input => {
128
- let r = def
129
- let i = input
130
- while (true) {
131
- const result = get(i)
132
- if (result === undefined) {
133
- return r
134
- }
135
- r = result[0]
136
- i = result[1]
137
- }
138
- return r
139
- }
140
-
141
- /** @type {<T, R>(s: base.InclusiveScan<T, R>) => (input: List<T>) => R} */
142
- const reduce = ([first, s]) => input => last(first)(scan(s)(input))
143
-
144
- const entries = scan(base.entries)
145
-
146
- const sum = reduce(base.sum)
147
-
148
- const length = reduce(base.length)
149
-
150
- const join = pipe(base.join)(reduce)
151
-
152
- /** @type {<T>(f: (value: T) => boolean) => ListMap<T, T>} */
153
- const takeWhile = f => input => () => {
154
- const result = get(input)
155
- if (result === undefined || !f(result[0])) { return undefined }
156
- return result
157
- }
158
-
159
- /** @type {<T>(f: (value: T) => boolean) => (input: List<T>) => T|undefined} */
160
- const find = f => input => {
161
- /** @typedef {typeof f extends (value: infer T) => boolean ? T : never} T */
162
- /** @type {(result: FirstAndTail<T>) => T} */
163
- const defined = ([first]) => first
164
- return option.map(defined)(get(filter(f)(input)))
165
- }
166
-
167
- /**
168
- * Note: probably, it's possible to implement using the `scan` concept.
169
- * @type {<T>(list: List<T>) => Iterable<T>}
170
- */
171
- const iterable = list => ({
172
- *[Symbol.iterator]() {
173
- let i = list
174
- while (true) {
175
- const result = get(i)
176
- if (result === undefined) { return }
177
- yield result[0]
178
- i = result[1]
179
- }
180
- }
181
- })
182
-
183
- module.exports = {
184
- /** @readonly */
185
- get,
186
- /** @readonly */
187
- one,
188
- /** @readonly */
189
- empty,
190
- /** @readonly */
191
- concat,
192
- /** @readonly */
193
- fromArray,
194
- /** @readonly */
195
- iterable,
196
- /** @readonly */
197
- flatMap,
198
- /** @readonly */
199
- flat,
200
- /** @readonly */
201
- map,
202
- /** @readonly */
203
- filter,
204
- /** @readonly */
205
- scan,
206
- /** @readonly */
207
- inclusiveScan,
208
- /** @readonly */
209
- last,
210
- /** @readonly */
211
- reduce,
212
- /** @readonly */
213
- entries,
214
- /** @readonly */
215
- sum,
216
- /** @readonly */
217
- join,
218
- /** @readonly */
219
- length,
220
- /** @readonly */
221
- find,
222
- }
@@ -1,38 +0,0 @@
1
- const list = require('.')
2
- const { sum } = require('..')
3
-
4
- /** @type {<T>(l: list.List<T>) => void} */
5
- const print = a => {
6
- let i = a
7
- while (true) {
8
- const result = list.get(i)
9
- if (result === undefined) { return }
10
- console.log(result[0])
11
- i = result[1]
12
- }
13
- }
14
-
15
- {
16
- const big = list.fromArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 42, 60])
17
- const list0 = list.fromArray([0, 1, 2, 3])
18
- const list1 = list.flatMap(x => list.fromArray([x, x * 2, x * 3]))(list0)
19
- const list2 = list.concat(list0)(list0)
20
- const list3 = list.inclusiveScan(sum)(list0)
21
- const r = list.find(x => x === 42)(big)
22
- {
23
- let x = big
24
- for (let i = 0; i < 1_000_000; ++i) {
25
- x = list.concat(list.empty)(x)
26
- }
27
- const r = list.get(x)
28
- print(x)
29
- }
30
- {
31
- let x = big
32
- for (let i = 0; i < 1_000_000; ++i) {
33
- x = list.concat(x)(list.one(i))
34
- }
35
- const r = list.get(x)
36
- // print(x)
37
- }
38
- }