FRP, incremental computation and the partial re-render problem

9 views
Skip to first unread message

Ben Hutchison

unread,
Feb 19, 2026, 7:32:27 PM (8 days ago) Feb 19
to Melbourne Compose Group
At dinner last night we touched on UI libraries based on Functional Reactive Programming (FRP).

As an aside: for the longest time it used to annoy me that FRP seemed to have a fuzzy definition and meant different things to different people.

I'd been exposed to "classic FRP" as espoused by Paul Hudak & Conal Elliot (see eg "Haskell School of Expression" , FRAN, or http://conal.net/blog/posts/why-classic-frp-does-not-fit-interactive-behavior), which emphasized "Behaviors" as functions from time to values. 

But that Behavior stuff was an apparent evolutionary dead end. Another community took the term FRP to mean "Signal-oriented programming", a looser and more feasible idea, and I think this branch has won.

So I now use FRP as a term describing Signal-oriented programming, a stream-like series of values that vary over time, typically originating at a mutable cell.

The main distinction of Signals vs Streams is that the consumer or handler of a Signal doesn't control when the next value is pushed out. Whereas, with Streams, the consumer can pull the next value at their leisure; a good fit for reading bytes from a file, but less good for mouse clicks.

In this FRP UIs, we model the data that powers the UI as a Signal; a Signal[List[Customer]] for example in Scala syntax. We then transform these data signals into Signal[UIComponent] and the framework provides an efficient way of updating the screen/DOM/canvas etc from such signals.

However, there's a classic problem with this approach, which I mentioned last night, but thought I should clarify..

Imagine we have a screen showing a table of 100 customers as rows. One single customer changes their boolean subscription status. Since the List[Customer] data has changed, that signal pushes out a new update. If we handle this naively, we will re-render the entire table despite the change being far more localised to a single checkbox. 

Laminar, a leading Scala FRP UI framework, has a good explanation of the problem and a good solution (probably evolved from earlier frameworks; Laminar is just where I joined the story):

https://laminar.dev/documentation#performant-children-rendering--split

Conceptually, a signal over a collection is split into many signals over the parts, and only those parts that have actually changed generate updated events.

I expect something like this is essential for any FRP UI framework that succeeds industrially at scale.

@Peter: you mentioned "Sodium" [https://github.com/SodiumFRP/sodium]. I had a look at the Manning book and couldn't locate a specific analog to Laminar's split. And Sodium's website is down atm :( 

-Ben

John Walker

unread,
Feb 20, 2026, 9:27:30 PM (7 days ago) Feb 20
to Ben Hutchison, Melbourne Compose Group
  • Haskell Deprecation: The Haskell version of Sodium has been deprecated in favour of reactive-banana, as their APIs are remarkably similar.
Reflex FRP https://share.google/F21YblC0DLvm7I1zu is also a big one, though my only experience with it was working through a tute in the before-time. Imagining your UI table I would guess it would be set up with Dynamics. 

--
You received this message because you are subscribed to the Google Groups "Melbourne Compose Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to melbourne-compose...@googlegroups.com.
To view this discussion, visit https://groups.google.com/d/msgid/melbourne-compose-group/d4fc71e5-69e3-43a1-9e7a-2a8e89704d7bn%40googlegroups.com.

Ben Hutchison

unread,
Feb 21, 2026, 7:24:23 PM (6 days ago) Feb 21
to Melbourne Compose Group
Guessing from a quick scan of Reflex, this section "covers" the incremental re-render problem in the Reflex docs? [https://app.readthedocs.org/projects/reflex-frp/downloads/pdf/latest/]:

3.4.2 Patch and Incremental

An Incremental is a more general form of a Dynamic. Instead of always fully replacing the value, only parts of it
can be patched. This is only needed for performance critical code via merge Incremental to make small changes
to large values.
Reflex.Patch.* provides a number of data structures which have the ability to do incremental updates.

Jack Kelly

unread,
Feb 21, 2026, 8:07:18 PM (6 days ago) Feb 21
to Ben Hutchison, Melbourne Compose Group
I haven't used Reflex in anything large, and I haven't used it in a long time, but I think incremental stuff doesn't get used directly all that often.

There are a few different tools IIRC to limit the amount of "do nothing" propagation:

* Reflex.Dynamic.Uniq uses pointer equality tests to limit propagation:
https://hackage-content.haskell.org/package/reflex-0.9.4.0/docs/Reflex-Dynamic-Uniq.html

* The `fan` function efficiently splits an event-of-DMap into several individual events, instead of having to repeatedly mapMaybe the same event. There are variants for simpler types:
https://hackage-content.haskell.org/package/reflex-0.9.4.0/docs/Reflex-Class.html#v:fan

* Reflex.Collection allows one to add/remove built widgets based on map keys being added/removed:
https://hackage-content.haskell.org/package/reflex-0.9.4.0/docs/Reflex-Collection.html

******

Also interesting: the Verse language is planning to implement "live variables" and "live expressions":
https://verselang.github.io/book/15_live_variables/

They seem to have planned some interesting language-level work around batching updates, read/write dynamic variables, synchronisation, etc.

-- Jack



February 22, 2026 at 12:24 AM, "Ben Hutchison" <brhut...@gmail.com mailto:brhut...@gmail.com?to=%22Ben%20Hutchison%22%20%3Cbrhutchison%40gmail.com%3E > wrote:


>
> Guessing from a quick scan of Reflex, this section "covers" the incremental re-render problem in the Reflex docs? [https://app.readthedocs.org/projects/reflex-frp/downloads/pdf/latest/]:
>
> 3.4.2 Patch and Incremental
>
> An Incremental is a more general form of a Dynamic. Instead of always fully replacing the value, only parts of it
> can be patched. This is only needed for performance critical code via merge Incremental to make small changes
> to large values.
> Reflex.Patch.* provides a number of data structures which have the ability to do incremental updates.
>
> On Saturday, 21 February 2026 at 13:27:30 UTC+11 thegh...@gmail.com wrote:
>
> >
> > * Reflex FRP https://share.google/F21YblC0DLvm7I1zu is also a big one, though my only experience with it was working through a tute in the before-time. Imagining your UI table I would guess it would be set up with Dynamics.
> >
>
> --
> You received this message because you are subscribed to the Google Groups "Melbourne Compose Group" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to melbourne-compose...@googlegroups.com mailto:melbourne-compose...@googlegroups.com .
> To view this discussion, visit https://groups.google.com/d/msgid/melbourne-compose-group/d22fcdb2-9984-4e87-a337-e3214066c3c8n%40googlegroups.com https://groups.google.com/d/msgid/melbourne-compose-group/d22fcdb2-9984-4e87-a337-e3214066c3c8n%40googlegroups.com?utm_medium=email&utm_source=footer .
>
Reply all
Reply to author
Forward
0 new messages