functionalscript 0.0.217 → 0.0.222

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/functionalscript
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.217",
3
+ "version": "0.0.222",
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,327 @@
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 | ResultOne<T> } Result
36
28
  */
37
29
 
38
- const empty = () => undefined
39
-
40
- /** @type {<T>(first: T) => (tail: Sequence<T>) => Sequence<T>} */
41
- const sequence = first => tail => () => [first, tail]
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]]
45
-
46
- /** @type {<T>(input: Sequence<T>) => Result<T>} */
47
- const next = input => {
48
- let i = input
49
- 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)
56
- }
57
- i = b
58
- } else {
59
- i = norm(a)(b)
60
- }
61
- }
62
- }
63
-
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]
69
- }
70
-
71
30
  /**
72
31
  * @template T
73
- * @template R
74
- * @typedef {(list: Sequence<T>) => Sequence<R>} SequenceMap
32
+ * @typedef {{ readonly first: T, readonly tail: Sequence<T> }} ResultOne
75
33
  */
76
34
 
77
- /**
78
- * @template T
79
- * @template R
80
- * @typedef {(list: Sequence<T>) => R} SequenceReduce
81
- */
35
+ const empty = () => undefined
82
36
 
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) => {
37
+ /** @type {<T>(array: readonly T[]) => Result<T>} */
38
+ const fromArray = array => {
89
39
  /** @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)
40
+ /** @type {(index: number) => Result<T>} */
41
+ const at = index => {
42
+ if (array.length <= index) { return undefined }
43
+ return { first: array[index], tail: () => at(index + 1) }
96
44
  }
97
- return iResult
45
+ return at(0)
98
46
  }
99
47
 
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]
48
+ /** @type {<T>(sequence: Sequence<T>) => Node<T>} */
49
+ const node = sequence => sequence instanceof Array ? fromArray(sequence) : sequence()
50
+
51
+ /** @type {<T>(concat: Concat<T>) => Sequence<T>} */
52
+ const concatNext = ([a, b]) => {
53
+ const result = node(a)
54
+ if (result === undefined) {
55
+ return b
56
+ } else if (result instanceof Array) {
57
+ const [aa, ab] = result
58
+ return () => [aa, () => [ab, b]]
59
+ } else {
60
+ const { first, tail } = result
61
+ return () => ({ first, tail: () => [tail, b] })
109
62
  }
110
- return result
111
63
  }
112
64
 
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)]
65
+ /** @type {<T>(sequence: Sequence<T>) => Result<T>} */
66
+ const next = sequence => {
67
+ let i = sequence
68
+ while (true) {
69
+ const n = node(i)
70
+ if (!(n instanceof Array)) { return n }
71
+ i = concatNext(n)
119
72
  }
120
- return f(0)
121
73
  }
122
74
 
123
- /** @type {<T, R>(f: (value: T) => Sequence<R>) => SequenceMap<T, R>} */
124
- const flatMap = f => input => () => {
125
- let i = input
126
- while (true) {
127
- const result = next(i)
128
- 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))
75
+ /** @type {<T>(sequence: Sequence<T>) => Iterable<T>} */
76
+ const iterable = sequence => ({
77
+ *[Symbol.iterator]() {
78
+ let i = sequence
79
+ while (true) {
80
+ if (i instanceof Array) { return yield *i }
81
+ const n = next(i)
82
+ if (n === undefined) { return }
83
+ const { first, tail } = n
84
+ yield first
85
+ i = tail
133
86
  }
134
- i = tail
135
87
  }
88
+ })
89
+
90
+ /** @type {<T>(sequence: Sequence<T>) => readonly T[]} */
91
+ const toArray = sequence => {
92
+ if (sequence instanceof Array) { return sequence }
93
+ return Array.from(iterable(sequence))
94
+ }
95
+
96
+ /** @type {<I, O>(f: (result: ResultOne<I>) => Node<O>) => (input: Sequence<I>) => Thunk<O>} */
97
+ const nextMap = f => input => () => {
98
+ const n = next(input)
99
+ if (n === undefined) { return undefined }
100
+ return f(n)
136
101
  }
137
102
 
138
- /** @type {<T>(list: Sequence<Sequence<T>>) => Sequence<T>} */
139
- const flat = flatMap(i => i)
103
+ /** @type {<T>(result: ResultOne<Sequence<T>>) => Node<T>} */
104
+ const flatFn = ({first, tail}) => [first, flat(tail)]
140
105
 
141
- /** @type {<T, R>(f: (value: T) => R) => SequenceMap<T, R>} */
142
- const map = f => flatMap(i => list(f(i)))
106
+ /** @type {<T>(sequence: Sequence<Sequence<T>>) => Thunk<T>} */
107
+ const flat = nextMap(flatFn)
143
108
 
144
- /** @type {<T>(f: (value: T) => boolean) => SequenceMap<T, T>} */
145
- const filter = f => flatMap(i => f(i) ? list(i) : empty)
109
+ /** @type {<T>(...array: readonly Sequence<T>[]) => Thunk<T>} */
110
+ const concat = (...array) => flat(array)
146
111
 
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)
112
+ /** @type {<I, O>(f: (value: I) => O) => (result: ResultOne<I>) => Node<O>} */
113
+ const mapFn = f => ({ first, tail }) => ({ first: f(first), tail: map(f)(tail) })
114
+
115
+ /** @type {<I, O>(f: (value: I) => O) => (input: Sequence<I>) => Thunk<O>} */
116
+ const map = f => nextMap(mapFn(f))
117
+
118
+ /** @type {<I, O>(f: (value: I) => Sequence<O>) => (input: Sequence<I>) => Thunk<O>} */
119
+ const flatMap = f => compose(map(f))(flat)
120
+
121
+ /** @type {<T>(f: (value: T) => boolean) => (result: ResultOne<T>) => Node<T>} */
122
+ const filterFn = f => ({ first, tail }) => {
123
+ const fTail = filter(f)(tail)
124
+ return f(first) ? { first, tail: fTail } : fTail()
125
+ }
126
+
127
+ /** @type {<T>(f: (value: T) => boolean) => (input: Sequence<T>) => Thunk<T>} */
128
+ const filter = f => nextMap(filterFn(f))
129
+
130
+ /** @type {<I, O>(f: (value: I) => O|undefined) => (result: ResultOne<I>) => Node<O>} */
131
+ const filterMapFn = f => ({first, tail}) => {
132
+ const fFirst = f(first)
133
+ const fTail = filterMap(f)(tail)
134
+ return fFirst === undefined ? fTail() : { first: fFirst, tail: fTail }
152
135
  }
153
136
 
154
- /** @type {<T, R>(f: (value: T) => R|undefined) => SequenceMap<T, R>} */
155
- const filterMap = f => flatMap(filterMapFunc(f))
137
+ /** @type {<I, O>(f: (value: I) => O|undefined) => (input: Sequence<I>) => Thunk<O>} */
138
+ const filterMap = f => nextMap(filterMapFn(f))
156
139
 
157
- /** @type {<T, R>(s: seqOp.Scan<T, R>) => SequenceMap<T, R>} */
158
- const scan = s => input => () => {
140
+ /** @type {<T>(f: (value: T) => boolean) => (input: Sequence<T>) => Thunk<T>} */
141
+ const takeWhile = f => input => () => {
159
142
  const result = next(input)
160
- if (result === undefined) {
161
- return result
143
+ if (result === undefined) { return undefined }
144
+ const { first, tail } = result
145
+ if (!f(first)) { return undefined }
146
+ return { first, tail: takeWhile(f)(result.tail) }
147
+ }
148
+
149
+ /** @type {<T>(f: (value: T) => boolean) => (input: Sequence<T>) => Thunk<T>} */
150
+ const dropWhile = f => input => () => {
151
+ let i = input
152
+ while (true) {
153
+ const result = next(i)
154
+ if (result === undefined) { return undefined }
155
+ const { first, tail } = result
156
+ if (!f(first)) return result
157
+ i = tail
162
158
  }
163
- const [first, tail] = result
164
- const [newFirst, newS] = s(first)
165
- return [newFirst, scan(newS)(tail)]
166
159
  }
167
160
 
168
- /** @type {<T, R>(s: seqOp.ExclusiveScan<T, R>) => SequenceMap<T, R>} */
169
- const exclusiveScan = ([first, s]) => input => () => [first, scan(s)(input)]
161
+ /** @type {<D>(def: D) => <T>(input: Sequence<T>) => D|T} */
162
+ const first = def => input => {
163
+ const result = next(input)
164
+ if (result === undefined) { return def }
165
+ return result.first
166
+ }
170
167
 
171
- /** @type {<T>(def: T) => (input: Sequence<T>) => T} */
168
+ /** @type {<D>(def: D) => <T>(input: Sequence<T>) => D|T} */
172
169
  const last = def => input => {
170
+ /** @typedef {typeof input extends Sequence<infer T> ? T : never} T */
171
+ /** @type {(typeof def)|T} */
173
172
  let r = def
174
- let i = input
173
+ let i = input
175
174
  while (true) {
176
175
  const result = next(i)
177
176
  if (result === undefined) {
178
177
  return r
179
178
  }
180
- r = result[0]
181
- i = result[1]
179
+ r = result.first
180
+ i = result.tail
182
181
  }
183
182
  }
184
183
 
185
- /** @type {<T, R>(s: seqOp.ExclusiveScan<T, R>) => (input: Sequence<T>) => R} */
186
- const reduce = ([first, s]) => input => last(first)(scan(s)(input))
184
+ /** @type {<D>(def: D) => <T>(f: (value: T) => boolean) => (sequence: Sequence<T>) => D|T} */
185
+ const find = def => f => input => first(def)(filter(f)(input))
187
186
 
188
- /** @type {<T, R>(operator: op.ReduceOperator<R, T>) => (first: R) => (input: Sequence<T>) => R} */
189
- const fold = operator => first => reduce(seqOp.exclusiveScan(operator)(first))
187
+ /** @type {<T>(f: (value: T) => boolean) => (sequence: Sequence<T>) => boolean} */
188
+ const some = f => input => find(false)(x => x)(map(f)(input))
190
189
 
191
- const entries = scan(seqOp.entries)
190
+ /** @type {<T>(f: (value: T) => boolean) => (sequence: Sequence<T>) => boolean} */
191
+ const every = f => input => !some(compose(f)(logicalNot))(input)
192
192
 
193
- const sum = reduce(seqOp.sum)
193
+ /** @type {<T>(value: T) => (sequence: Sequence<T>) => boolean} */
194
+ const includes = value => some(strictEqual(value))
194
195
 
195
- const length = reduce(seqOp.length)
196
+ /** @type {(count: number) => Thunk<number>} */
197
+ const countdown = count => () => {
198
+ if (count <= 0) { return undefined }
199
+ const first = count - 1
200
+ return { first, tail: countdown(first) }
201
+ }
196
202
 
197
- /** @type {(separator: string) => SequenceReduce<string, string>} */
198
- const join = separator => reduce(seqOp.join(separator))
203
+ /**
204
+ * @template T,A
205
+ * @typedef {(value: T) => ScanState<T, A>} ScanFunc
206
+ */
199
207
 
200
- /** @type {<T>(f: (value: T) => boolean) => SequenceMap<T, T>} */
201
- const takeWhile = f => input => () => {
208
+ /**
209
+ * @template T,A
210
+ * @typedef {readonly[A, ScanFunc<T, A>]} ScanState
211
+ */
212
+
213
+ /** @type {<T,A>(operator: ScanFunc<T, A>) => (input: Sequence<T>) => Thunk<A>} */
214
+ const scan = operator => input => () => {
202
215
  const result = next(input)
203
- if (result === undefined || !f(result[0])) { return undefined }
204
- return result
216
+ if (result === undefined) { return undefined }
217
+ const { first, tail } = result
218
+ const r = operator(first)
219
+ return { first: r[0], tail: scan(r[1])(tail) }
205
220
  }
206
221
 
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
- }
222
+ /** @type {<T,A>(operator: ScanFunc<T, A>) => <D>(def: D)=> (input: Sequence<T>) => D|A} */
223
+ const scanReduce = operator => def => input => last(def)(scan(operator)(input))
224
+
225
+ /**
226
+ * @template T,A
227
+ * @typedef {(prior: A) => (value: T) => A} ReduceOperator
228
+ */
229
+
230
+ /** @type {<T,A>(operator: ReduceOperator<T, A>) => (init: A) => ScanState<T, A>} */
231
+ const scanState = operator => init => [init, scanFunc(operator)(init)]
232
+
233
+ /** @type {<T,A>(operator: ReduceOperator<T, A>) => (init: A) => ScanFunc<T, A>} */
234
+ const scanFunc = operator => init => value => {
235
+ const result = operator(init)(value)
236
+ return scanState(operator)(result)
217
237
  }
218
238
 
219
- /** @type {(n: number) => <T>(input: Sequence<T>) => T|undefined} */
220
- const at = n => input => first(drop(n)(input))
239
+ /** @type {<T,A>(operator: ReduceOperator<T, A>) => (init: A) => (input: Sequence<T>) => A} */
240
+ const reduce = operator => init => scanReduce(scanFunc(operator)(init))(init)
221
241
 
222
- /** @type {<T>(f: (value: T) => boolean) => SequenceReduce<T, T|undefined>} */
223
- const find = f => input => first(filter(f)(input))
242
+ /**
243
+ * @template T
244
+ * @typedef {ReduceOperator<T, T>} FoldOperator
245
+ */
224
246
 
225
- /** @type {<T>(f: (value: T) => boolean) => SequenceReduce<T, boolean>} */
226
- const some = f => input => find(x => x)(map(f)(input)) !== undefined
247
+ /** @type {<T>(operator: FoldOperator<T>) => <D>(def: D) => (input: Sequence<T>) => D|T} */
248
+ const fold = operator => def => scanReduce(scanState(operator))(def)
227
249
 
228
- /** @type {<T>(value: T) => SequenceReduce<T, boolean>} */
229
- const includes = value => some(strictEqual(value))
250
+ const sum = fold(addition)(0)
230
251
 
231
- /** @type {<T>(f: (value: T) => boolean) => SequenceReduce<T, boolean>} */
232
- const every = f => input => !some(compose(logicalNot)(f))(input)
252
+ /** @type {(separator: string) => (input: Sequence<string>) => string} */
253
+ const join = separator => fold(op.join(separator))('')
233
254
 
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
- })
255
+ /** @type {(a: number) => () => number} */
256
+ const counter = a => () => a + 1
246
257
 
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
- })
258
+ /** @type {<T>(input: Sequence<T>) => number} */
259
+ const length = reduce(counter)(0)
259
260
 
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
- }
261
+ /**
262
+ * @template T
263
+ * @typedef {readonly[number, T]} Entry
264
+ */
268
265
 
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
- }
266
+ /** @type {(index: number) => <T>(value: T) => ScanState<T, Entry<T>>} */
267
+ const entryOp = index => value => [[index, value], entryOp(index + 1)]
268
+
269
+ /** @type {<T>(input: Sequence<T>) => Thunk<Entry<T>>} */
270
+ const entries = scan(entryOp(0))
282
271
 
283
272
  module.exports = {
284
273
  /** @readonly */
285
- next,
274
+ empty,
286
275
  /** @readonly */
287
- list,
276
+ iterable,
288
277
  /** @readonly */
289
- empty,
278
+ next,
279
+ /** @readonly */
280
+ toArray,
290
281
  /** @readonly */
291
- sequence,
282
+ flat,
292
283
  /** @readonly */
293
- at,
284
+ last,
294
285
  /** @readonly */
295
286
  concat,
296
287
  /** @readonly */
297
- generate,
298
- /** @readonly */
299
288
  first,
300
289
  /** @readonly */
301
- iterable,
302
- /** @readonly */
303
- asyncIterable,
290
+ map,
304
291
  /** @readonly */
305
292
  flatMap,
306
293
  /** @readonly */
307
- flat,
308
- /** @readonly */
309
- map,
310
- /** @readonly */
311
294
  filter,
312
295
  /** @readonly */
313
- filterMap,
314
- /** @readonly */
315
- scan,
316
- /** @readonly */
317
- exclusiveScan,
296
+ find,
318
297
  /** @readonly */
319
- last,
298
+ some,
320
299
  /** @readonly */
321
- fold,
300
+ every,
322
301
  /** @readonly */
323
- reduce,
302
+ includes,
324
303
  /** @readonly */
325
- entries,
304
+ takeWhile,
326
305
  /** @readonly */
327
- sum,
306
+ dropWhile,
328
307
  /** @readonly */
329
- join,
308
+ scanFunc,
330
309
  /** @readonly */
331
- length,
310
+ scanState,
332
311
  /** @readonly */
333
- drop,
312
+ scan,
334
313
  /** @readonly */
335
- find,
314
+ reduce,
336
315
  /** @readonly */
337
- takeWhile,
316
+ fold,
338
317
  /** @readonly */
339
- some,
318
+ sum,
340
319
  /** @readonly */
341
- every,
320
+ join,
342
321
  /** @readonly */
343
- includes,
322
+ entries,
344
323
  /** @readonly */
345
- zip,
324
+ length,
346
325
  /** @readonly */
347
- reverse,
348
- }
326
+ countdown,
327
+ }
@@ -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
  {
@@ -1,41 +0,0 @@
1
- # Operator
2
-
3
- ## Sequence
4
-
5
- ```ts
6
- type Sequence<T> = SubSequence<T, undefined>
7
- ```
8
-
9
- ## SubSequence
10
-
11
- ```ts
12
- type SubSequence<T, C> = () => SubSequenceResult<T, C>
13
- type SubSequenceResult<T, C> = [T, SubSequence<T, C>] | [C]
14
- ```
15
-
16
- ## The Main FlatScan Operator
17
-
18
- ```ts
19
- type FlatScanOperator<T, A> = (value: T) => SubSequence<A, FlatScanOp<T, A>>
20
-
21
- const flatScanConcat
22
- : SubSequence<A, FlatScanOp<T, A>> => Sequence<T> => Sequence<A>
23
- => a => b => () => {
24
- switch (next(a)) {
25
- case [first, tail]: { return [first, flatScanConcat(tail)(b)] }
26
- case [operator]: { return flatScan(operator)(b)() }
27
- }
28
- }
29
-
30
- const flatScan
31
- : FlatScanOperator<T, A> => Sequence<T> => Sequence<A>
32
- => operator => sequence => () => {
33
- // optimization for `takeWhile`, `find`
34
- if (operator === flatScanEmpty) { return [undefined] }
35
- //
36
- switch (next(s)) {
37
- case [first, tail]: { return flatScanConcat(operator(first))(tail)() }
38
- case [undefined]: { return [undefined] }
39
- }
40
- }
41
- ```