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(-) (limited to 'src') 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