Closed Shimuuar closed 10 years ago
In which situations/code examples do you find scanE
to be more convenient?
The thing is that I'm intentionally discouraging fold-like type signatures for the accumulation functions. I have found that they tend to invite the creation of extra data types for each event, which clutters code very quickly. But I'm happy to discuss examples.
Actually I found accumE
diffucult to use and use scanE
instead. Maybe it's beacause I too used to think in terms of folds. For example following function for merging event streams. Of course it's trivial to rewrite it using accumE but I found it easier to use scanE
ap :: (a,Event a) -> (b, Event b) -> IO ((a,b),Event (a,b))
ap (a0,evtA) (b0,evtB)
= scanE step (a0,b0) $ joinE evtA evtB
where
step (_,b) (Left a) = (a,b)
step (a,_) (Right b) = (a,b)
joinE :: Event a -> Event b -> Event (Either a b)
joinE ea eb = unionWith const (Left <$> ea) (Right <$> eb)
Your examples demonstrates what I mean with "invites the creation of extra data types for each event". :smile:
As you say, it's not difficult to replace the use of scanE
with a direct use of accumE
. But the point is that you can do even more than that: you can get rid of the intermediate Either
type.
ap :: (a,Event a) -> (b, Event b) -> IO ((a,b),Event (a,b))
ap (a0,evtA) (b0,evtB) =
accumE (a0,b0) $ unionWith (.) (stepA <$> evtA) (stepB <$> evtB)
where
stepA a (_,b) = (a,b)
stepB b (a,_) = (a,b)
As an additional benefit, the code will now be correct when the two events occur simultaneously. Your original code discards the left occurence, which is probably not what you want. (In fact, you could say that joinE
maps to the wrong type: the case where both events occur at once cannot be represented by Either
.)
I think I'm starting to understand what do you mean. Problems arise because of simultaneous events. Without them both functions are equivalent and choice is purely matter of taste. But we have to take simultaneous events into account when we merge event streams and we need event values to form semigroup (we don't necessarily want associativety though). accumE
works fine because a → a
is a monoid. On the other hand folds puts no such restriction on event type.
It's probably worth mentioning in the documentation. But it's all a bit vague
I found this combinator quite handy and usually more convenient than accumE