I'm working on a program using reactive-banana, and I'm wondering how to structure my types with the basic FRP building blocks.
For instance, here's a simplified example from my real program: say my system is composed primarily of widgets — in my program, pieces of text that vary over time.
I could have
newtype Widget = Widget { widgetText :: Behavior String }
but I could also have
newtype Widget = Widget { widgetText :: String }
and use Behavior Widget
when I want to talk about time-varying behaviour. This seems to make things "simpler", and means I can use Behavior
operations more directly, rather than having to unpack and repack Widgets to do it.
On the other hand, the former seems to avoid duplication in the code that actually defines widgets, since almost all of the widgets vary over time, and I find myself defining even the few that don't with Behavior
, since it lets me combine them with the others in a more consistent manner.
As another example, with both representations, it makes sense to have a Monoid
instance (and I want to have one in my program), but the implementation for the latter seems more natural (since it's just a trivial lifting of the list monoid to the newtype).
(My actual program uses Discrete
rather than Behavior
, but I don't think that's relevant.)
Similarly, should I use Behavior (Coord,Coord)
or (Behavior Coord, Behavior Coord)
to represent a 2D point? In this case, the former seems like the obvious choice; but when it's a five-element record representing something like an entity in a game, the choice seems less clear.
In essence, all these problems reduce down to:
When using FRP, at what layer should I apply the Behavior
type?
(The same question applies to Event
too, although to a lesser degree.)