Skip to content

Commit 32e5293

Browse files
committed
feat: initial implementation of contrib/most.ts
Create the initial type classes for most.js Stream. Reexport a few most libraries for convenience: * @most/core * @most/types * @most/scheduler#{newScheduler, newDefaultScheduler} * @most/hold It seems @most/index is not published to npm so I've replicated the primary exports here. They are MIT licensed. Additionally, made a dangerous collect function like the ones in iterable.ts and async_iterable.ts. This is primarily useful for testing. I should probably make the returned promise abortable.
1 parent a73e11b commit 32e5293

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed

contrib/most.ts

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import type { Scheduler, Stream } from "npm:@most/[email protected]";
2+
import type { $, In, InOut, Kind, Out } from "../kind.ts";
3+
import type { Wrappable } from "../wrappable.ts";
4+
import type { Mappable } from "../mappable.ts";
5+
import type { Applicable } from "../applicable.ts";
6+
import type { Flatmappable } from "../flatmappable.ts";
7+
8+
import * as M from "npm:@most/[email protected]";
9+
import { createBind, createTap } from "../flatmappable.ts";
10+
import { createBindTo } from "../mappable.ts";
11+
import { flow, pipe } from "../fn.ts";
12+
13+
export * from "npm:@most/[email protected]";
14+
export * from "npm:@most/[email protected]";
15+
export * from "npm:@most/[email protected]";
16+
export { newDefaultScheduler, newScheduler } from "npm:@most/[email protected]";
17+
18+
export interface KindStream extends Kind {
19+
readonly kind: Stream<Out<this, 0>>;
20+
}
21+
22+
export function count<A>(sa: Stream<A>): Stream<number> {
23+
return keepIndex(withCount(sa));
24+
}
25+
26+
export function withCount<A>(sa: Stream<A>): Stream<[number, A]> {
27+
return withIndexStart(1, sa);
28+
}
29+
30+
export function index<A>(sa: Stream<A>): Stream<number> {
31+
return keepIndex(withIndex(sa));
32+
}
33+
34+
export function withIndex<A>(sa: Stream<A>): Stream<[number, A]> {
35+
return withIndexStart(0, sa);
36+
}
37+
38+
export function withIndexStart<A>(
39+
start: number,
40+
sa: Stream<A>,
41+
): Stream<[number, A]> {
42+
return indexed((i) => [i, i + 1], start, sa);
43+
}
44+
45+
export function indexed<S, I, A>(
46+
f: (s: S) => [I, S],
47+
init: S,
48+
sa: Stream<A>,
49+
): Stream<[I, A]> {
50+
return M.loop(
51+
(s, a) => {
52+
const [index, seed] = f(s);
53+
return { seed, value: [index, a] };
54+
},
55+
init,
56+
sa,
57+
);
58+
}
59+
60+
export function keepIndex<I>(s: Stream<[I, unknown]>): Stream<I> {
61+
return M.map((ia) => ia[0], s);
62+
}
63+
64+
export async function collect<A>(
65+
stream: Stream<A>,
66+
scheduler: Scheduler,
67+
): Promise<readonly A[]> {
68+
const as: A[] = [];
69+
await M.runEffects(pipe(stream, M.tap((a) => as.push(a))), scheduler);
70+
return as;
71+
}
72+
73+
export const wrap: Wrappable<KindStream>["wrap"] = M.now;
74+
75+
export const map: Mappable<KindStream>["map"] = M.map;
76+
77+
export const apply: Applicable<KindStream>["apply"] = M.ap;
78+
79+
export const flatmap: Flatmappable<KindStream>["flatmap"] = M.chain;
80+
81+
export const WrappableStream: Wrappable<KindStream> = { wrap };
82+
83+
export const MappableStream: Mappable<KindStream> = { map };
84+
85+
export const ApplicableStream: Applicable<KindStream> = { wrap, map, apply };
86+
87+
export const FlatmappableStream: Flatmappable<KindStream> = {
88+
wrap,
89+
map,
90+
apply,
91+
flatmap,
92+
};
93+
94+
export const bind = createBind(FlatmappableStream);
95+
96+
export const bindTo = createBindTo(FlatmappableStream);
97+
98+
export const tap = createTap(FlatmappableStream);
99+
100+
export interface TransformStream<U extends Kind> extends Kind {
101+
readonly kind: Stream<
102+
$<
103+
U,
104+
[Out<this, 0>, Out<this, 1>, Out<this, 2>],
105+
[In<this, 0>],
106+
[InOut<this, 0>]
107+
>
108+
>;
109+
}
110+
111+
export function transformStream<U extends Kind>(
112+
FM: Flatmappable<U>,
113+
extract: <
114+
I,
115+
J = unknown,
116+
K = unknown,
117+
L = never,
118+
M = never,
119+
B = unknown,
120+
C = unknown,
121+
D = never,
122+
E = never,
123+
>(
124+
usua: $<U, [Stream<$<U, [I, J, K], [L], [M]>>, B, C], [D], [E]>,
125+
) => Stream<$<U, [I, J | B, K | C], [L & D], [M & E]>>,
126+
): Flatmappable<TransformStream<U>> {
127+
return {
128+
wrap: (a) => wrap(FM.wrap(a)),
129+
map: (fai) => map(FM.map(fai)),
130+
apply: M.combine(FM.apply) as Flatmappable<TransformStream<U>>["apply"],
131+
flatmap: (faui) => (sua) =>
132+
pipe(
133+
sua,
134+
flatmap(flow(FM.map(faui), extract)),
135+
),
136+
};
137+
}
138+
139+
export type * from "npm:@most/[email protected]"; /** Export types */

examples/most.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as M from "../contrib/most.ts";
2+
import { pipe } from "../fn.ts";
3+
4+
const stream = pipe(
5+
pipe(M.periodic(1000), M.scan((a) => a + 1, 0)),
6+
M.bindTo("seconds"),
7+
M.bind("timestamps", () => M.wrap(Date.now())),
8+
M.tap((a) => console.log(a)),
9+
M.take(10),
10+
);
11+
12+
// Strangely, this emits the first two events quickly.
13+
await M.runEffects(stream)(M.newDefaultScheduler());

testing/contrib/most.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { assertEquals } from "https://deno.land/[email protected]/testing/asserts.ts";
2+
3+
import type { Stream } from "../../contrib/most.ts";
4+
import * as M from "../../contrib/most.ts";
5+
6+
const scheduler = M.newDefaultScheduler();
7+
const run = <A>(s: Stream<A>): Promise<readonly A[]> => M.collect(s, scheduler);
8+
9+
Deno.test("Most wrap", async () => {
10+
assertEquals(await run(M.wrap(1)), [1]);
11+
});

0 commit comments

Comments
 (0)