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