> Is this how you imagined it? (edited) @yilin.wei10 Yes. That is event-loop, right.
> you’re imposing languages to do it the javascript/node way rather than using threads/coroutines/whatever the language provides. @jestarray What I have to say is that “async/await in Javascript” and “coroutine (or generator) in Racket” have equivalent capabilities, though their implementation are different. Both can convert an unblocking operation to a blocking operation (at least from the perspective of syntax). But future&promise mechanism can do <https://stackoverflow.com/questions/35612428/call-async-await-functions-in-parallel|more than that>: await/yield
only supports vertical composition. future
supports vertical and horizontal composition (i.e. future-all, future-race) and it also supports error-handling. (Another advantage of future&promise is that they give a clear contract for asynchronous functions.)
> also gamedevs use threads to implement asynchronous operations all the time. I use rust for gamedev and everyone just uses threads. we have async await but its still iffy @jestarray Gamedev can use multi-threading indeed (it often depends on different game types and optimization strategies). But most games’ main thread has the following structure: ;; Graphic/UI thread
(let loop(())
(update) ; <--- logic code
(draw) ; <--- rending gameobject and UI
(loop))
Imagine that, in the logic code (update)
, we invoke an async operation (which need access network) to get score and then refresh the UI. It can not block UI normal drawing.
If you want to, for example, read from a channel but not block if nothing’s available, you might use sync/timeout
.
You might also want to use async-channel
@chansey97 sync
is like race
and https://docs.racket-lang.org/reference/sync.html?q=choice-evt#%28def._%28%28quote._~23~25kernel%29._choice-evt%29%29\|`choice-evt` is similar to all.
wrap-evt
is similar to map.
Does the example illustrate enough to answer your problems, or are there still issues which are unclear/you’re seeking answers for?
@yilin.wei10 I am not familiar with Racket evt. (sync/timeout 0 evt)
seems to be able to convert a blocking operation to non-blocking operation (like unblocking socket), but I dont know how to encode my code above by Racket evt. What channel
corresponds to ? promise
? What wrap-evt
corresponds to? then
? Is there any one-to-one correspondence between Racket event and promise?
Could you demonstrate how to use evt
to encode my code above? It would be nice if they can be combined with coroutine.
Argh! I just fixed a bunch of bugs and made some refinements. The engine was playing really well just now, but failed to generate one particular move for my opponent, so when I entered their move, it thought it was illegal, and I had to resign a good position. Moving a queen to a1, so I probably have an off by one error :)
Game is here. Letting Stockfish chew on it for a while showed my engine was up 1.37 points: 1. c3 e5 2. a3 Nf6 3. d4 exd4 4. Qxd4 Nc6 5. Qe3+ Be7 6. c4 O-O 7. Qd2 Bc5 8. Qd3 d6 9. b4 Bd4 10. Ra2 Be5 11. b5 Nd4 12. Nd2 a6 13. f4 Bxf4 14. Qxd4 Be5 15. Qd3 axb5 16. cxb5 Qd7 17. Ndf3 Re8 18. a4 Qe6 19. Ra3 d5 20. Nxe5 Qxe5 21. Qd2 Qe4 22. e3 Rxa4 23. Rxa4 Qxa4 24. Bb2 Re6 25. Bxf6 Rxf6 26. Qxd5 Rd6 27. Qc4 Qa1+
My engine has no notion of “king safety” yet - obviously :)
> wrap-evt
is similar to map. But future then
is more like bind
, not map
then : future a -> (a -> future b) -> future b
vs wrap-evt : evt a -> (a -> b) -> evt b
So unfortunately they are different thing. In other words, you can not do async operation in the callback of wrap-evt
: (define (async-rpc op x)
(match op
['A (thread (λ () (channel-put ch 11)))]
['B (thread (λ () (channel-put ch 22)))]
['C (thread (λ () (channel-put ch 33)))]))
(wrap-evt ch (λ (v)
;; Where can I write the continuation code for the async result of (async-rpc 'A 1)?
(async-rpc 'A 1)
))
I think you’d want either guard-evt
or replace-evt
Could you give me some examples of using guard-evt
and replace-evt
? I don’t really understand the guard-evt
and replace-evt
in the document. Or is there any reference or paper about Racket events?
Moreover, this is just one problem of evt
, another problem is that evt
has no “future source” which can trigger the callbacks’ chain. For example: (define e (wrap-evt ch (λ (v) 1 )))
;; (sync/timeout 1 e) ; this can trigger the e's callback, OK.
;; (sync/timeout 1 ch) ; but this can not trigger the e's callback!
;; So `ch` can not be thought of as a promise.
then
is polymorphic on the argument type, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then\|MDN
Do you mean you want the event to be memoized?
When you mean coroutines, do you mean userland coroutines, or the ones already in Racket?
I’ve possibly got the terminology wrong but Racket already has a form of preemptive scheduling through it’s threads. With events, these are “like” coroutines.
@yilin.wei10 > Do you mean you want the event to be memoized? I dont understand what you mean.
> When you mean coroutines, do you mean userland coroutines, or the ones already in Racket? userland coroutines (e.g. generator).
I just tried to use Racket events to simulate promise&future which like my code above and then I found a lot of problems. I think Racket currently may not have the equivalent mechanism like promise/future as Javascript.
In fact, promise&future can also support multithreading. (e.g. Scala ExecuteContext, C# TaskScheduler) For example, in C# you can do async operation (in another thread) to modify UI element by Task UITask= task.ContinueWith(() =>
{
this.TextBlock1.Text = "Complete";
}, TaskScheduler.FromCurrentSynchronizationContext());
But it will take more efforts to achieve this kind of things in Racket.
Uh, so futures and promises are ambiguous because the behaviour is not well-defined (you get a spectrum across different languages, lazy/eager, memoized/unmemo, default parallel/default not). Memoized is where the future is side-effecting and only runs once.
so f.race(f)) == f
semantically
Futures and promises are ambiguous, yes. For example, Javascript conflates future and promise. More detail, see http://dist-prog-book.com/chapter/2/futures.html
Despite the ambiguity, there is still a most common explanation of promise&future. That is > A Future
is a read-only reference to a yet-to-be-computed value. > A Promise
(or a CompletableFuture
/Completer
/etc.) is a single-assignment variable which the Future
refers to.
OK, but that has the same properties as a channel
and a single read evt
.
The problems of channel
and evt
, see https://racket.slack.com/archives/C09L257PY/p1611772879101400?thread_ts=1611680164.072800&cid=C09L257PY and https://racket.slack.com/archives/C09L257PY/p1611774676102100?thread_ts=1611680164.072800&cid=C09L257PY
@samth said they can be implemented by either guard-evt
or replace-evt
, but it has a bit difficult (for me).
@chansey97 My suggestion is that if you want to write concurrent programs in Racket, you should use threads. If you need to communicate asynchronously in some contexts, you should use sync/timeout
, async-channel
, etc. Futures and promises are an approach to concurrency, and Racket emphasizes a somewhat different approach.
I haven’t looked at asynchronous channel yet. I will look into it. The examples of @yilin.wei10 given earlier use synchronous channel.
I don’t think I used a channel?
The behavior of a port like a channel.
I’m extremely confused; but if it makes sense to you, that’s good.
I think if the concurrent programs do not involve the UI thread, there is no problem to use threads in Racket, because the form (thread ...)
creates a “green” thread. But if they involve UI thread, then promise&future are very useful.
There are lots of existing GUI programs written in Racket (such as DrRacket). They make extensive use of threads.
The problem of not blocking the UI thread is important, but that means that you don’t want to use blocking operations there. Using threads is the idiomatic way to do asynchronous prorgramming in Racket, which is thus how you avoid blocking the UI thread.
I haven’t seen many Racket GUI programs. I think one way to avoid blocking the UI thread is using a big state-machine. Or using coroutine in UI thread (but promise&future can do <https://racket.slack.com/archives/C09L257PY/p1611738469097500?thread_ts=1611680164.072800&cid=C09L257PY|more than that>).
@yilin.wei10 In your example, the tcp port is more like asynchronous channel, not synchronous channel. It is my fault sorry.
No need to be sorry :slightly_smiling_face: @chansey97, as long as it helps your understanding, it’s all good.
Oh, this was interesting. I just played someone on http://lichess.org\|lichess.org, and even though the engine could have mated in one move, it just kept gobbling up pieces. This was due to two factors: 1) it examines tactical moves, such as captures, before quiet moves, and 2) the alpha / beta infinite score was the same for a bunch of moves i.e. mate in 7, mate in 6, mate in 5, … , mate in 1, so it kept going down the path of capturing something instead of the shorter mate ! :) I just added/subtracted the depth from the min/max score to favor shorter mates, and that fixed it. The latest code is on the development branch, and should be reasonably good now: https://github.com/lojic/RacketChess/tree/development I’ll merge it after playing a few games on lichess.
can slideshow play gif/video files?