6 ms·
Yes. The callback is not a natural construct (i.e. it does not map well to our intuitive understanding of X is doing something while Y is doing something else).
by jcarrano 3y ago
Yes. The callback is not a natural construct (i.e. it does not map well to our intuitive understanding of X is doing something while Y is doing something else).
I'm annoyed when coroutines are reserved for use only in high performance, c10k-type of situations. For example, the KJ library's doc says:
"Because of this, fibers should not be used just to make code look nice (C++20's co_await, described below, is a better way to do that)."
With this "stackless or nothing" attitude we do not have good C++ coroutine libraries outside C++20's.
For me the purpose IS to make the code look nice and I do not care if the coros are stackfull and consume more stack memory. At the end of the day, for my application, I saved more in programmer's time and bugs than I lost in RAM (and I'm on an embedded board with only 64MB)
- vlovich123 3y ago> For example, the KJ library's doc says Just to be clear, the audience for that tour is primarily Cloudflare engineers working on workerd / Workers runtime. Within that context, that's very much correct. The entire codebase is written using asynchronous I/O - synchronous I/O doesn't show up (or if it does, it's in weird parts I've never looked). Fibers are used sparingly in very specific contexts and we have very special code to make it memory efficient at our scale.
- DSingularity 3y agoI interpreted that as “if you can go stackless prefer it for the coroutines-for-elegance use case”.
- z3t4 3y agoI want to add to the callback vs coroutines and async/serial discussion that it all depends on how you treat errors. Are errors mere exceptions or do you want to handle errors in the control flow ? For example turndeg(90), Move(10), PickupItem(), turndeg(180), Move(10) you treat errors as exceptions, if the robot fail to pickup the item, or if it ends up at the wrong place it's an exception. Now if you put all these in try/catch, have the functions return error code (or 0 for success), or use callbacks, the code will be more "ugly" yes, but you then treat errors as "first class citizens", if the robot for example fail to pickup the item you want to try something else, maybe apply more vacum to the suction arm, or switch to a grip arm. And if the robot goes off course you want to make a course direction. async/await, coroutines, futures, promises, do make the code "look nice", but that nice look comes from treating errors as exceptions.
- spc476 3y agoI have a gopher server written in Lua [1] that uses coroutines to handle each connection. This bit of code is executed as the main menu page [2] is being displayed (with comments) -- ------------------- -- Load in some modules. -- The first makes a gopher menu item of type link -- The second allows us to do a TCP connection -- -------------------------- local mklink = require "port70.mklink" local tcp = require "org.conman.nfl.tcp" -- ------------------------------ -- tcp.connect() connects to the given address and port and timeout -- (in seconds). This function will create a socket, set it -- to non-blocking, call connect, then yield. When the socket has -- connected, the coroutine is then resumed with the connection; if -- 1 second has passed before the connection is done, then nil is -- returned when resumed. This just calls a local QOTD service. -- --------------------- local ios = tcp.connect("127.0.0.1",'qotd',1) if ios then local res = "" -- -------------------------------- -- The following loop will yield the coroutine until a line -- of data has been accumulated from the network. The coroutine -- is then resumed with the line of text. -- --------------------------------------- for line in ios:lines() do -- ----------- -- We're just accumulating the text into one long blob of -- text that we'll return -- ----------------- res = res .. mklink { type = 'info' , display = line } end ios:close() -- another yield point, when closed, resume return res else return mklink { type = 'info' , display = "Not Available" } end No exceptions here. If we can't connect, it's a simple 'if' test and do something else. The functions `tcp.connect()`, `ios:lines()` and `ios:close()` are all blocking points that cause the coroutine to yield. Yes, if I put something like `while true do end` that will block the entire process as there is no preemption, but aside from that detail, I find this code easy to read. [1] https://github.com/spc476/port70/blob/master/share/index.port70 https://github.com/spc476/port70/blob/master/share/index.por... [2] gopher://gopher.conman.org/
- z3t4 3y agotip: In order to flatten those if-statements you can return early, so if you start with if not ios then return - you get rid of one if..else.
- 3y ago
- CyberDildonics 3y agoYes. What question are you answering 'yes' to? The callback is not a natural construct According to who? It is very natural to anyone making a GUI or anything interactive for the firs time. Eventually I would try to move people to queueing up events and handling them all at the same time so that the order is easier to debug. Coroutines are the latest silver bullet syndrome. Fundamentally you still need to synchronize and order data and that's the hard part. I don't know why students would need to do more than the classic interactive loop of: 1. get data 2. update state 3. interactive output (drawing a frame, moving a robot etc.)
- jcarrano 3y agoThe callbacks (as in a chain of callbacks) are not natural to a beginner, like a student trying to program a robot. For a GUI, where you set up a callback in response to user input, it is also easy to understand because the program's control flow does not progress as a chain of callbacks. The input-update-output loop is fine, but you cannot easily combine and nest such loops. For example, in a robot you may have a speed control inner loop, then a path following loop and on top of that a high-level behavior loop. Nesting one inside the others you end up with a hand-baked implementation of a coroutine.
- CyberDildonics 3y agoas in a chain of callbacks That's not what you said at first and there is no reason to assume that callbacks automatically mean a chain of callbacks. The input-update-output loop is fine, but you cannot easily combine and nest such loops. So don't, where is this assumption that a technique is bad because it might ignore what makes it good and warp it into something terrible? For example, in a robot you may have a speed control inner loop, then a path following loop and on top of that a high-level behavior loop. Nesting one inside the others you end up with a hand-baked implementation of a coroutine. Then don't do that. You have a loop, do what you need inside of it. If you don't need to do something every time, skip it most of the time. This isn't rocket science. If a certain architecture doesn't work, don't do it like that. A car doesn't work well if you drive it backwards either.
- 3y ago
- ilyt 3y ago>I'm annoyed when coroutines are reserved for use only in high performance,c10k-type of situations Go's pretty much that idea. Make the threads light as coroutines (I think it's like 4k or 8k per goroutine) and give some basic messaging (channels) to go with it. Works well but some of the simplicity ended up biting it in the arse so there can be quite a bit of boilerplate in some cases.