Comments (12)
My recommendation is to remove coalesce
and (the combiner-less overload of) merge
altogether¹, and to make StreamSink
assert when more than one event is being sent with sink.send(value)
.
@romansl I think you've got a point we can solve there, but I'm not entirely following – what exactly makes Sodium.tx
dangerous here?
¹) For convenience, the library could provide the left and right biased combiner functions separately.
from sodium.
The marge
without arguments can combine events to Pair<A, A>
or List<A>(2)
.
Also marge
can have signature Stream<A>.marge(b: Stream<B>, combiner: (A, B) -> C)
.
Transaction is dangerous because can accidentally loose important events.
from sodium.
I don't think Pair<A, A>
is a good default. You'd only merge two things like that. But then when you have 3 streams you want to merge, you'd end up with something like Pair<A, Pair<A, A>>
. …Let alone N streams to merge!
The merging of streams has always been closely related to monoids or actually, since there never ever is a stream firing with nothing – that'd be absurd! – semigroups.
With that thought in mind, the way to merge things into a List<A>
would be to map
each of the inputs into single-element List<A>
's, and then merge
those with list concatenation as the combiner.
In Haskell, we could even redefine the old merge
conditionally as:
merge :: Semigroup a => Stream a -> Stream a -> Stream a
merge x y = mergeWith (<>) x y -- or, for short: merge = mergeWith (<>)
which is exactly what I described above.
Further, Stream a
could be made conditionally a Monoid
if there's a default way to append a
's together:
instance (Semigroup a) => Monoid (Stream a) where
mempty = never
mappend = merge
from sodium.
Transaction is dangerous because can accidentally loose important events.
Yeah, I'd try hard to make it impossible to send to the same stream more than once per transaction. If it can't be done with the interface, asserting a precondition would be my second best suggestion:
val sink = streamSink<Command>()
Sodium.tx {
sink.send(BeginCommand())
sink.send(SomeCommand()) // throws Exception: "StreamSink already sent"
// ...
}
from sodium.
Throwing exceptions makes it even worse. That if this happens inside logic construction?
And here we got that firing of the event in one part of code may affect another.
The send
must be as safe as possible.
from sodium.
May be send
must produce new transaction like defer
?
from sodium.
I think we come from different schools of thought here, and there's no Proven Right Way™ to write real-world FRP applications yet, so I value your ideas. Would like to understand them better.
I was talking about enforcing a precondition against send
ing multiple firings into a StreamSink
. Whether it's an Exception
or just Log.fatal("...")
is irrelevant.
And here we got that firing of the event in one part of code may affect another.
In my view, transactions should be very local beasts that do as little as possible, stuff like update the mouse state into what it currently is or such. One transaction shouldn't contain many "parts of code" (but I'd be interested in seeing what the counterexample would be).
That if this happens inside logic construction?
What do you mean by inside logic construction?
from sodium.
I've checked in ed0c403 which changes it so an exception is now thrown if send() is called more than once per transaction. Now we have this:
- coalesce() doesn't exist in the API.
- send() throws an exception by default if called more than once per transaction. This will generally not be a problem during a "one big transaction" program initialization because the code will tend to be deterministic so the bug will tend to be caught during testing. It will never be a problem where a transaction is started implicitly.
- Passing a combining function to StreamSink() allows you to change this policy.
- s1.merge(s2) without f will throw events away. This may seem dangerous but it's a tradeoff against convenience, because the case where a merge is done when the user knows the event can never be simultaneous is extremely common. Use of the Unit type here (where throwing one away doesn't matter) is also common. Convenience of the user interface is important to me because I see Sodium as competing for mainstream approval against the broken FRP implementations (Rx and friends).
Other options I didn't choose:
s1.merge(s2)
throws an exception. I think in a practical situation this is more dangerous than dropping the s1 event.static <A extends SemiGroup> Stream.merge(Stream<A> s1, Stream<A> s2)
would be extremely cumbersome in Java but the equivalent Haskell with typeclasses would work and I will most likely use that. Haskell programmers are used to much stricter APIs than Java programmers are.
So let me know if you think we can do better than this.
from sodium.
how about naming s1.merge(s2) "orElse" instead? it more clearly describes what the method actually does. and of course i'm used to scala's Option#orElse ...
from sodium.
That's certainly possible, but in the non-simultaneous case it's still a merge operation. Hmm.... I'm starting to like the idea.
from sodium.
It is good idea I think.
from sodium.
merge() renamed to orElse() as suggested.
from sodium.
Related Issues (20)
- Add Test For Transaction Regen To C# Version
- Fix Transaction Regen Bug In C# Version
- Add Test For Transaction Regen to Java Version
- Add Test For Transaction Regen To Scala Version
- Improve Transaction.Post
- Fix Send allowed in Operator
- Fix Send allowed in Operator
- Fix Send allowed in Operator
- Fix Send allowed in Operator
- More aggressive cleanup for switchC / switchS HOT 2
- FRP book, javascript code refactoring HOT 1
- Check whether rank bug needs to be fixed in Java version
- Check whether rank bug needs to be fixed in C# version HOT 1
- C# Cell Lift firing twice, but only propogating value from single execution HOT 2
- [1.17/dev] Game crashed after joined the server HOT 1
- Simultaneous timer events HOT 4
- Causality in the Haskell denotational semantics
- Alternative to ranks & priority queue
- Cannot create account on sodium.nz because https is down
- Cell WPF binding HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from sodium.