functionalscript 0.0.176 → 0.0.182

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
@@ -358,7 +358,7 @@ const valuesList = node => () => {
358
358
  }
359
359
  }
360
360
  }
361
- return f()()
361
+ return list.next(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,11 +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.next(valuesList(node))
25
26
  while (result !== undefined) {
26
27
  const t = result[0]
27
28
  console.log(t)
28
- result = result[1]()
29
+ result = list.next(result[1])
29
30
  }
30
31
  }
31
32
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.0.176",
3
+ "version": "0.0.182",
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
  /**
package/sequence/index.js CHANGED
@@ -56,7 +56,10 @@ const join = separator => ['', value => [value, scan(op.join(separator))(value)]
56
56
 
57
57
  const sum = inclusiveScan(op.addition)(0)
58
58
 
59
- const length = inclusiveScan(a => () => a + 1)(0)
59
+ /** @type {(a: number) => () => number} */
60
+ const counter = a => () => a + 1
61
+
62
+ const length = inclusiveScan(counter)(0)
60
63
 
61
64
  /**
62
65
  * @template T
@@ -6,7 +6,25 @@ const { todo } = require('../../dev')
6
6
 
7
7
  /**
8
8
  * @template T
9
- * @typedef {() => Result<T>} List
9
+ * @typedef {() => Result<T>} ListFunc
10
+ */
11
+
12
+ /**
13
+ * @template T
14
+ * @typedef {readonly [List<T>, List<T>]} Concat
15
+ */
16
+
17
+ /**
18
+ * Please note that the list also contains `Concat<T>. We need this as
19
+ * a workaround because modern JavaScript implementations don't support
20
+ * ES6 TCO (Tail Call Optimization).
21
+ *
22
+ * Without this wotkaround we may have a stack overflow if a list
23
+ * contains a lot of concateneted lists. Use `next` function to extract
24
+ * a list.
25
+ *
26
+ * @template T
27
+ * @typedef { ListFunc<T> | Concat<T>} List
10
28
  */
11
29
 
12
30
  /**
@@ -21,46 +39,59 @@ const { todo } = require('../../dev')
21
39
 
22
40
  const empty = () => undefined
23
41
 
42
+ /** @type {<F, T>(a: readonly[F, List<T>]) => (b: List<T>) => readonly[F, List<T>]} */
43
+ const norm = ([a0, a1]) => b => [a0, [a1, b]]
44
+
45
+ /** @type {<T>(list: List<T>) => Result<T>} */
46
+ const next = list => {
47
+ let i = list
48
+ while (true) {
49
+ if (typeof i === 'function') { return i() }
50
+ const [a, b] = i
51
+ if (typeof a === 'function') {
52
+ const result = a()
53
+ if (result !== undefined) {
54
+ return norm(result)(b)
55
+ }
56
+ i = b
57
+ } else {
58
+ i = norm(a)(b)
59
+ }
60
+ }
61
+ }
62
+
24
63
  /**
25
64
  * @template T
26
65
  * @template R
27
66
  * @typedef {(list: List<T>) => List<R>} ListMap
28
67
  */
29
68
 
30
- /** @type {<T>(first: T) => ListMap<T, T>} */
31
- const list = first => tail => () => [first, tail]
32
-
33
69
  /** @type {<T>(first: T) => List<T>} */
34
- const one = first => list(first)(empty)
70
+ const one = first => () => [first, empty]
35
71
 
36
72
  /** @type {<T>(array: array.Array<T>) => List<T>} */
37
73
  const fromArray = a => {
38
74
  /** @typedef {typeof a extends array.Array<infer T> ? T : never} T */
39
75
  /** @type {(index: number) => List<T>} */
40
- const at = index => {
41
- /** @type {(value: readonly [T]) => Result<T>} */
42
- const result = ([value]) => list(value)(at(index + 1))()
43
- return () => option.map(result)(array.at(index)(a))
76
+ const at = index => () => {
77
+ const result = array.at(index)(a)
78
+ if (result === undefined) { return undefined }
79
+ return [result[0], at(index + 1)]
44
80
  }
45
81
  return at(0)
46
82
  }
47
83
 
48
84
  /** @type {<T>(list0: List<T>) => ListMap<T, T>} */
49
- const concat = a => b => () => {
50
- /** @typedef {typeof a extends List<infer T> ? T : never} T */
51
- /** @type {(firstAntTail: FirstAndTail<T>) => Result<T>} */
52
- const defined = ([first, tail]) => [first, concat(tail)(b)]
53
- return option.match(defined)(b)(a())
54
- }
85
+ const concat = a => b => [a, b]
55
86
 
56
87
  /** @type {<T, R>(f: (value: T) => List<R>) => ListMap<T, R>} */
57
88
  const flatMap = f => input => () => {
58
89
  let i = input
59
90
  while (true) {
60
- const result = i()
91
+ const result = next(i)
61
92
  if (result === undefined) { return undefined }
62
93
  const [first, tail] = result
63
- const firstResult = f(first)()
94
+ const firstResult = next(f(first))
64
95
  if (firstResult !== undefined) {
65
96
  const [firstFirst, firstTail] = firstResult
66
97
  return [firstFirst, concat(firstTail)(flatMap(f)(tail))]
@@ -80,33 +111,35 @@ const filter = f => flatMap(i => f(i) ? one(i) : empty)
80
111
 
81
112
  /** @type {<T, R>(s: base.Scan<T, R>) => ListMap<T, R>} */
82
113
  const scan = s => input => () => {
83
- /** @typedef {typeof s extends base.Scan<infer T, infer R> ? [T, R] : never} TR */
84
- /** @typedef {TR[0]} T */
85
- /** @typedef {TR[1]} R */
86
- /** @type {(firstAndTail: FirstAndTail<T>) => Result<R>} */
87
- const defined = ([first, tail]) => {
88
- const [newFirst, newS] = s(first)
89
- return [newFirst, scan(newS)(tail)]
114
+ const result = next(input)
115
+ if (result === undefined) {
116
+ return result
90
117
  }
91
- return option.map(defined)(input())
118
+ const [first, tail] = result
119
+ const [newFirst, newS] = s(first)
120
+ return [newFirst, scan(newS)(tail)]
92
121
  }
93
122
 
94
123
  /** @type {<T, R>(s: base.InclusiveScan<T, R>) => ListMap<T, R>} */
95
- const inclusiveScan = ([first, s]) => input => list(first)(scan(s)(input))
124
+ const inclusiveScan = ([first, s]) => input => () => [first, scan(s)(input)]
96
125
 
97
126
  /** @type {<T>(def: T) => (input: List<T>) => T} */
98
127
  const last = def => input => {
99
- let i = input()
100
128
  let r = def
101
- while (i !== undefined) {
102
- r = i[0]
103
- i = i[1]()
129
+ let i = input
130
+ while (true) {
131
+ const result = next(i)
132
+ if (result === undefined) {
133
+ return r
134
+ }
135
+ r = result[0]
136
+ i = result[1]
104
137
  }
105
138
  return r
106
139
  }
107
140
 
108
141
  /** @type {<T, R>(s: base.InclusiveScan<T, R>) => (input: List<T>) => R} */
109
- const reduce = s => input => last(s[0])(inclusiveScan(s)(input))
142
+ const reduce = ([first, s]) => input => last(first)(scan(s)(input))
110
143
 
111
144
  const entries = scan(base.entries)
112
145
 
@@ -116,12 +149,18 @@ const length = reduce(base.length)
116
149
 
117
150
  const join = pipe(base.join)(reduce)
118
151
 
152
+ /** @type {<T>(f: (value: T) => boolean) => ListMap<T, T>} */
153
+ const takeWhile = f => input => () => {
154
+ const result = next(input)
155
+ if (result === undefined || !f(result[0])) { return undefined }
156
+ return result
157
+ }
158
+
119
159
  /** @type {<T>(f: (value: T) => boolean) => (input: List<T>) => T|undefined} */
120
160
  const find = f => input => {
121
- /** @typedef {typeof f extends (value: infer T) => boolean ? T : never} T */
122
- /** @type {(result: FirstAndTail<T>) => T} */
123
- const defined = ([first]) => first
124
- return option.map(defined)(filter(f)(input)())
161
+ const result = next(filter(f)(input))
162
+ if (result === undefined) { return undefined }
163
+ return result[0]
125
164
  }
126
165
 
127
166
  /**
@@ -130,17 +169,19 @@ const find = f => input => {
130
169
  */
131
170
  const iterable = list => ({
132
171
  *[Symbol.iterator]() {
133
- let result = list()
134
- while (result !== undefined) {
172
+ let i = list
173
+ while (true) {
174
+ const result = next(i)
175
+ if (result === undefined) { return }
135
176
  yield result[0]
136
- result = result[1]()
177
+ i = result[1]
137
178
  }
138
179
  }
139
180
  })
140
181
 
141
182
  module.exports = {
142
183
  /** @readonly */
143
- list,
184
+ next,
144
185
  /** @readonly */
145
186
  one,
146
187
  /** @readonly */
@@ -177,4 +218,6 @@ module.exports = {
177
218
  length,
178
219
  /** @readonly */
179
220
  find,
221
+ /** @readonly */
222
+ takeWhile,
180
223
  }
@@ -3,23 +3,36 @@ const { sum } = require('..')
3
3
 
4
4
  /** @type {<T>(l: list.List<T>) => void} */
5
5
  const print = a => {
6
- let i = a()
7
- while (i !== undefined) {
8
- console.log(i[0])
9
- i = i[1]()
6
+ let i = a
7
+ while (true) {
8
+ const result = list.next(i)
9
+ if (result === undefined) { return }
10
+ console.log(result[0])
11
+ i = result[1]
10
12
  }
11
13
  }
12
14
 
13
15
  {
14
16
  const big = list.fromArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 42, 60])
15
- // print(big)
16
- /*
17
17
  const list0 = list.fromArray([0, 1, 2, 3])
18
18
  const list1 = list.flatMap(x => list.fromArray([x, x * 2, x * 3]))(list0)
19
19
  const list2 = list.concat(list0)(list0)
20
20
  const list3 = list.inclusiveScan(sum)(list0)
21
- print(list3)
22
- */
23
21
  const r = list.find(x => x === 42)(big)
24
- console.log(r)
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.next(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.next(x)
36
+ // print(x)
37
+ }
25
38
  }