4 ms·
Seems like one has to be very out of touch with modern approaches to concurrency to like callbacks. Sounds flamey, but I'm just not sure how one can look at Go
by sausagefeet 15y ago
Seems like one has to be very out of touch with modern approaches to concurrency to like callbacks. Sounds flamey, but I'm just not sure how one can look at Go, or Erlang, or even Ocaml/Lwt with syntax extensions, and think "yes, callbacks are superior to this".
- lobster_johnson 15y agoSurely Erlang uses callbacks heavily, in the sense that you pass functions around. Supervisors, for example, depend heavily on callbacks to receive notifications when something happens to the processes it supervises.
- sausagefeet 15y agoYou're confusing things. Callbacks are not the unit of concurrency in Erlang, a process is, which is a sequence of steps. You implement callbacks for behaviors in Erlang, but that is an interface system, not a concurrency unit.
- lobster_johnson 15y agoWell, I was referring to the "interface system", which is a pretty significant detail when you are comparing two programming approaches. The fact that Erlang has a richer set of concurrency semantics — that, among other things, let you quite easily manage a graph of dependencies so that your callback logic becomes much simpler — doesn't change the fact that it relies on callbacks (something "calls back" a function in order to supply information asynchronously) as a fundamental unit of programming. I don't know gevent in detail, but I suspect that it's possible to create an Erlang-style process/supervisor-style framework on top of it, albeit with less elegance and more syntax than Erlang. That would change the underlying concurrency mechanism, but the interface — callbacks — would remain very similar.
- sausagefeet 15y agoA callback in Twisted/Tornado/Node is a handler for the result of some asynchronous action happening. "Download this webpage and call ME with the results". A callback in Erlang is some functionality in a service a process provides. You call a callback to perform some task then it gives it back to you, possibly synchronously in the case of gen_server:call. Using a function in the Twisted/Node style would not make sense since a process is the fundamental unit of concurrency in Erlang so message passing is the only way to communicate back and forth. It would simply make no sense to ask a service in Erlang to "call THIS function when you're done" in most cases. The function would run in the wrong process and this isn't how you construct Erlang code. The semantics are completely different. A callback in Erlang is much closer to a webservices, for example, where you ask for some work to be done and it gives you the result. As far as I know, Gevent does not use the 'callback' terminology for anything. If you want to redefine "callback" to be "any method to communicate between two piece of code" then you're free to, but we aren't talking about the same thing then and nobody else will know what you're talking about.
- lobster_johnson 15y agoI'm arguing that the semantics aren't that different. Callbacks (such as used in Node and in libevent/libev-based async programs, as I said I don't really know gevent) and Erlang's process model both depend on individually dispatched messages to communicate events that are often fine-grained in nature: connection accepted, bytes received from buffer, failure occurred, child process died, name resolution completed, etc. This necessarily fragments the logic by requiring that each event be handled pieacemeal, either in separate callbacks or, in Erlang's case, often using pattern matching. I am arguing that the way you code these "callbacks" is superficially similar both in Erlang and in other async systems, and that the patterns, challenges, etc. are very similar, even though Erlang usually outcompetes the alternatives in terms of power and richness.
- sausagefeet 15y agoI really so very little similarity between how Erlang handles this communication and Node. Erlang processes communicate that events happen, sure. But resulting code has almost zero similarity to that of Node. Individual paths of execution don't even exist in Node since everything is jumbled together. Relating a callback to a pattern match is a rather naive comment.
- yorhel 15y agoRather flamey indeed, but let me take the bait. I've been using callback-style programming in many applications, ranging from IRC bots to application servers and whatnot. I've also been playing around with Go for several months and tried very hard to implement something that is inherently asynchronous. I gave up after about two months, I just couldn't get used to the concurrency model. I always end up with either data races, deadlocks and have otherwise no good overview of the program logic. This is exactly what the article describes, and seems to apply to me as well. Incidentally, while figuring out how to implement something inherently asynchronous in Go I looked at some IRC client implementations, and noticed how all of those used callback-style programming one way or another. That doesn't inspire much confidence. :( All in all, I am not sure yet whether this is purely due to my inexperience with concurrent programming or whether I am just inherently incompatible with it. I'd love to be able to play with alternatives to callbacks in the future, but for now it's just way over my head for the things I want to do.
- sausagefeet 15y agoIt appears, much like FORTH, callback coding rots the brain. I am surprised you had such issues with deadlocks in Go, it takes some effort to write code that deadlocks in Go (I don't mean that a deadlock cannot be shown with a trivial amount of Go code, but that the style of programming tends to lend itself to not having deadlocks). > and noticed how all of those used callback-style programming one way or another. What do you mean precisely here, because I think you are conflating two concepts. There is a distinction between using a callback as a unit of concurrency (do this async task and call THIS function when you're done), and genuinely being an event handler (call THIS function when you get receive a particular IRC event). I would expect to see the latter in Go for IRC code, not the former. But there is another aspect of this too, beyond code looking prettier. Go/Erlang/Haskell scale to multiple cores as the runtime evolves to support this. Callback codes doesn't, it can't. This doesn't seem to be because callback developers don't see the value in multiple cores, Node has built-in support for spawning a VM per core. This is quite restrictive. Callback developers have to either limit themselves to solving problems that don't need to communicate with each other or use some other means to communicate between VMs. That doesn't sound like progress to me. But shrug, plenty of people don't neat things in Node so maybe it doesn't matter.