functionalscript 0.0.214 → 0.0.218

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