Squawk

A Squawk[A] is a value-over-time. Mutable sources are created effectfully; map / zipWith are pure and lazy; derived squawks are not live until something observes them. Changes are deduped by a pluggable Eq[A] (an Eq-equal write is a no-op).

Sources and updates

sq(init) allocates a Source[A]. set / update / observe are effects. Every observe returns a Subscription; the mount engine collects those into cleanup and tears them down LIFO.

{
  for
    n   <- sq(0)
    _   <- n.set(1)
    _   <- n.update(_ + 1)
    cur <- n.get
  yield cur
}
2

Eq dedup

An Eq-equal write does not notify observers; no spurious repaints.

{
  for
    n     <- sq(0)
    fired <- Ref.make(0)
    _     <- n.observe(_ => fired.update(_ + 1).unit)
    _     <- n.set(0)
    _     <- n.set(1)
    count <- fired.get
  yield count
}
1

Derived squawks

map and zipWith stay lazy. zipWith reads both sources at observe-time, which keeps diamond updates glitch-free.

{
  for
    a <- sq(1)
    b <- sq(10)
    sum = Squawk.zipWith(a, b)(_ + _)
  yield E.span(sum.map(_.toString))
}
11

In the tree

A Squawk[String] child becomes reactive text; an attr key applied to a Squawk becomes a reactive attribute; all via the DSL's Arg lifting.

{
  for label <- sq("hello")
  yield E.p(A.className(label.map(s => s"x-$s")), label)
}

hello