Listeners
A Listener[M, E, S] watches slice S and runs S => IO[E, Unit] when that slice changes
per FastEq. c.subscribe(_.count)(cb) registers immediately (it does
not wait for run()). The Subscribe / Unsubscribe ops exist so a handler can wire a
listener as a follow-up.
Slice notifications
Changing label does not fire a _.count listener. The timeline below only records count.
Mermoid.diagram(slice)for
c <- Conduit(S(0, "x"))(handler)
seen <- Ref.make(List.empty[Int])
_ <- c.subscribe(_.count)(n => seen.update(_ :+ n))
_ <- c(Op.Inc, Op.Inc, Op.SetLabel("y"), Op.Inc, Op.Dec)
_ <- c.run()
ns <- seen.get
yield nsList(1, 2, 3, 2)Live log
The log appends only when count changes. Toggle the label: the log stays still. +/−
append a line.
for
(c, ctx) <- DocsRuntime.live(S(0, "x"))(handler)
log <- sq(List.empty[String])
_ <- c.subscribe(_.count)(n => log.update(_ :+ s"count=$n").unit)
count <- ctx.squawk(_.count)
label <- ctx.squawk(_.label)
yield E.div(
E.p("count ", E.strong(count.map(_.toString)), " label ", E.strong(label)),
E.button(Events.onClick(_ => ctx(Op.Dec)), "−"),
E.button(Events.onClick(_ => ctx(Op.Inc)), "+"),
E.button(Events.onClick(_ => ctx(Op.SetLabel("alpha"))), "Label alpha"),
E.button(Events.onClick(_ => ctx(Op.SetLabel("beta"))), "Label beta"),
E.pre(log.map(lines => if lines.isEmpty then "(no count events yet)" else lines.mkString("\n"))),
)count 0 label x
(no count events yet)
Subscribe as a follow-up
A handler can return Subscribe(listener) in ActionResult.next. Until that op runs, the
callback never fires. c.unsubscribe(listener) removes it; there is also an Unsubscribe op.
Mermoid.diagramInteractive(slice, initialWidth = 640)viewport 640px