Live data from Hacker News

Callbacks as our Generation's Goto Statement

tirania.org

51–60 of 287 posts

Re: Callbacks as our Generation's Goto Statement

#51
post #7

"I have just delegated the bookkeeping to the compiler." That's not obviously a good thing. Debugging the compiler (or just figuring out why it did something, even if correct) is far more difficult than debugging application code. Given the choice between implementing behavior with application code (or a library function) or adding semantics to the language, I prefer the former because it's much easier to reason abou…

This is a nonsensical comment, and I voted it down. The same point can be made about any time languages got a level higher. This kind of rejection of powerful in favor of complex-but-familiar is precisely what Bret Vector warns against in the Future of Programming talk[1]. If anything, `await` makes debugging easier because you don't have to untangle callbacks and jump back and forth. You're not supposed to “debug th…

"Callbacks seem simpler to you not because they are simpler (try explaining them to someone just learning the language, and you'll see what I mean), but because you got used to them."

No, they're simpler in the literal sense: they introduce no new concepts into the language or runtime semantics. (The dynamic behavior is still complex, of course.)

"Even so, error handling and explicit thread synchronization make maintaining callback-ridden code painful. I think setting `Busy` to `false` in `finally` block is a great example (in the blog post). You just can't do that with nested callbacks—they are not that expressive."

Right -- nested callbacks aren't the answer, either. In JavaScript (where most of my non-C experience comes from), a good solution is a control flow function:

    busy = true;
    series([
        function (callback) {
             // step 1, invoke callback();
        },
        function (callback) {
            // step 2, invoke callback();
        },
        function (callback) {
            // step 3, invoke callback();
        }
    ],
    function (err) {
            // finally goes here
            busy = false;
            if (err)
                // ...
    });
This construct is clear and requires no extension to the language or runtime.

This is fundamentally a matter of opinion based on differing values. I just want to point out that there's a tradeoff to expanding the language and to dispel the myth that callbacks necessarily trade off readability when control flow gets complex.

Re: Callbacks as our Generation's Goto Statement

#52
Instead of using callbacks, golang embraces synchronous-style calls and makes them asynchronous by switching between goroutines (lightweight threads). gevent (for Python) does something similar. It's certainly an interesting approach IMO.

Re: Callbacks as our Generation's Goto Statement

#53
post #6

Earlier quoted context omitted.

Continuations don't help with the problem that the visual structure of callback-oriented programs doesn't reflect the order of execution. As a heavy JS programmer, that's the most compelling point for me in this post.

> the visual structure of callback-oriented programs doesn't reflect the order of execution. One of my bosses made this assertion about Object Oriented code that followed the Law of Demeter and other OO best practices. I don't think he's entirely the best OO person, or entirely on the right track. However, I would venture to say that all programming paradigms hit a point where visualizing the flow of control gets exh…

Callback-oriented code is different. With by-the-book OOP code you're still executing one line at a time. You might be teleporting in space, which has its own problems, but your code still reflects the order of execution.

With callback-oriented code you're teleporting in space and time.

They can both make it hard to trace the path of execution. At least with OOP code you have a sensible stack trace, though. ;)

Re: Callbacks as our Generation's Goto Statement

#54

I don't understand what the big deal is. Callbacks are OK. They're less cumbersome if the language you're using has smaller function definitions. Callbacks 'get crazy' when you've got more than one I think, and thankfully someone smart has made a library you can use to manage them! https://github.com/caolan/async Saying that, I don't mind the way things look with the whole await/async stuff in C# and etc. However I d…

How do you do this with callbacks?

    foreach (var player in players) {
        while (true) {
           var name = await Ask("What's your name");
           if (IsValidName(name)) {
               player.name = name;
               break;
           }
        }
    }
Assuming `Ask` is an asynchronous operation and must not block the UI thread.

Note that second player is only asked after the first player has given a valid name.

(And the code structure reflects that :-)

My point is of course it's doable with callbacks, but I spent more time indenting this code than writing it, and I darn well know I'm not smart enough to spell out the correct callback-style code in a comment field on Hacker News. And if I suddenly had to add error handling...

Re: Callbacks as our Generation's Goto Statement

#56
post #42

I love when Node.js advocates try to convince you that promises are as good a concept as anyone would need to handle asynchronous programming

Promises were removed from node core a long time ago, and remained unpopular until very recently. They are making a comeback due to lobbying in standards committees, forward-compatibility with ES6 generators, and the jQuery effect.

Re: Callbacks as our Generation's Goto Statement

#57
You get a similar interface in Python's Twisted using the @inlineCallbacks decorator:

    @inlineCallbacks
    def example():
        try:
            obtain_some_lock()
            ui_status("Fetching file...")
            result = yield fetch_file_from_server(args)
            ui_status("Uploading file...")
            yield post_file_to_other_server(result)
            ui_status("Done.")
        except SomeError as e:
            ui_status("Error: %s" % e.msg)
        finally:
            release_some_lock()
I must say that this style of writing async code is much friendlier than descending into callback hell.

There is work to make a similar async interface native in Python 3, in PEP 3156 http://www.python.org/dev/peps/pep-3156/, so this should become more widely available even to those who don't use Twisted.

Re: Callbacks as our Generation's Goto Statement

#58

Earlier quoted context omitted.

You're missing the point of `await`. Await does change how your program is structured, unless we're talking about most trivial cases. You can use `await` inside a `for` loop—can you do the same with callbacks without significantly re-structuring your code? What is the “callback analog” of placing something in a `finally` block that executes no matter which callback in a nested chain fails? You'd have to repeat that c…

I'm not disagreeing with you at all. I guess we're using "structured" in different ways. Libraries like TameJS do wind up creating structures to deal with for loops, in the same way you'd otherwise manually have to deal with. Likewise with exceptions (which I said are the main actual benefit to await, that can't be reproduced in normal callback routines). You obviously have to write a bunch of "plumbing" code to do w…

I see your point. Closures also don't affect structure on conceptual level, but I think they're pretty darn useful. By the way, async can affect things on conceptual level if you embrace[1] it (I posted this link somewhere below as well, but just in case you haven't seen it).

[1]: http://praeclarum.org/post/45277337108/await-in-the-land-of-...

Re: Callbacks as our Generation's Goto Statement

#59
post #57

You get a similar interface in Python's Twisted using the @inlineCallbacks decorator: @inlineCallbacks def example(): try: obtain_some_lock() ui_status("Fetching file...") result = yield fetch_file_from_server(args) ui_status("Uploading file...") yield post_file_to_other_server(result) ui_status("Done.") except SomeError as e: ui_status("Error: %s" % e.msg) finally: release_some_lock() I must say that this style of w…

That's very cool, thanks for sharing.

Re: Callbacks as our Generation's Goto Statement

#60
post #48

"Await" is fantastic, and having using it for JavaScript (via TameJS and then IcedCoffeeScript), it makes things a lot easier and clearer. That being said, I don't think the comparison between callbacks and goto is valid. "Goto" allows you to create horrible spaghetti-code programs, and getting rid of it forces you to structure your programs better. "Await", fundamentally, isn't really anything more than syntactic su…

Let's not forget that "if", "for", "while", "switch", and friends, fundamentally, aren't anything more than syntactic sugar for "goto" either. ;)

Very true. :)

The genius about them, however, is that with all those, we discovered that you could get rid of "goto" afterwards. Which was kind of amazing.

"Await", on the other hand, doesn't remove the need for callbacks -- it just makes it much easier to use them in a lot of common use cases, in a much clearer way. But there are still plenty of valid/necessary uses for callbacks that can't be handled by "await".

Post reply on HN