-- | Composable events
data Event a where
SumEvent :: Event a -> Event a -> Event a -- The first event to fire will be returned.
AppEvent :: Event (a -> b) -> Event a -> Event b -- Both events should fire, and then the result is returned.
PureEvent :: a -> Event a -- Create a fake event. The result is useable with no delay.
EmptyEvent :: Event a -- An event that is never fired.
BindEvent :: Event a -> (a -> Event b) -> Event b -- The first event should fire, then a second event is created using the result.
BaseEvent :: BaseEvent a -> Event a -- Embed a base event.
ShortcutEvents :: [Event a] -> ([Maybe a] -> Maybe b) -> Event b -- The function is called each time an event fires, as soon as the result can be computed from the available data, it is returned, dismissing the events that haven't fired yet.
instance Functor Event where
fmap f a = pure f <*> a
instance Applicative Event where
pure = PureEvent
(<*>) = AppEvent
instance Alternative Event where
(<|>) = SumEvent
empty = EmptyEvent
instance Monad Event where
(>>=) = BindEvent
return = PureEvent
instance MonadPlus Event where
mplus = SumEvent
mzero = EmptyEvent
The Applicative instance is good if you have two events and you want
both of them to fire ("and"). The Alternative instance is good if you have two events and you need only one to fire ("or").
But
what if you have several events, but you need only a part of them to
fire in order to construct a final result? Say you have 10 events, but the 5 first to fire will give you enough data to construct a result.
You cannot do that with
Applicative/Alternative because with Applicative, you need *all* events
results, with Alternative you need *only one*.
That's why I added this primitive "ShortcutEvents" in my DSL, but I'm not convinced by it. So my questions are:
1. is ShortcutEvents expressible in term of Applicative/Alternative/Monad/MonadPlus?
2. if not is their a well known typeclass that covers this case?
3. if not is their a better way to write it? I especially don't like the list of Event, I'd prefer a more generic writing. What if I want a structure containing the events, instead of a list? What if I want event of various types (say a pair (Event a, Event b) for example)?
Note that I'm not working with streams of events (like in traditional FRP frameworks): just with single events (the "BaseEvents") that I want to combine with each other. Those "BaseEvents" will fire only once. The final result of the combination of events will trigger a callback.
Cheers,
Corentin