SodiumFRP / sodium

Sodium - Functional Reactive Programming (FRP) Library for multiple languages
http://sodium.nz/
Other
851 stars 140 forks source link

Why support many firings of one Event in a transaction? #39

Closed pyrtsa closed 9 years ago

pyrtsa commented 9 years ago

I'm not sure if the idea of the same Event firing many times "simultaneously" has that much real use. Doesn't it make the model harder to reason about? If there's an Event that could yield many values at once, why not reflect that in the type signature, e.g. Event [a]? (Omitting the r type parameter here for simplification.)

If the semantics of Event was simplified to exclude multiple simultaneous firings…

mergeAsList :: Event a -> Event a -> Event [a]
mergeAsList x y = mergeWith (++) ((:[]) <$> x) ((:[]) <$> y)
instance Monoid a => Monoid (Event a) where
    mempty = never
    mappend = mergeWith mappend

P.S. I'm not sure how it works right now, but I'd also define sync such that no changes caused by the transaction itself are observable within the transaction. That has to do with the semantics of listen and execute as well. But maybe that's a topic worth another discussion (was thinking about this some time ago, but I'm forgetting the details right now).

the-real-blackh commented 9 years ago

You may be right. I think it may be possible to force the person always to deal with simultaneity by making only mergeWith available, not merge. I really haven't thought this through.

P.S. I'm not sure how it works right now, but I'd also define sync such that no changes caused by the transaction itself are observable within the transaction.

It does work that way.

pyrtsa commented 9 years ago

I strongly recommend reopening this issue. Life is just too complicated with this design where Streams can have multiple firings per transaction:

type S a = [(T, a)]         -- for non-decreasing T values
type C a = (a, [(T, a)])    -- for increasing T values

Why not make them both require increasing T values?

Additionally, at least accum and collectE seem to be broken in the Haskell implementation. They both ignore all but the last firing per transaction.

HeinrichApfelmus commented 9 years ago

In reactive-banana, I have also used the "multiple occurrences per Event" model, but I now intend to switch to the proposed "one occurrence per Event" model as well. It forces the programmer to think about simultaneous occurrences explicitly, which I think is a good thing.

the-real-blackh commented 9 years ago

All right - I'm persuaded. Let's do it. Now's the time to do it - before I've finished the book. Any counter-arguments?

HeinrichApfelmus commented 9 years ago

The only counterargument I can think of is that we lose the completely general instance Monoid (Event a), but I think that's a fair price to pay.

pyrtsa commented 9 years ago

Yeah, already mentioned in my first comment. Fortunately, we can still define

instance Monoid a => Monoid (Event a)

or theoretically even

instance Semigroup a => Monoid (Event a)
the-real-blackh commented 9 years ago

94d26530c44ef98bce27a78fa1074486a04dcaf1

commit 94d26530c44ef98bce27a78fa1074486a04dcaf1
Author: Stephen Blackheath <docks.cattlemen.stephen@blacksapphire.com>
Date:   Fri Jul 24 10:41:10 2015 +1200

    Issue #39: Why support many firings of one Event in a transaction?
    With this change, now we don't in the denotational semantics and in the Java
    version of Sodium. Other versions will follow as they get revamped to be in
    line with the Java version.