functionalscript 0.10.1 → 0.10.3

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/bnf/module.f.d.ts CHANGED
@@ -7,9 +7,21 @@ import { type Array2 } from '../types/array/module.f.ts';
7
7
  * - 0xEEEEEE is the last symbol (24 bits)
8
8
  */
9
9
  export type TerminalRange = number;
10
+ /**
11
+ * Full 24-bit symbol range packed into a single {@link TerminalRange}.
12
+ */
10
13
  export declare const fullRange: TerminalRange;
14
+ /**
15
+ * Unicode scalar value range packed into a single {@link TerminalRange}.
16
+ */
11
17
  export declare const unicodeRange: TerminalRange;
18
+ /**
19
+ * Maximal non-Unicode symbol encoded as a string value.
20
+ */
12
21
  export declare const max: string;
22
+ /**
23
+ * Maximal Unicode code point encoded as a string value.
24
+ */
13
25
  export declare const unicodeMax: string;
14
26
  /** A sequence of rules. */
15
27
  export type Sequence = readonly Rule[];
@@ -17,16 +29,47 @@ export type Sequence = readonly Rule[];
17
29
  export type Variant = {
18
30
  readonly [k in string]: Rule;
19
31
  };
32
+ /**
33
+ * Data-only grammar rule.
34
+ */
20
35
  export type DataRule = Variant | Sequence | TerminalRange | string;
36
+ /**
37
+ * Lazily evaluated grammar rule.
38
+ */
21
39
  export type LazyRule = () => DataRule;
40
+ /**
41
+ * Grammar rule, either immediate data or lazy rule factory.
42
+ */
22
43
  export type Rule = DataRule | LazyRule;
23
44
  export declare const rangeEncode: (a: number, b: number) => TerminalRange;
45
+ /**
46
+ * Encodes a single symbol as a {@link TerminalRange}.
47
+ */
24
48
  export declare const oneEncode: (a: number) => TerminalRange;
49
+ /**
50
+ * End-of-file marker represented as one code point beyond Unicode range.
51
+ */
25
52
  export declare const eof: TerminalRange;
53
+ /**
54
+ * Decodes a packed range into `[start, end]` symbols.
55
+ */
26
56
  export declare const rangeDecode: (r: number) => Array2<number>;
27
57
  export declare const toSequence: (s: string) => readonly TerminalRange[];
58
+ /**
59
+ * Converts the whole string into one rule:
60
+ * - a single {@link TerminalRange} when the string has one symbol,
61
+ * - a sequence of {@link TerminalRange} values when the string has multiple symbols.
62
+ */
28
63
  export declare const str: (s: string) => readonly TerminalRange[] | TerminalRange;
64
+ /**
65
+ * Converts a string into a variant that maps each character to its symbol range.
66
+ */
29
67
  export declare const set: (s: string) => RangeVariant;
68
+ /**
69
+ * Encodes a two-symbol string into a terminal range.
70
+ *
71
+ * @throws If `ab` does not contain exactly two unicode code points.
72
+ */
30
73
  export declare const range: (ab: string) => TerminalRange;
31
74
  /**
32
75
  * A set of terminal ranges compatible with the `Variant` rule.
@@ -35,14 +78,32 @@ export type RangeVariant = {
35
78
  readonly [k in string]: TerminalRange;
36
79
  };
37
80
  export declare const remove: (range: TerminalRange, v: RangeVariant) => RangeVariant;
81
+ /**
82
+ * Returns the complement set of the provided ranges over {@link fullRange}.
83
+ */
38
84
  export declare const not: (v: RangeVariant) => RangeVariant;
85
+ /**
86
+ * Returns the complement set of a character set over {@link fullRange}.
87
+ */
39
88
  export declare const notSet: (s: string) => RangeVariant;
89
+ /**
90
+ * Empty sequence type for optional grammar branches.
91
+ */
40
92
  export type None = readonly [];
93
+ /**
94
+ * Shared empty sequence literal.
95
+ */
41
96
  export declare const none: None;
97
+ /**
98
+ * Optional grammar branch.
99
+ */
42
100
  export type Option<S> = {
43
101
  some: S;
44
102
  none: None;
45
103
  };
104
+ /**
105
+ * Creates an option value from a required branch.
106
+ */
46
107
  export declare const option: <S extends Rule>(some: S) => Option<S>;
47
108
  export type Repeat0Plus<T> = () => Option<readonly [T, Repeat0Plus<T>]>;
48
109
  /**
@@ -61,9 +122,21 @@ export type Repeat1Plus<T> = readonly [T, Repeat0Plus<T>];
61
122
  */
62
123
  export declare const repeat1Plus: <T extends Rule>(some: T) => Repeat1Plus<T>;
63
124
  export type Join1Plus<T, S> = readonly [T, Repeat0Plus<readonly [S, T]>];
125
+ /**
126
+ * Repeats `some` one or more times separated by `separator`.
127
+ */
64
128
  export declare const join1Plus: <T extends Rule, S extends Rule>(some: T, separator: S) => Join1Plus<T, S>;
65
129
  export type Join0Plus<T, S> = Option<readonly [T, Repeat0Plus<readonly [S, T]>]>;
130
+ /**
131
+ * Repeats `some` zero or more times separated by `separator`.
132
+ */
66
133
  export declare const join0Plus: <T extends Rule, S extends Rule>(some: T, separator: S) => Rule;
67
134
  export type Repeat<T> = readonly T[];
135
+ /**
136
+ * Repeats a rule a fixed number of times.
137
+ */
68
138
  export declare const repeat: (n: number) => <T extends Rule>(some: T) => Repeat<T>;
139
+ /**
140
+ * Determines whether the rule is an empty rule.
141
+ */
69
142
  export declare const isEmpty: (rule: Rule) => boolean;
package/bnf/module.f.js CHANGED
@@ -1,9 +1,21 @@
1
1
  import { codePointListToString, stringToCodePointList } from "../text/utf16/module.f.js";
2
2
  import { isArray2 } from "../types/array/module.f.js";
3
3
  import { map, toArray, repeat as listRepeat } from "../types/list/module.f.js";
4
+ /**
5
+ * Full 24-bit symbol range packed into a single {@link TerminalRange}.
6
+ */
4
7
  export const fullRange = 0x000000_FFFFFF;
8
+ /**
9
+ * Unicode scalar value range packed into a single {@link TerminalRange}.
10
+ */
5
11
  export const unicodeRange = 0x000000_10FFFF;
12
+ /**
13
+ * Maximal non-Unicode symbol encoded as a string value.
14
+ */
6
15
  export const max = codePointListToString([0xFFFFFF]);
16
+ /**
17
+ * Maximal Unicode code point encoded as a string value.
18
+ */
7
19
  export const unicodeMax = codePointListToString([0x10FFFF]);
8
20
  // Internals:
9
21
  const { fromEntries, values } = Object;
@@ -20,17 +32,39 @@ export const rangeEncode = (a, b) => {
20
32
  }
21
33
  return Number((BigInt(a) << BigInt(offset)) | BigInt(b));
22
34
  };
35
+ /**
36
+ * Encodes a single symbol as a {@link TerminalRange}.
37
+ */
23
38
  export const oneEncode = (a) => rangeEncode(a, a);
39
+ /**
40
+ * End-of-file marker represented as one code point beyond Unicode range.
41
+ */
24
42
  export const eof = oneEncode(0x110000);
43
+ /**
44
+ * Decodes a packed range into `[start, end]` symbols.
45
+ */
25
46
  export const rangeDecode = (r) => [Number(BigInt(r) >> BigInt(offset)), Number(BigInt(r) & BigInt(mask))];
26
47
  const mapOneEncode = map(oneEncode);
27
48
  export const toSequence = (s) => toArray(mapOneEncode(stringToCodePointList(s)));
49
+ /**
50
+ * Converts the whole string into one rule:
51
+ * - a single {@link TerminalRange} when the string has one symbol,
52
+ * - a sequence of {@link TerminalRange} values when the string has multiple symbols.
53
+ */
28
54
  export const str = (s) => {
29
55
  const x = toSequence(s);
30
56
  return x.length === 1 ? x[0] : x;
31
57
  };
32
58
  const mapEntry = map((v) => [fromCodePoint(v), oneEncode(v)]);
59
+ /**
60
+ * Converts a string into a variant that maps each character to its symbol range.
61
+ */
33
62
  export const set = (s) => fromEntries(toArray(mapEntry(stringToCodePointList(s))));
63
+ /**
64
+ * Encodes a two-symbol string into a terminal range.
65
+ *
66
+ * @throws If `ab` does not contain exactly two unicode code points.
67
+ */
34
68
  export const range = (ab) => {
35
69
  const a = toArray(stringToCodePointList(ab));
36
70
  if (!isArray2(a)) {
@@ -65,9 +99,21 @@ export const remove = (range, v) => {
65
99
  }
66
100
  return toVariantRangeSet(result);
67
101
  };
102
+ /**
103
+ * Returns the complement set of the provided ranges over {@link fullRange}.
104
+ */
68
105
  export const not = (v) => remove(fullRange, v);
106
+ /**
107
+ * Returns the complement set of a character set over {@link fullRange}.
108
+ */
69
109
  export const notSet = (s) => not(set(s));
110
+ /**
111
+ * Shared empty sequence literal.
112
+ */
70
113
  export const none = [];
114
+ /**
115
+ * Creates an option value from a required branch.
116
+ */
71
117
  export const option = (some) => ({
72
118
  some,
73
119
  none,
@@ -89,9 +135,21 @@ export const repeat0Plus = (some) => {
89
135
  * Repeat one or more times.
90
136
  */
91
137
  export const repeat1Plus = (some) => [some, repeat0Plus(some)];
138
+ /**
139
+ * Repeats `some` one or more times separated by `separator`.
140
+ */
92
141
  export const join1Plus = (some, separator) => [some, repeat0Plus([separator, some])];
142
+ /**
143
+ * Repeats `some` zero or more times separated by `separator`.
144
+ */
93
145
  export const join0Plus = (some, separator) => option(join1Plus(some, separator));
146
+ /**
147
+ * Repeats a rule a fixed number of times.
148
+ */
94
149
  export const repeat = (n) => (some) => toArray(listRepeat(some)(n));
150
+ /**
151
+ * Determines whether the rule is an empty rule.
152
+ */
95
153
  export const isEmpty = (rule) => {
96
154
  const d = typeof rule === 'function' ? rule() : rule;
97
155
  return d === '' || (d instanceof Array && d.length === 0);
package/cas/module.f.d.ts CHANGED
@@ -5,8 +5,8 @@
5
5
  */
6
6
  import { type Sha2 } from "../crypto/sha2/module.f.ts";
7
7
  import type { Vec } from "../types/bit_vec/module.f.ts";
8
- import { type Effect, type Operations } from "../types/effect/module.f.ts";
9
- import { type Fs, type NodeOperations } from "../types/effect/node/module.f.ts";
8
+ import { type Effect, type Operations } from "../types/effects/module.f.ts";
9
+ import { type Fs, type NodeOperations } from "../types/effects/node/module.f.ts";
10
10
  export type KvStore<O extends Operations> = {
11
11
  readonly read: (key: Vec) => Effect<O, Vec | undefined>;
12
12
  readonly write: (key: Vec, value: Vec) => Effect<O, void>;
package/cas/module.f.js CHANGED
@@ -6,8 +6,8 @@
6
6
  import { computeSync, sha256 } from "../crypto/sha2/module.f.js";
7
7
  import { parse } from "../path/module.f.js";
8
8
  import { cBase32ToVec, vecToCBase32 } from "../types/cbase32/module.f.js";
9
- import { pure } from "../types/effect/module.f.js";
10
- import { error, log, mkdir, readdir, readFile, writeFile } from "../types/effect/node/module.f.js";
9
+ import { fluent, pure } from "../types/effects/module.f.js";
10
+ import { error, log, mkdir, readdir, readFile, writeFile } from "../types/effects/node/module.f.js";
11
11
  import { toOption } from "../types/nullable/module.f.js";
12
12
  import { unwrap } from "../types/result/module.f.js";
13
13
  const o = { withFileTypes: true };
@@ -20,20 +20,29 @@ const toPath = (key) => {
20
20
  return `${prefix}/${a}/${b}/${c}`;
21
21
  };
22
22
  export const fileKvStore = (path) => ({
23
- read: (key) => readFile(toPath(key))
24
- .map(([status, data]) => status === 'error' ? undefined : data),
23
+ read: (key) => fluent
24
+ .step(() => readFile(toPath(key)))
25
+ .step(([status, data]) => pure(status === 'error' ? undefined : data))
26
+ .effect,
25
27
  write: (key, value) => {
26
28
  const p = toPath(key);
27
29
  const parts = parse(p);
28
30
  const dir = `${path}/${parts.slice(0, -1).join('/')}`;
29
31
  // TODO: error handling
30
- return mkdir(dir, { recursive: true })
31
- .pipe(() => writeFile(`${path}/${p}`, value))
32
- .map(() => undefined);
32
+ return fluent
33
+ .step(() => mkdir(dir, { recursive: true }))
34
+ .step(() => writeFile(`${path}/${p}`, value))
35
+ .step(() => pure(undefined))
36
+ .effect;
33
37
  },
34
- list: () => readdir('.cas', { recursive: true })
35
- // TODO: remove unwrap
36
- .map(r => unwrap(r).flatMap(({ name, parentPath, isFile }) => toOption(isFile ? cBase32ToVec(parentPath.substring(prefix.length).replaceAll('/', '') + name) : null))),
38
+ list: () =>
39
+ // TODO: remove unwrap
40
+ fluent
41
+ .step(() => readdir('.cas', { recursive: true }))
42
+ .step(r => pure(unwrap(r).flatMap(({ name, parentPath, isFile }) => toOption(isFile
43
+ ? cBase32ToVec(parentPath.substring(prefix.length).replaceAll('/', '') + name)
44
+ : null))))
45
+ .effect,
37
46
  });
38
47
  export const cas = (sha2) => {
39
48
  const compute = computeSync(sha2);
@@ -41,13 +50,18 @@ export const cas = (sha2) => {
41
50
  read,
42
51
  write: (value) => {
43
52
  const hash = compute([value]);
44
- return write(hash, value)
45
- .map(() => hash);
53
+ return fluent
54
+ .step(() => write(hash, value))
55
+ .step(() => pure(hash))
56
+ .effect;
46
57
  },
47
58
  list,
48
59
  });
49
60
  };
50
- const e = (s) => error(s).map(() => 1);
61
+ const e = (s) => fluent
62
+ .step(() => error(s))
63
+ .step(() => pure(1))
64
+ .effect;
51
65
  export const main = (args) => {
52
66
  const c = cas(sha256)(fileKvStore('.'));
53
67
  const [cmd, ...options] = args;
@@ -57,10 +71,12 @@ export const main = (args) => {
57
71
  return e("'cas add' expects one parameter");
58
72
  }
59
73
  const [path] = options;
60
- return readFile(path)
61
- .pipe(v => c.write(unwrap(v)))
62
- .pipe(hash => log(vecToCBase32(hash)))
63
- .map(() => 0);
74
+ return fluent
75
+ .step(() => readFile(path))
76
+ .step(v => c.write(unwrap(v)))
77
+ .step(hash => log(vecToCBase32(hash)))
78
+ .step(() => pure(0))
79
+ .effect;
64
80
  }
65
81
  case 'get': {
66
82
  if (options.length !== 2) {
@@ -71,25 +87,36 @@ export const main = (args) => {
71
87
  if (hash === null) {
72
88
  return e(`invalid hash format: ${hashCBase32}`);
73
89
  }
74
- return c.read(hash)
75
- .pipe(v => {
90
+ return fluent
91
+ .step(() => c.read(hash))
92
+ .step(v => {
76
93
  const result = v === undefined
77
94
  ? e(`no such hash: ${hashCBase32}`)
78
- : writeFile(path, v).map(() => 0);
95
+ : fluent
96
+ .step(() => writeFile(path, v))
97
+ .step(() => pure(0))
98
+ .effect;
79
99
  return result;
80
- });
100
+ })
101
+ .effect;
81
102
  }
82
103
  case 'list': {
83
- return c.list()
84
- .pipe(v => {
104
+ return fluent
105
+ .step(() => c.list())
106
+ .step(v => {
85
107
  // TODO: make it lazy.
86
- let i = pure(undefined);
108
+ let i = fluent.effect;
87
109
  for (const j of v) {
88
- i = i.pipe(() => log(vecToCBase32(j)));
110
+ const prev = i;
111
+ i = fluent
112
+ .step(() => prev)
113
+ .step(() => log(vecToCBase32(j)))
114
+ .effect;
89
115
  }
90
116
  return i;
91
117
  })
92
- .map(() => 0);
118
+ .step(() => pure(0))
119
+ .effect;
93
120
  }
94
121
  case undefined: {
95
122
  return e('Error: CAS command requires subcommand');
package/ci/module.f.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type NodeEffect } from '../types/effect/node/module.f.ts';
1
+ import { type NodeEffect } from '../types/effects/node/module.f.ts';
2
2
  export declare const effect: NodeEffect<number>;
3
- declare const _default: () => NodeEffect<number>;
3
+ declare const _default: () => import("../types/effects/module.f.ts").Pure<number>;
4
4
  export default _default;
package/ci/module.f.js CHANGED
@@ -4,7 +4,8 @@
4
4
  * @module
5
5
  */
6
6
  import { utf8 } from "../text/module.f.js";
7
- import { writeFile } from "../types/effect/node/module.f.js";
7
+ import { fluent, pure } from "../types/effects/module.f.js";
8
+ import { writeFile } from "../types/effects/node/module.f.js";
8
9
  const os = ['ubuntu', 'macos', 'windows'];
9
10
  const architecture = ['intel', 'arm'];
10
11
  // https://docs.github.com/en/actions/reference/runners/github-hosted-runners#standard-github-hosted-runners-for-public-repositories
@@ -176,6 +177,8 @@ const gha = {
176
177
  on: { pull_request: {} },
177
178
  jobs,
178
179
  };
179
- export const effect = writeFile('.github/workflows/ci.yml', utf8(JSON.stringify(gha, null, ' ')))
180
- .map(() => 0);
180
+ export const effect = fluent
181
+ .step(() => writeFile('.github/workflows/ci.yml', utf8(JSON.stringify(gha, null, ' '))))
182
+ .step(() => pure(0))
183
+ .effect;
181
184
  export default () => effect;
package/dev/module.f.js CHANGED
@@ -5,10 +5,11 @@
5
5
  */
6
6
  import { fromIo } from "../io/module.f.js";
7
7
  import { updateVersion } from "./version/module.f.js";
8
- import { decodeUtf8, encodeUtf8 } from "../types/uint8array/module.f.js";
9
- import { readFile } from "../types/effect/node/module.f.js";
8
+ import { encodeUtf8 } from "../types/uint8array/module.f.js";
9
+ import { readFile } from "../types/effects/node/module.f.js";
10
10
  import { utf8ToString } from "../text/module.f.js";
11
11
  import { unwrap } from "../types/result/module.f.js";
12
+ import { fluent, pure } from "../types/effects/module.f.js";
12
13
  export const todo = () => { throw 'not implemented'; };
13
14
  const cmp = ([a], [b]) => a < b ? -1 : a > b ? 1 : 0;
14
15
  export const env = ({ process: { env } }) => a => {
@@ -57,9 +58,11 @@ export const loadModuleMap = async (io) => {
57
58
  return Object.fromEntries(map.toSorted(cmp));
58
59
  };
59
60
  const denoJson = './deno.json';
60
- const index2 = updateVersion
61
- .pipe(() => readFile(denoJson))
62
- .map(v => JSON.parse(utf8ToString(unwrap(v))));
61
+ const index2 = fluent
62
+ .step(() => updateVersion)
63
+ .step(() => readFile(denoJson))
64
+ .step(v => pure(JSON.parse(utf8ToString(unwrap(v)))))
65
+ .effect;
63
66
  export const index = async (io) => {
64
67
  const runner = fromIo(io);
65
68
  const jsr_json = await runner(index2);
@@ -48,4 +48,5 @@ const run = async () => {
48
48
  }
49
49
  }
50
50
  };
51
+ // we need `await` for Playwright.
51
52
  await run();
@@ -1,2 +1,2 @@
1
- import { type NodeEffect } from "../../types/effect/node/module.f.ts";
1
+ import { type NodeEffect } from "../../types/effects/node/module.f.ts";
2
2
  export declare const updateVersion: NodeEffect<number>;
@@ -4,21 +4,24 @@
4
4
  * @module
5
5
  */
6
6
  import { utf8, utf8ToString } from "../../text/module.f.js";
7
- import { all } from "../../types/effect/module.f.js";
8
- import { readFile, writeFile } from "../../types/effect/node/module.f.js";
7
+ import { all, fluent, pure, step } from "../../types/effects/module.f.js";
8
+ import { readFile, writeFile } from "../../types/effects/node/module.f.js";
9
9
  import { unwrap } from "../../types/result/module.f.js";
10
10
  const { stringify, parse } = JSON;
11
11
  const jsonFile = (jsonFile) => `${jsonFile}.json`;
12
- const readJson = (name) => readFile(jsonFile(name))
13
- .map(v => parse(utf8ToString(unwrap(v))));
14
- const writeVersion = (version) => (name) => readJson(name)
15
- .pipe(json => writeFile(jsonFile(name), utf8(stringify({
12
+ const readJson = (name) => fluent
13
+ .step(() => readFile(jsonFile(name)))
14
+ .step(v => pure(parse(utf8ToString(unwrap(v)))))
15
+ .effect;
16
+ const writeVersion = (version) => (name) => step(readJson(name))(json => writeFile(jsonFile(name), utf8(stringify({
16
17
  ...json,
17
18
  version,
18
19
  }, null, 2))));
19
- export const updateVersion = readJson('package')
20
- .pipe(p => {
20
+ export const updateVersion = fluent
21
+ .step(() => readJson('package'))
22
+ .step(p => {
21
23
  const w = writeVersion(p.version);
22
24
  return all([w('package'), w('deno')]);
23
25
  })
24
- .map(() => 0);
26
+ .step(() => pure(0))
27
+ .effect;
@@ -1,9 +1,9 @@
1
1
  import { utf8, utf8ToString } from "../../text/module.f.js";
2
2
  import { isVec } from "../../types/bit_vec/module.f.js";
3
- import { run } from "../../types/effect/mock/module.f.js";
4
- import { all } from "../../types/effect/module.f.js";
5
- import { writeFile } from "../../types/effect/node/module.f.js";
6
- import { emptyState, virtual } from "../../types/effect/node/virtual/module.f.js";
3
+ import { run } from "../../types/effects/mock/module.f.js";
4
+ import { all } from "../../types/effects/module.f.js";
5
+ import { writeFile } from "../../types/effects/node/module.f.js";
6
+ import { emptyState, virtual } from "../../types/effects/node/virtual/module.f.js";
7
7
  import { updateVersion } from "./module.f.js";
8
8
  const version = '0.3.0';
9
9
  const x = {
package/io/module.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type Io, type Run } from './module.f.ts';
2
- import type { NodeProgram } from '../types/effect/node/module.f.ts';
2
+ import type { NodeProgram } from '../types/effects/node/module.f.ts';
3
3
  export declare const io: Io;
4
4
  export declare const legacyRun: Run;
5
5
  export type NodeRun = (p: NodeProgram) => Promise<number>;
package/io/module.f.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { NodeEffect } from '../types/effect/node/module.f.ts';
1
+ import type { NodeEffect } from '../types/effects/node/module.f.ts';
2
2
  import { type Result } from '../types/result/module.f.ts';
3
3
  /**
4
4
  * Represents a directory entry (file or directory) in the filesystem
package/io/module.f.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { normalize } from "../path/module.f.js";
2
- import { asyncRun } from "../types/effect/module.js";
2
+ import { asyncRun } from "../types/effects/module.js";
3
3
  import { error, ok } from "../types/result/module.f.js";
4
4
  import { fromVec, toVec } from "../types/uint8array/module.f.js";
5
5
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.10.1",
3
+ "version": "0.10.3",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "**/*.js",
@@ -2,10 +2,10 @@ export const run = (o) => (state) => (effect) => {
2
2
  let s = state;
3
3
  let e = effect;
4
4
  while (true) {
5
- if ('pure' in e) {
6
- return [s, e.pure];
5
+ if (e.length === 1) {
6
+ return [s, e[0]];
7
7
  }
8
- const [cmd, payload, cont] = e.do;
8
+ const [cmd, payload, cont] = e;
9
9
  const operation = o[cmd];
10
10
  const [ns, m] = operation(s, payload);
11
11
  s = ns;
@@ -6,23 +6,22 @@
6
6
  export type Operations = {
7
7
  readonly [command in string]: readonly [input: unknown, output: unknown];
8
8
  };
9
- export type Effect<O extends Operations, T> = Pure<O, T> | Do<O, T>;
10
- export type Map<O extends Operations, T> = {
11
- readonly pipe: <O1 extends Operations, R>(f: (_: T) => Effect<O1, R>) => Effect<O | O1, R>;
12
- readonly map: <O1 extends Operations, R>(f: (_: T) => R) => Effect<O | O1, R>;
13
- };
14
- export type Pure<O extends Operations, T> = {
15
- readonly pure: T;
16
- } & Map<O, T>;
17
- export declare const pure: <O extends Operations, T>(value: T) => Pure<O, T>;
9
+ export type Effect<O extends Operations, T> = Pure<T> | Do<O, T>;
10
+ export type Pure<T> = readonly [T];
18
11
  export type One<O extends Operations, T, K extends keyof O & string> = readonly [K, O[K][0], (input: O[K][1]) => Effect<O, T>];
19
12
  export type Do<O extends Operations, T> = {
20
- readonly do: {
21
- readonly [K in keyof O & string]: One<O, T, K>;
22
- }[keyof O & string];
23
- } & Map<O, T>;
13
+ readonly [K in keyof O & string]: One<O, T, K>;
14
+ }[keyof O & string];
15
+ export declare const pure: <T>(value: T) => Pure<T>;
24
16
  export declare const do_: <O extends Operations, K extends keyof O & string>(cmd: K, payload: O[K][0]) => Do<O, O[K][1]>;
25
17
  export type ToAsyncOperationMap<O extends Operations> = {
26
18
  readonly [K in keyof O]: (payload: O[K][0]) => Promise<O[K][1]>;
27
19
  };
20
+ export declare const step: <O extends Operations, T>(e: Effect<O, T>) => <O1 extends Operations, R>(f: (_: T) => Effect<O1, R>) => Effect<O | O1, R>;
21
+ export declare const map: <O extends Operations, T>(e: Effect<O, T>) => <R>(f: (_: T) => R) => Effect<O, R>;
22
+ export type Fluent<O extends Operations, T> = {
23
+ readonly effect: Effect<O, T>;
24
+ readonly step: <O1 extends Operations, R>(f: (_: T) => Effect<O1, R>) => Fluent<O | O1, R>;
25
+ };
26
+ export declare const fluent: Fluent<{}, void>;
28
27
  export declare const all: <O extends Operations, T>(set: readonly Effect<O, T>[]) => Effect<O, readonly T[]>;
@@ -0,0 +1,20 @@
1
+ export const pure = (value) => [value];
2
+ const doFull = (cmd, payload, cont) => [cmd, payload, cont];
3
+ export const do_ = (cmd, payload) => doFull(cmd, payload, pure);
4
+ export const step = (e) => (f) => {
5
+ if (e.length === 1) {
6
+ const [value] = e;
7
+ return f(value);
8
+ }
9
+ const [cmd, payload, cont] = e;
10
+ return doFull(cmd, payload, x => step(cont(x))(f));
11
+ };
12
+ export const map = (e) => (f) => step(e)(x => pure(f(x)));
13
+ const wrap = (effect) => ({
14
+ effect,
15
+ step: x => wrap(step(effect)(x)),
16
+ });
17
+ export const fluent = wrap(pure(undefined));
18
+ const empty = pure([]);
19
+ // TODO: replace with either a `Do` operation or as an addition to `Pure` and `Do`.
20
+ export const all = (set) => set.reduce((previous, current) => step(previous)(previousResult => map(current)(currentResult => [...previousResult, currentResult])), empty);
@@ -1,9 +1,9 @@
1
1
  export const asyncRun = (map) => async (effect) => {
2
2
  while (true) {
3
- if ('pure' in effect) {
4
- return effect.pure;
3
+ if (effect.length === 1) {
4
+ return effect[0];
5
5
  }
6
- const [command, payload, continuation] = effect.do;
6
+ const [command, payload, continuation] = effect;
7
7
  const operation = map[command];
8
8
  const result = await operation(payload);
9
9
  effect = continuation(result);
@@ -1,10 +1,11 @@
1
1
  import { empty, isVec, uint, vec8 } from "../../bit_vec/module.f.js";
2
+ import { map } from "../module.f.js";
2
3
  import { run } from "../mock/module.f.js";
3
4
  import { fetch, mkdir, readdir, readFile, writeFile } from "./module.f.js";
4
5
  import { emptyState, virtual } from "./virtual/module.f.js";
5
6
  export default {
6
7
  map: () => {
7
- let e = readFile('hello').map(([k, v]) => {
8
+ let e = map(readFile('hello'))(([k, v]) => {
8
9
  if (k === 'error') {
9
10
  throw v;
10
11
  }
@@ -12,14 +13,14 @@ export default {
12
13
  });
13
14
  //
14
15
  while (true) {
15
- if ('pure' in e) {
16
- const result = e.pure;
16
+ if (e.length === 1) {
17
+ const result = e[0];
17
18
  if (result !== 0x2an) {
18
19
  throw result;
19
20
  }
20
21
  return;
21
22
  }
22
- const [cmd, p, cont] = e.do;
23
+ const [cmd, p, cont] = e;
23
24
  if (cmd !== 'readFile') {
24
25
  throw cmd;
25
26
  }
@@ -1,3 +1,2 @@
1
- import { type NodeEffect } from '../types/effect/node/module.f.ts';
2
- declare const _default: () => NodeEffect<number>;
1
+ declare const _default: () => import("../types/effects/module.f.ts").Pure<number>;
3
2
  export default _default;
@@ -4,11 +4,14 @@
4
4
  * @module
5
5
  */
6
6
  import { htmlToString } from "../html/module.f.js";
7
- import { writeFile } from "../types/effect/node/module.f.js";
7
+ import { writeFile } from "../types/effects/node/module.f.js";
8
8
  import { utf8 } from "../text/module.f.js";
9
+ import { fluent, pure } from "../types/effects/module.f.js";
9
10
  const html = ['body',
10
11
  ['a', { href: 'https://github.com/functionalscript/functionalscript' }, 'GitHub Repository']
11
12
  ];
12
- const program = writeFile('index.html', utf8(htmlToString(html)))
13
- .map(() => 0);
13
+ const program = fluent
14
+ .step(() => writeFile('index.html', utf8(htmlToString(html))))
15
+ .step(() => pure(0))
16
+ .effect;
14
17
  export default () => program;
@@ -1,14 +0,0 @@
1
- export const pure = (value) => ({
2
- pure: value,
3
- pipe: e => e(value),
4
- map: f => pure(f(value))
5
- });
6
- const doFull = (cmd, payload, cont) => ({
7
- do: [cmd, payload, cont],
8
- pipe: e => doFull(cmd, payload, x => cont(x).pipe(e)),
9
- map: f => doFull(cmd, payload, x => cont(x).map(f))
10
- });
11
- export const do_ = (cmd, payload) => doFull(cmd, payload, pure);
12
- const empty = pure([]);
13
- // TODO: replace with either a `Do` operation or as an addition to `Pure` and `Do`.
14
- export const all = (set) => set.reduce((previous, current) => previous.pipe(previousResult => current.map(currentResult => [...previousResult, currentResult])), empty);
File without changes
File without changes
File without changes
File without changes
File without changes