functionalscript 0.0.173 → 0.0.180

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/btree/index.js CHANGED
@@ -336,7 +336,7 @@ const values = node => ({
336
336
  const flatArray = (...array) => list.flat(list.fromArray(array))
337
337
 
338
338
  /** @type {<T>(node: Node<T>) => list.List<T>} */
339
- const valuesList = node => {
339
+ const valuesList = node => () => {
340
340
  const f = () => {
341
341
  switch (node.length) {
342
342
  case 1: case 2: { return list.fromArray(node) }
@@ -358,7 +358,7 @@ const valuesList = node => {
358
358
  }
359
359
  }
360
360
  }
361
- return f()
361
+ return list.get(f())
362
362
  }
363
363
 
364
364
  module.exports = {
package/btree/test.js CHANGED
@@ -1,6 +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
5
 
5
6
  /** @type {(node: btree.Node<string>) => (value: string) => btree.Node<string>} */
6
7
  const set = node => value => {
@@ -21,10 +22,11 @@ const test = () => {
21
22
  //
22
23
  {
23
24
  /** @type {import('../sequence/list').Result<string>} */
24
- let result = valuesList(node)()
25
+ let result = list.get(valuesList(node))
25
26
  while (result !== undefined) {
26
- console.log(result[0])
27
- result = result[1]()
27
+ const t = result[0]
28
+ console.log(t)
29
+ result = list.get(result[1])
28
30
  }
29
31
  }
30
32
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.0.173",
3
+ "version": "0.0.180",
4
4
  "description": "FunctionalScript is a functional subset of JavaScript",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -12,7 +12,7 @@ Sequence types:
12
12
  See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
13
13
 
14
14
  - `length: Sequence<infer T> => number`
15
- - `at: number => Sequence<infer T> = T`
15
+ - `at: number => Sequence<infer T> = T|undefined`
16
16
  - `concat: Sequence<infer T> => Sequence<T> => Sequence<T>`
17
17
  - `entries: Sequence<infer T> => Sequence<[number, T]>`
18
18
  - `every: (infer T => boolean) => Sequence<T> => boolean`
@@ -24,7 +24,6 @@ See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Obj
24
24
  - `includes: infer T => Sequence<T> => boolean`
25
25
  - `indexOf: infer T => Sequence<T> => number`
26
26
  - `join: string => Sequence<string> => string`
27
- - `keys: Sequence<T> => Sequence<string>`
28
27
  - `lastIndexOf: infer T => Sequence<T> => number`
29
28
  - `map: (infer T => infer R) => Sequence<T> => Sequence<R>`
30
29
  - `reduce: ...Scan<T, R> => Sequence<T> => R`
@@ -38,6 +37,7 @@ See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Obj
38
37
 
39
38
  ### Priority 3.
40
39
 
40
+ - `keys: Sequence<T> => Sequence<string>`
41
41
  - `values: Sequence<infer T> => Sequence<T>`
42
42
 
43
43
  ## Prohibited Array Operations
@@ -30,6 +30,13 @@ const option = require('../../option')
30
30
  * @typedef {readonly [T, T, T]} Array3
31
31
  */
32
32
 
33
+ /**
34
+ * @template T0
35
+ * @template T1
36
+ * @template T2
37
+ * @typedef {readonly [T0, T1, T2]} Tuple3
38
+ */
39
+
33
40
  /** @typedef {0|1|2} Index3 */
34
41
 
35
42
  /**
@@ -85,8 +92,8 @@ const splitLast = a => {
85
92
  return option.map(split)(last(a))
86
93
  }
87
94
 
88
- /** @type {<T>(a: Array<T>) => (index: number) => readonly[T]|undefined} */
89
- const at = a => index => index < a.length ? [a[index]] : undefined
95
+ /** @type {(index: number) => <T>(a: Array<T>) => readonly[T]|undefined} */
96
+ const at = index => a => index < a.length ? [a[index]] : undefined
90
97
 
91
98
  module.exports = {
92
99
  /** @readonly */
@@ -56,12 +56,12 @@ const scan = s => c => ({
56
56
  })
57
57
 
58
58
  /** @type {<T, R>(s: seq.InclusiveScan<T, R>) => (c: AsyncOrSyncIterable<T>) => AsyncIterable<R>} */
59
- const inclusiveScan = s => c => concat([s.first])(scan(s.scan)(c))
59
+ const inclusiveScan = ([first, s]) => c => concat([first])(scan(s)(c))
60
60
 
61
61
  /** @type {<T, R>(is: seq.InclusiveScan<T, R>) => (c: AsyncOrSyncIterable<T>) => Promise<R>} */
62
- const reduce = is => async c => {
63
- let next = is.first
64
- for await (const i of scan(is.scan)(c)) {
62
+ const reduce = ([first, s]) => async c => {
63
+ let next = first
64
+ for await (const i of scan(s)(c)) {
65
65
  next = i
66
66
  }
67
67
  return next
@@ -71,7 +71,7 @@ const sum = reduce(seq.sum)
71
71
 
72
72
  const join = pipe(seq.join)(reduce)
73
73
 
74
- const size = reduce(seq.size)
74
+ const length = reduce(seq.length)
75
75
 
76
76
  /** @type {<T>(f: (value: T) => boolean) => (c: AsyncOrSyncIterable<T>) => AsyncIterable<T>} */
77
77
  const takeWhile = f => c => ({
@@ -99,7 +99,7 @@ module.exports = {
99
99
  /** @readonly */
100
100
  sum,
101
101
  /** @readonly */
102
- size,
102
+ length,
103
103
  /** @readonly */
104
104
  join,
105
105
  /** @readonly */
package/sequence/index.js CHANGED
@@ -22,10 +22,7 @@ const { id } = require('../function')
22
22
  /**
23
23
  * @template T
24
24
  * @template R
25
- * @typedef {{
26
- * readonly scan: Scan<T, R>
27
- * readonly first: R
28
- * }} InclusiveScan
25
+ * @typedef {Tuple2<R, Scan<T, R>>} InclusiveScan
29
26
  */
30
27
 
31
28
  /** @type {<R, T>(operator: op.BinaryOperator<R, T>) => (prior: R) => Scan<T, R>} */
@@ -42,10 +39,7 @@ const scan = operator => {
42
39
  }
43
40
 
44
41
  /** @type {<R, T>(operator: op.BinaryOperator<R, T>) => (first: R) => InclusiveScan<T, R>} */
45
- const inclusiveScan = operator => first => ({
46
- scan: scan(operator)(first),
47
- first,
48
- })
42
+ const inclusiveScan = operator => first => [first, scan(operator)(first)]
49
43
 
50
44
  /**
51
45
  * @template T
@@ -58,14 +52,14 @@ const createEntries = index => value => [[index, value], createEntries(index + 1
58
52
  const entries = createEntries(0)
59
53
 
60
54
  /** @type {(separator: string) => InclusiveScan<string, string>} */
61
- const join = separator => ({
62
- scan: value => [value, scan(op.join(separator))(value)],
63
- first: ''
64
- })
55
+ const join = separator => ['', value => [value, scan(op.join(separator))(value)]]
65
56
 
66
57
  const sum = inclusiveScan(op.addition)(0)
67
58
 
68
- const size = inclusiveScan(a => () => a + 1)(0)
59
+ /** @type {(a: number) => () => number} */
60
+ const counter = a => () => a + 1
61
+
62
+ const length = inclusiveScan(counter)(0)
69
63
 
70
64
  /**
71
65
  * @template T
@@ -89,7 +83,7 @@ module.exports = {
89
83
  /** @readonly */
90
84
  sum,
91
85
  /** @readonly */
92
- size,
86
+ length,
93
87
  /** @readonly */
94
88
  entries,
95
89
  /** @readonly */
@@ -22,12 +22,12 @@ const scan = s => c => ({
22
22
  })
23
23
 
24
24
  /** @type {<T, R>(s: seq.InclusiveScan<T, R>) => (c: Iterable<T>) => Iterable<R>} */
25
- const inclusiveScan = s => c => concat([s.first])(scan(s.scan)(c))
25
+ const inclusiveScan = ([first, s]) => c => concat([first])(scan(s)(c))
26
26
 
27
27
  /** @type {<T, R>(s: seq.InclusiveScan<T, R>) => (c: Iterable<T>) => R} */
28
- const reduce = s => c => {
29
- let next = s.first
30
- for (const i of scan(s.scan)(c)) {
28
+ const reduce = ([first, s]) => c => {
29
+ let next = first
30
+ for (const i of scan(s)(c)) {
31
31
  next = i
32
32
  }
33
33
  return next
@@ -37,7 +37,7 @@ const entries = scan(seq.entries)
37
37
 
38
38
  const sum = reduce(seq.sum)
39
39
 
40
- const size = reduce(seq.size)
40
+ const length = reduce(seq.length)
41
41
 
42
42
  const join = pipe(seq.join)(reduce)
43
43
 
@@ -87,7 +87,7 @@ module.exports = {
87
87
  /** @readonly */
88
88
  sum,
89
89
  /** @readonly */
90
- size,
90
+ length,
91
91
  /** @readonly */
92
92
  entries,
93
93
  /** @readonly */
@@ -1,9 +1,28 @@
1
1
  const array = require('../array')
2
2
  const option = require('../../option')
3
+ const base = require('..')
4
+ const { pipe } = require('../../function')
5
+ const { todo } = require('../../dev')
3
6
 
4
7
  /**
5
8
  * @template T
6
- * @typedef {() => Result<T>} List
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
7
26
  */
8
27
 
9
28
  /**
@@ -18,76 +37,149 @@ const option = require('../../option')
18
37
 
19
38
  const empty = () => undefined
20
39
 
40
+ /** @type {<T>(list: List<T>) => Result<T>} */
41
+ const get = list => {
42
+ let i = list
43
+ while (true) {
44
+ if (typeof i === 'function') { return i() }
45
+ const [a, b] = i
46
+ if (typeof a === 'function') {
47
+ const result = a()
48
+ if (result !== undefined) {
49
+ return [result[0], [result[1], b]]
50
+ }
51
+ i = b
52
+ } else {
53
+ i = [a[0], [a[1], b]]
54
+ }
55
+ }
56
+ }
57
+
21
58
  /**
22
59
  * @template T
23
60
  * @template R
24
- * @typedef {(list: List<T>) => List<R>} Map
61
+ * @typedef {(list: List<T>) => List<R>} ListMap
25
62
  */
26
63
 
27
- /** @type {<T>(first: T) => Map<T, T>} */
28
- const list = first => tail => () => [first, tail]
29
-
30
64
  /** @type {<T>(first: T) => List<T>} */
31
- const one = first => list(first)(empty)
65
+ const one = first => () => [first, empty]
32
66
 
33
67
  /** @type {<T>(array: array.Array<T>) => List<T>} */
34
68
  const fromArray = a => {
35
69
  /** @typedef {typeof a extends array.Array<infer T> ? T : never} T */
36
70
  /** @type {(index: number) => List<T>} */
37
- const listFrom = index => {
38
- /** @type {(value: readonly [T]) => Result<T>} */
39
- const result = ([value]) => list(value)(listFrom(index + 1))()
40
- return () => option.map(result)(array.at(a)(index))
71
+ const at = index => () => {
72
+ const result = array.at(index)(a)
73
+ if (result === undefined) { return undefined }
74
+ return [result[0], at(index + 1)]
41
75
  }
42
- return listFrom(0)
76
+ return at(0)
43
77
  }
44
78
 
45
- /** @type {<T>(list0: List<T>) => Map<T, T>} */
46
- const concat = list0 => list1 => () => {
47
- /** @typedef {typeof list0 extends List<infer T> ? T : never} T */
48
- /** @type {(firstAntTail: FirstAndTail<T>) => Result<T>} */
49
- const result = ([first, tail]) => [first, concat(tail)(list1)]
50
- return option.map(result)(list0())
51
- }
79
+ /** @type {<T>(list0: List<T>) => ListMap<T, T>} */
80
+ const concat = a => b => [a, b]
52
81
 
53
- /** @type {<T, R>(f: (value: T) => List<R>) => Map<T, R>} */
54
- const flatMap = f => {
55
- /** @typedef {typeof f extends (value: infer T) => List<infer R> ? [T, R] : never} TR */
56
- /** @typedef {TR[0]} T */
57
- /** @typedef {TR[1]} R */
58
- /** @type {(firstAntTail: FirstAndTail<T>) => Result<R>} */
59
- const result = ([first, tail]) => concat(f(first))(map(tail))()
60
- /** @type {(list: List<T>) => List<R>} */
61
- const map = list => () => option.map(result)(list())
62
- return map
82
+ /** @type {<T, R>(f: (value: T) => List<R>) => ListMap<T, R>} */
83
+ const flatMap = f => input => () => {
84
+ let i = input
85
+ while (true) {
86
+ const result = get(i)
87
+ if (result === undefined) { return undefined }
88
+ const [first, tail] = result
89
+ const firstResult = get(f(first))
90
+ if (firstResult !== undefined) {
91
+ const [firstFirst, firstTail] = firstResult
92
+ return [firstFirst, concat(firstTail)(flatMap(f)(tail))]
93
+ }
94
+ i = tail
95
+ }
63
96
  }
64
97
 
65
98
  /** @type {<T>(list: List<List<T>>) => List<T>} */
66
99
  const flat = flatMap(i => i)
67
100
 
68
- /** @type {<T, R>(f: (value: T) => R) => Map<T, R>} */
101
+ /** @type {<T, R>(f: (value: T) => R) => ListMap<T, R>} */
69
102
  const map = f => flatMap(i => one(f(i)))
70
103
 
71
- /** @type {<T>(f: (value: T) => boolean) => Map<T, T>} */
104
+ /** @type {<T>(f: (value: T) => boolean) => ListMap<T, T>} */
72
105
  const filter = f => flatMap(i => f(i) ? one(i) : empty)
73
106
 
107
+ /** @type {<T, R>(s: base.Scan<T, R>) => ListMap<T, R>} */
108
+ const scan = s => input => () => {
109
+ /** @typedef {typeof s extends base.Scan<infer T, infer R> ? [T, R] : never} TR */
110
+ /** @typedef {TR[0]} T */
111
+ /** @typedef {TR[1]} R */
112
+ /** @type {(firstAndTail: FirstAndTail<T>) => Result<R>} */
113
+ const defined = ([first, tail]) => {
114
+ const [newFirst, newS] = s(first)
115
+ return [newFirst, scan(newS)(tail)]
116
+ }
117
+ return option.map(defined)(get(input))
118
+ }
119
+
120
+ /** @type {<T, R>(s: base.InclusiveScan<T, R>) => ListMap<T, R>} */
121
+ const inclusiveScan = ([first, s]) => input => () => [first, scan(s)(input)]
122
+
123
+ /** @type {<T>(def: T) => (input: List<T>) => T} */
124
+ const last = def => input => {
125
+ let r = def
126
+ let i = input
127
+ while (true) {
128
+ const result = get(i)
129
+ if (result === undefined) {
130
+ return r
131
+ }
132
+ r = result[0]
133
+ i = result[1]
134
+ }
135
+ return r
136
+ }
137
+
138
+ /** @type {<T, R>(s: base.InclusiveScan<T, R>) => (input: List<T>) => R} */
139
+ const reduce = ([first, s]) => input => last(first)(scan(s)(input))
140
+
141
+ const entries = scan(base.entries)
142
+
143
+ const sum = reduce(base.sum)
144
+
145
+ const length = reduce(base.length)
146
+
147
+ const join = pipe(base.join)(reduce)
148
+
149
+ /** @type {<T>(f: (value: T) => boolean) => ListMap<T, T>} */
150
+ const takeWhile = f => input => () => {
151
+ const result = get(input)
152
+ if (result === undefined || !f(result[0])) { return undefined }
153
+ return result
154
+ }
155
+
156
+ /** @type {<T>(f: (value: T) => boolean) => (input: List<T>) => T|undefined} */
157
+ const find = f => input => {
158
+ /** @typedef {typeof f extends (value: infer T) => boolean ? T : never} T */
159
+ /** @type {(result: FirstAndTail<T>) => T} */
160
+ const defined = ([first]) => first
161
+ return option.map(defined)(get(filter(f)(input)))
162
+ }
163
+
74
164
  /**
75
165
  * Note: probably, it's possible to implement using the `scan` concept.
76
166
  * @type {<T>(list: List<T>) => Iterable<T>}
77
167
  */
78
168
  const iterable = list => ({
79
169
  *[Symbol.iterator]() {
80
- let result = list()
81
- while (result !== undefined) {
170
+ let i = list
171
+ while (true) {
172
+ const result = get(i)
173
+ if (result === undefined) { return }
82
174
  yield result[0]
83
- result = result[1]()
175
+ i = result[1]
84
176
  }
85
177
  }
86
178
  })
87
179
 
88
180
  module.exports = {
89
181
  /** @readonly */
90
- list,
182
+ get,
91
183
  /** @readonly */
92
184
  one,
93
185
  /** @readonly */
@@ -106,4 +198,22 @@ module.exports = {
106
198
  map,
107
199
  /** @readonly */
108
200
  filter,
201
+ /** @readonly */
202
+ scan,
203
+ /** @readonly */
204
+ inclusiveScan,
205
+ /** @readonly */
206
+ last,
207
+ /** @readonly */
208
+ reduce,
209
+ /** @readonly */
210
+ entries,
211
+ /** @readonly */
212
+ sum,
213
+ /** @readonly */
214
+ join,
215
+ /** @readonly */
216
+ length,
217
+ /** @readonly */
218
+ find,
109
219
  }
@@ -0,0 +1,38 @@
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
+ }
package/test.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const i = require('./')
2
2
 
3
+ require('./sequence/list/test')
3
4
  require('./btree/test')
4
5
  require('./sequence/iterable/test')
5
6
  require('./sequence/asyncIterable/test')