Version 0.5 of my library reactive-banana for functional reactive programming is looming on the distant horizon and I have a significant API change to discuss with you, dear users.
(Do you like bananas? Support the project with a small donation: )
The first, harmless change is that I will remove the FRP
class. In the beginning, it was useful for testing my push-driven implementation against the model implementation. But now I have found a way to do that without the overhead of an additional class, so we can get rid of it; a move that greatly simplifies type signatures.
Unfortunately, the second change will make type signatures more complicated again: I intend the main types Event
, Behavior
and NetworkDescription
to get an additional parameter t
, like this:
accumE :: a -> Event t (a -> a) -> Event t a
fromPoll :: NetworkDescription t (Behavior t a)
Of course, I have profound reasons for this change, in total three:
The parameter will be used to guarantee the absence of time leaks in my upcoming implementation of dynamic event switching. There will be types like
switchE :: (forall t. Trim s t (Event s (Event t a))) -> Event s a
Wolfgang Jeltsch has developed beautiful theory that reinterprets functional reactive programming as the Curry-Howards Isomorphism for constructive temporal logic. Brilliant! Of interest for us is that the seemingly superfluous parameter t
has meaning, it corresponds to a “starting time”.
Without the parameter, it is actually possible to break library internals. This is similar to how the parameter for the ST
monad prevents mutable state from escaping. For instance, the following program uses an event e
way out of its intended scope:
compile $ do
e <- fromAddHandler something
let anotherNetwork =
actuate <=< compile $ reactimate e -- Oops!
reactimate $ anotherNetwork <$ e
This program can be outlawed by giving compile
a second-rank type.
compile :: (forall t. NetworkDescription t ()) -> IO EventNetwork
Still, I’m not entirely happy with the change, because it makes type signatures slightly more annoying, especially for new users. Also, GHC’s recent monomorphic local bindings restriction might force you to spell out type signatures.
Concerning 1, Gergely Patai offers another option, but it’s not really simpler than the additional parameter. Point 2 can be ignored. I’m willing to sacrifice point 3, the idea being that the library comes with an API for beginners and an API for advanced users
type Beginner.Event = Adept.Event Start
type Beginner.Behavior = Adept.Behavior Start
where the beginner version does away with the seemingly superfluous type parameter. If you want to use dynamic event switching, you have to become an adept and also change a few type signature. Code will mostly stay compatible, though.
Should I go ahead with adding the type parameter or do you think that the convenience of the beginner version is worth the cost? Comments appreciated!