Journal entry for


Experiment: Imperative FRP

I just managed to get TodoMVC working with Threepenny-GUI, in an “imperative FRP” style. This means that I used functions with signatures like

newTellEvent :: (CommutativeSemigroup a) => Moment (Event a -> Moment (), Event a)

This gives you a “tellEvent”/“callback adder” to which you can add events which of which the occurrences will be combined in the result event.

It allows you to send event occurrences “up” from deep within the code without having to pass the events around, just like with Reflex’ EventWriter. EventWriter is much more cumbersome to use I feel, but on the other hand newTellEvent would need some way to ensure that you cannot pass the “tellEvent” outside of the scope, which might lead to implementation problems? I think figuring out the semantics for newTellEvent might help me figure things out more on that front.

Another thing I did is implement things like the “x items left” feature of TodoMVC by having

do (tellAdjustCompletedCount, adjustCompletedCountE :: Event (Sum Int)) <- newTellEvent
   (tellAdjustTodoCount, adjustTodoCount :: Event (Sum Int)) <- newTellEvent
   ...

Then on every todo add, completetion, delete, toggle, etc. you tell Sum 1 or Sum (-1). This is quite error prone (you might miss something which should influence the count), but at the same time much less error prone than doing it with IORefs, because you still have the trustable FRP semantics backing you up, making sure that whatever happens at time t stays true at time t.

Why do this?

I think what you get is like a manual version of what you’d get writing a fully declarative TodoMVC with all the optimizations, e.g. using Reflex’ Incremental data type.

A better interface?

Because tell/result always come in pairs, you might as well keep them in a single value. This could have Profunctor implemented on it.

Future of programming potential

I feel like having to use newTellEvent to do this is just a limitation of the current textual programming model. In an editor you might just be able to click at all the event producing locations deep in the code and combine them in a certain way, after which you can use the result anywhere else.

The hard part would be to decide what “context” to use, because if a button is used all over a program you almost certainly want only a subset of buttons to count for your event.