functionalscript 0.0.215 → 0.0.219

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
@@ -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/functionscript
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 seq.list(...node) }
312
+ case 1: case 2: { return node }
313
313
  case 3: {
314
314
  return seq.concat(
315
315
  values(node[0]),
316
- seq.list(node[1]),
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
- seq.list(node[1]),
323
+ [node[1]],
324
324
  values(node[2]),
325
- seq.list(node[3]),
325
+ [node[3]],
326
326
  values(node[4])
327
327
  )
328
328
  }
package/btree/test.js CHANGED
@@ -25,7 +25,7 @@ const test = () => {
25
25
  /** @type {import('../sequence').Result<string>} */
26
26
  let _item = list.next(values(_map))
27
27
  while (_item !== undefined) {
28
- _item = list.next(_item[1])
28
+ _item = list.next(_item.tail)
29
29
  }
30
30
  }
31
31
  }
package/function/index.js CHANGED
@@ -4,8 +4,12 @@
4
4
  * @typedef {(_: I) => O} Func
5
5
  */
6
6
 
7
- /** @type {<X, O>(f: Func<X, O>) => <I>(g: Func<I, X>) => Func<I, O>} */
8
- const compose = f => g => x => f(g(x))
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: readonly string[]) => (src: Json|undefined) => Json} */
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 [name, tail] = result
25
- return { ...srcObject, [name]: f(tail)(object.at(name)(srcObject)) }
26
- }
27
- return compose(f)(array.sequence)
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 => seq.list(JSON.stringify(input))
31
+ const stringSerialize = input => [JSON.stringify(input)]
32
32
 
33
33
  /** @type {(_: number) => seq.Sequence<string>} */
34
- const numberSerialize = input => seq.list(JSON.stringify(input))
34
+ const numberSerialize = input => [JSON.stringify(input)]
35
35
 
36
- const nullSerialize = seq.list('null')
36
+ const nullSerialize = ['null']
37
37
 
38
- const trueSerialize = seq.list('true')
38
+ const trueSerialize = ['true']
39
39
 
40
- const falseSerialize = seq.list('false')
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 = seq.list(':')
46
- const comma = seq.list(',')
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 {op.Scan<seq.Sequence<string>, seq.Sequence<string>>} */
52
- const joinScan = value => [value, commaValue]
51
+ /** @type {seq.FoldOperator<seq.Sequence<string>>} */
52
+ const joinOp = a => b => seq.concat(a, comma, b)
53
53
 
54
- /** @type {seq.SequenceMap<seq.Sequence<string>, string>} */
55
- const join = input => {
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 = seq.list(open)
63
- const seqClose = seq.list(close)
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 = object.entries(input)
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 sequence = array.sequence(input)
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 => seq.join('')(serialize(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 x = json.stringify(sort)(json.addProperty("Hello")(['a'])({}))
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: () => undefined
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: seq.empty,
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
- return seq.fold(setOperator)(/** @type {Map<T>} */(empty))(entries)
76
+ /** @type {Map<T>} */
77
+ const init = empty
78
+ return seq.reduce(setOperator)(init)(entries)
77
79
  }
78
80
 
79
81
  module.exports = {
@@ -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) => (_: Path) => Module|undefined} */
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
- /** @type {(_: Path) => readonly [string, Path]|undefined} */
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.215",
3
+ "version": "0.0.219",
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.9",
22
+ "@types/node": "^16.11.11",
23
23
  "typescript": "^4.5.2"
24
24
  }
25
25
  }
@@ -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
@@ -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
  }
@@ -69,7 +69,7 @@ const reduce = ([first, s]) => async c => {
69
69
 
70
70
  const sum = reduce(seq.sum)
71
71
 
72
- const join = compose(reduce)(seq.join)
72
+ const join = compose(seq.join)(reduce)
73
73
 
74
74
  const length = reduce(seq.length)
75
75
 
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 {() => Result<T>} SequenceFn
7
+ * @typedef { readonly T[] | Thunk<T> } Sequence<T>
9
8
  */
10
9
 
11
10
  /**
12
11
  * @template T
13
- * @typedef {readonly [Sequence<T>, Sequence<T>]} Concat
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 { SequenceFn<T> | Concat<T>} Sequence
17
+ * @typedef { Result<T> | Concat<T> } Node<T>
26
18
  */
27
19
 
28
20
  /**
29
21
  * @template T
30
- * @typedef {FirstAndTail<T>|undefined} Result<T>
22
+ * @typedef { readonly[Sequence<T>, Sequence<T>] } Concat<T>
31
23
  */
32
24
 
33
25
  /**
34
26
  * @template T
35
- * @typedef {readonly[T, Sequence<T>]} FirstAndTail
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>(first: T) => (tail: Sequence<T>) => Sequence<T>} */
41
- const sequence = first => tail => () => [first, tail]
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 {<F, T>(a: readonly[F, Sequence<T>]) => (b: Sequence<T>) => readonly[F, Sequence<T>]} */
44
- const norm = ([a0, a1]) => b => [a0, [a1, b]]
43
+ /** @type {<T>(sequence: Sequence<T>) => Node<T>} */
44
+ const node = sequence => sequence instanceof Array ? fromArray(sequence) : sequence()
45
45
 
46
- /** @type {<T>(input: Sequence<T>) => Result<T>} */
47
- const next = input => {
48
- let i = input
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
- if (typeof i === 'function') { return i() }
51
- const [a, b] = i
52
- if (typeof a === 'function') {
53
- const result = a()
54
- if (result !== undefined) {
55
- return norm(result)(b)
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>(input: Sequence<T>) => T|undefined} */
65
- const first = input => {
66
- const result = next(input)
67
- if (result === undefined) { return undefined }
68
- return result[0]
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
- * @template T
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
- * @template T
79
- * @template R
80
- * @typedef {(list: Sequence<T>) => R} SequenceReduce
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
- * Note: the operation is not lazy. It traverses the given array and creates a linked list.
85
- *
86
- * @type {<T>(...array: readonly T[]) => Sequence<T>}
87
- */
88
- const list = (...array) => {
89
- /** @typedef {typeof array extends readonly(infer T)[] ? T : never} T */
90
- let i = array.length
91
- /** @type {Sequence<T>} */
92
- let iResult = empty
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 {<T>(...array: readonly Sequence<T>[]) => Sequence<T>} */
101
- const concat = (...array) => {
102
- let i = array.length
103
- if (i == 0) { return empty }
104
- i = i - 1
105
- let result = array[i]
106
- while (i !== 0) {
107
- i = i - 1
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 {(_: number) => Sequence<number>} */
114
- const generate = n => {
115
- /** @type {(_: number) => Sequence<number>} */
116
- const f = i => () => {
117
- if (n <= i) { return undefined }
118
- return [i, f(i + 1)]
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, R>(f: (value: T) => Sequence<R>) => SequenceMap<T, R>} */
124
- const flatMap = f => input => () => {
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 [first, tail] = result
130
- const firstResult = next(f(first))
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>(list: Sequence<Sequence<T>>) => Sequence<T>} */
139
- const flat = flatMap(i => i)
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
- return result
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, R>(s: seqOp.ExclusiveScan<T, R>) => SequenceMap<T, R>} */
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[0]
181
- i = result[1]
175
+ r = result.first
176
+ i = result.tail
182
177
  }
183
178
  }
184
179
 
185
- /** @type {<T, R>(s: seqOp.ExclusiveScan<T, R>) => (input: Sequence<T>) => R} */
186
- const reduce = ([first, s]) => input => last(first)(scan(s)(input))
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, R>(ro: op.ReduceOperator<R, T>) => (first: R) => (input: Sequence<T>) => R} */
189
- const fold = ro => first => reduce(seqOp.exclusiveScan(ro)(first))
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
- const entries = scan(seqOp.entries)
186
+ /** @type {<T>(f: (value: T) => boolean) => (sequence: Sequence<T>) => boolean} */
187
+ const every = f => input => !some(compose(f)(logicalNot))(input)
192
188
 
193
- const sum = reduce(seqOp.sum)
189
+ /** @type {<T>(value: T) => (sequence: Sequence<T>) => boolean} */
190
+ const includes = value => some(strictEqual(value))
194
191
 
195
- const length = reduce(seqOp.length)
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
- /** @type {(separator: string) => SequenceReduce<string, string>} */
198
- const join = separator => reduce(seqOp.join(separator))
199
+ /**
200
+ * @template T,A
201
+ * @typedef {(value: T) => ScanState<T, A>} ScanFunc
202
+ */
199
203
 
200
- /** @type {<T>(f: (value: T) => boolean) => SequenceMap<T, T>} */
201
- const takeWhile = f => input => () => {
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 || !f(result[0])) { return undefined }
204
- return result
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 {(n: number) => <T>(input: Sequence<T>) => Sequence<T>} */
208
- const drop = n => input => () => {
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
- /** @type {(n: number) => <T>(input: Sequence<T>) => T|undefined} */
220
- const at = n => input => first(drop(n)(input))
221
+ /**
222
+ * @template T,A
223
+ * @typedef {(prior: A) => (value: T) => A} ReduceOperator
224
+ */
221
225
 
222
- /** @type {<T>(f: (value: T) => boolean) => SequenceReduce<T, T|undefined>} */
223
- const find = f => input => first(filter(f)(input))
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>(f: (value: T) => boolean) => SequenceReduce<T, boolean>} */
226
- const some = f => input => find(x => x)(map(f)(input)) !== undefined
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>(value: T) => SequenceReduce<T, boolean>} */
229
- const includes = value => some(strictEqual(value))
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
- /** @type {<T>(f: (value: T) => boolean) => SequenceReduce<T, boolean>} */
232
- const every = f => input => !some(compose(logicalNot)(f))(input)
238
+ /**
239
+ * @template T
240
+ * @typedef {ReduceOperator<T, T>} FoldOperator
241
+ */
233
242
 
234
- /** @type {<T>(list: Sequence<T>) => Iterable<T>} */
235
- const iterable = list => ({
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
- /** @type {<T>(list: Sequence<T>) => AsyncIterable<T>} */
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 {<A>(a: Sequence<A>) => <B>(b: Sequence<B>) => Sequence<readonly[A, B]>} */
261
- const zip = a => b => () => {
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
- /** @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
- }
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
- next,
264
+ empty,
286
265
  /** @readonly */
287
- list,
266
+ iterable,
288
267
  /** @readonly */
289
- empty,
268
+ next,
290
269
  /** @readonly */
291
- sequence,
270
+ toArray,
292
271
  /** @readonly */
293
- at,
272
+ flat,
294
273
  /** @readonly */
295
- concat,
274
+ last,
296
275
  /** @readonly */
297
- generate,
276
+ concat,
298
277
  /** @readonly */
299
278
  first,
300
279
  /** @readonly */
301
- iterable,
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
- filterMap,
314
- /** @readonly */
315
- scan,
316
- /** @readonly */
317
- exclusiveScan,
318
- /** @readonly */
319
- last,
286
+ find,
320
287
  /** @readonly */
321
- fold,
288
+ some,
322
289
  /** @readonly */
323
- reduce,
290
+ every,
324
291
  /** @readonly */
325
- entries,
292
+ includes,
326
293
  /** @readonly */
327
- sum,
294
+ takeWhile,
328
295
  /** @readonly */
329
- join,
296
+ dropWhile,
330
297
  /** @readonly */
331
- length,
298
+ scanFunc,
332
299
  /** @readonly */
333
- drop,
300
+ scanState,
334
301
  /** @readonly */
335
- find,
302
+ scan,
336
303
  /** @readonly */
337
- takeWhile,
304
+ reduce,
338
305
  /** @readonly */
339
- some,
306
+ fold,
340
307
  /** @readonly */
341
- every,
308
+ sum,
342
309
  /** @readonly */
343
- includes,
310
+ join,
344
311
  /** @readonly */
345
- zip,
312
+ entries,
346
313
  /** @readonly */
347
- reverse,
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(reduce)(seq.join)
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.find(x => x !== undefined))
38
- (i.map(x => file(x())))
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 seq = require('.')
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 { identity } = require('../function')
7
-
8
- /** @type {<T>(input: seq.Sequence<T>) => void} */
9
- const print = input => {
10
- let i = input
11
- while (true) {
12
- const result = next(i)
13
- if (result === undefined) { return }
14
- console.log(result[0])
15
- i = result[1]
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 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' }
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
- const a = map(generate)(generate(100_000))
33
- const ar = array.fromSequence(a)
34
- // This operation uses a lot of stack because `...`
35
- // puts array items on a stack.
36
- // Use `array.sequence` instead
37
- const x = concat(...ar)
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
- const a = array.fromSequence(generate(1_000_000))
43
- let x = concat(array.sequence(a), big)
44
- const r = next(x)
45
- // print(x)
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
- let x = big
49
- for (let i = 0; i < 1_000_000; ++i) {
50
- x = concat(empty, x)
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 r = next(x)
53
- // print(x)
124
+ const a = _.next(sequence)
54
125
  }
126
+
55
127
  {
56
- let x = big
57
- for (let i = 0; i < 1_000_000; ++i) {
58
- x = concat(x, list(i))
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 r = next(x)
61
- // print(x)
134
+ const a = _.next(sequence)
62
135
  }
63
136
  }
64
137
 
65
- {
66
- const x = seq.join(':')(seq.list("1", "2", "3", "4", "5", "6"))
67
- if (x !== "1:2:3:4:5:6") { throw x }
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
  {