-
Notifications
You must be signed in to change notification settings - Fork 140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Java: flatMap #64
Comments
I think this is equivalent, but I might be wrong:
|
Actually it's probably this:
|
Now I understand why we need switchS. Thank you. Looking at code I found that
public fun <A> Stream<Stream<A>?>.flatten(): Stream<A>
Sodium.tx {
Operational.value(cell).flatten()
}
Sodium.tx {
stream.map { request() }.flatten()
} |
We have a problem here. This example is not working: fun foo(i: Int): Stream<String> {
val sink = Sodium.streamSink<String>()
sink.send("A" + i)
return sink
}
val l = sink.map {
foo(it.value)
}.hold(null).switchS().listen { out.add(it.value) } If resulted stream has firings, them not appear in listen because they cleaned by What do you think? |
The hold introduces a delay, so it's working as the semantics define. If you need the stream to output something when the switch happens, you'll need to do that separately. To put it another way, you can do what you want with Sodium as specified, but you can't do it that way. |
Depending on the problem, defer() might solve it. |
Where I must place |
"true" flatMap is something that hasn't been defined denotationally, so the whole thing would need to be thought through. Rx has no respect for event simultaneity and this means compositionality is completely broken in Rx. FRP's biggest advantage has just been thrown out the window. fun foo(i: Int): Stream<String> {
val sink = Sodium.streamSink<String>()
sink.send("A" + i)
return sink.defer() <-------------
}
val l = sink.map {
foo(it.value)
}.hold(null).switchS().listen { out.add(it.value) } |
It's probably more instructive to look at what actual problem you're trying to solve. Then we've got something to test the advantages and disadvantages against. |
It does not work. I dont know why for now. Logically this was to help... Hm. Actually im trying to use Sodum in my project. The fun queryImage(url: String): Stream<Bitmap> {
val bm = cahce.get(url)
if (bm != null) {
val out = Sodium.streamSink<Bitmap>()
out.send(bm)
return out
}
return queryFromServer(url)
}
clicked().flatMap {
queryImage(...)
}.listen(...) |
I think it should work. If it doesn't, it's a bug. If you can't make it work, can you reproduce it in a test case in Java for me? If you do that, I'll fix it. return out.defer() Now the cache lookup and I/O are both working the same way. This is just the sort of case that I think defer() is for. |
It does not work because same reason. The |
Ah, OK - that's a bug. I'll find some time and fix it. |
Fixing it now. |
I can't reproduce it in the Java version. I've written the test case below. Please add this to your test suite and see what happens. Check that the post() tasks are executed after the last() ones. NOTE: I've checked this in. public void testSwitchAndDefer()
{
List<String> out = new ArrayList();
StreamSink<Integer> si = new StreamSink();
Listener l = Cell.switchS(si.map(i -> {
Cell<String> c = new Cell<>("A"+i);
return Operational.value(c).defer();
}).hold(new Stream<String>())).listen(x -> { out.add(x); });
si.send(2);
si.send(4);
l.unlisten();
assertEquals(Arrays.asList("A2", "A4"), out);
} |
My code is not working. Hm... Can you explain why you code works? Here test case that shows loosing of value by defer: public fun testDefer2() {
val out = ArrayList<String>()
val sink = Sodium.streamSink<Int>()
val l2 = arrayOfNulls<Listener>(1)
val l = Sodium.tx {
sink.map {
const("A").operational().value().defer()
}.listen {
l2[0] = it.value.listen {
out.add(it.value)
}
}
}
sink.send(1)
l2[0]?.unlisten()
l.unlisten();
TestCase.assertEquals(listOf("A"), out);
} It fails. |
I've finished a day of book writing today so I'll get to this when I can. |
I found a bug in my |
Now it complitly works. But I dont like the fact that |
They don't lose events as such. It's the way the semantics are defined. The question, "Can we improve the semantics?" is always open. And, we should investigate these questions. But, these semantics are the same as Conal Elliott's definition and I have found they work in practice. Reactive Banana works the same way too. |
Back to fun foo(i: Int): Stream<String> {
val sink = Sodium.streamSink<String>()
sink.send("A" + i) // <--- Exception!
return sink
}
val l = sink.map {
foo(it.value)
}.hold(null).switchS().listen { out.add(it.value) } The public fun <A> just(value: A): Stream<A> {
val sink = StreamWithSend<A>()
Transaction.apply2 {
sink.send(it, Value<A>(value))
}
return sink
} It works without exceptions, but now I need |
There are two things to be said here:
These are the semantics of Sodium, and, as I said, they're basically the same as Conal Elliott's FRP. A lot of thought went into their design. flatMap() from Rx can't be immediately shoe-horned into this design. |
The Here: fun foo(i: Int): Stream<String> {
val sink = Sodium.streamSink<String>()
Promise.call(Executors.DB) {
// query from DB.
}.then(Executors.UI) {
val items = result.map { ... }
sink.send(items)
}
return sink
} This simple code will fail if called from UI thread and the |
I consider what you're doing there to be a legitimate way of doing things, and I discuss this in the section on promises in the book... I've got some re-writes to that section that aren't in the MEAP yet. What I recommended is different in some small details, though: Use the Operational.value(new Cell(i)) idiom to create a "spark" and listen to it with the (new) listenWeak() method. You should do this instead of using I/O "directly". Use addListener(l) on the StreamSink (where l was returned by listenWeak()). Have the promise callback call sink.send(). This must be on a different thread. You might have to explain how your promise works ... I don't quite understand about result and Promise.then. Must go to bed now. |
Here the code of my Promise implementation: https://github.com/romansl/Promeso And one more thing. enum Executors {
// ...
UI {
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final long mUIThreadId = Looper.getMainLooper().getThread().getId();
@Override
public void execute(@NotNull final Runnable command) {
if (Thread.currentThread().getId() == mUIThreadId) {
command.run();
} else {
mHandler.post(command);
}
}
},
// ...
} |
Library lacks primitive
flatMap
. It has strong practical usage. But I dont know how to implement it in terms of FRP.Example:
Or is there a way to do it without
flatMap
?The text was updated successfully, but these errors were encountered: