From 0267049f29374f0114bef23a5982c930c4d2bedb Mon Sep 17 00:00:00 2001
From: Joris
Date: Sun, 19 Feb 2023 13:19:05 +0100
Subject: Add pure and sequence
---
src/example.ts | 24 +++++++++++++++++
src/rx.ts | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 103 insertions(+), 5 deletions(-)
diff --git a/src/example.ts b/src/example.ts
index 5ff3fba..229d3a0 100644
--- a/src/example.ts
+++ b/src/example.ts
@@ -161,6 +161,30 @@ const doubleMapChild =
})
)
+const seq =
+ withState(0, a =>
+ withState(0, b =>
+ withState(0, c => [
+ counterComponent({
+ value: a,
+ onSub: () => a.update(n => n - 1),
+ onAdd: () => a.update(n => n + 1)
+ }),
+ counterComponent({
+ value: b,
+ onSub: () => b.update(n => n - 1),
+ onAdd: () => b.update(n => n + 1)
+ }),
+ counterComponent({
+ value: c,
+ onSub: () => c.update(n => n - 1),
+ onAdd: () => c.update(n => n + 1)
+ }),
+ sequence2([a, b, c]).map(xs => xs.reduce((a, b) => a + b, 0))
+ ])
+ )
+ )
+
const view = h('main',
h('h1', 'Rx'),
chrono
diff --git a/src/rx.ts b/src/rx.ts
index ea4f6a4..a83c915 100644
--- a/src/rx.ts
+++ b/src/rx.ts
@@ -112,6 +112,21 @@ export class Rx {
}
}
+class Pure extends Rx {
+ readonly type: 'Pure'
+ readonly value: A
+
+ constructor(value: A) {
+ super()
+ this.type = 'Pure'
+ this.value = value
+ }
+}
+
+export function pure(value: A): Rx {
+ return new Pure(value)
+}
+
class Var extends Rx {
readonly type: 'Var'
readonly id: string
@@ -151,6 +166,28 @@ class FlatMap extends Rx {
}
}
+export function sequence(xs: Array>): Sequence {
+ return new Sequence(xs)
+}
+
+export function sequence2(xs: Array>): Rx> {
+ return xs.reduce(
+ (acc: Rx>, x: Rx) => acc.flatMap(ys => x.map(y => [y, ...ys])),
+ new Pure([])
+ )
+}
+
+class Sequence extends Rx> {
+ readonly type: 'Sequence'
+ readonly xs: Array>
+
+ constructor(xs: Array>) {
+ super()
+ this.type = 'Sequence'
+ this.xs = xs
+ }
+}
+
// Mount
export function mount(html: Html): Cancelable {
@@ -222,8 +259,15 @@ const voidRemove = () => {}
// Rx run
-function rxRun(state: State, rx: Rx, effect: (value: A) => void): Cancelable {
- if (isVar(rx)) {
+function rxRun(
+ state: State,
+ rx: Rx,
+ effect: (value: A) => void
+): Cancelable {
+ if (isPure(rx)) {
+ effect(rx.value)
+ return voidCancel
+ } else if (isVar(rx)) {
const cancel = state.subscribe(rx, effect)
effect(state.get(rx))
return cancel
@@ -239,13 +283,39 @@ function rxRun(state: State, rx: Rx, effect: (value: A) => void): Cancelab
cancel2()
cancel1()
}
+ } else if (isSequence(rx)) {
+ const cancels = Array(rx.xs.length).fill(voidCancel)
+ const xs = Array(rx.xs.length).fill(undefined)
+ let initEnded = false
+
+ rx.xs.forEach((rxChild, i) => {
+ cancels[i] = rxRun(
+ state,
+ rxChild,
+ (value: A) => {
+ xs[i] = value
+ if (initEnded) {
+ // @ts-ignore
+ effect(xs)
+ }
+ }
+ )
+ })
+ // @ts-ignore
+ effect(xs)
+ initEnded = true
+ return () => cancels.forEach(cancel => cancel())
} else {
throw new Error(`Unrecognized rx: ${rx}`)
}
}
function isRx(x: any): x is Rx {
- return x !== undefined && x.type !== undefined && (x.type === "Var" || x.type === "Map" || x.type === "FlatMap")
+ return x !== undefined && x.type !== undefined && (x.type === "Var" || x.type === "Map" || x.type === "FlatMap" || x.type === 'Sequence' || x.type === 'Pure')
+}
+
+function isPure(x: any): x is Pure {
+ return x.type === 'Pure'
}
function isVar(x: any): x is Var {
@@ -253,11 +323,15 @@ function isVar(x: any): x is Var {
}
function isMap(x: any): x is Map {
- return x.type === "Map"
+ return x.type === "Map"
}
function isFlatMap(x: any): x is FlatMap {
- return x.type === "FlatMap"
+ return x.type === "FlatMap"
+}
+
+function isSequence(x: any): x is Sequence {
+ return x.type === "Sequence"
}
// Append
--
cgit v1.2.3