Live data from Hacker News

What Color Is Your Function? (2015)

journal.stuffwithstuff.com

61–70 of 90 posts

Re: What Color Is Your Function? (2015)

#61
post #16

Earlier quoted context omitted.

Sometimes you want to block the main thread, or at least finish what you were doing. With async/await and cooperative concurrency you can be explicit about what will run on a thread. You retain control of the thread until you yield or await. You can ask tasks to complete on other threads or post back to the main thread. You have a lot of control. Its easy to write code without locks that runs concurrently on the main…

You can spawn a goroutine that immediately waits on a channel, so that it will not actually do anything until you want it to. This seems at least as expressive as a single-threaded switch() primitive. I think it can also express the threaded async pattern you want, but I'm not sure.

[deleted]

Re: What Color Is Your Function? (2015)

#64
This is something I ran into recently when learning about promises and async functions in javsscript. It took me a while to grok it, and I'm still not sure if I fully understand it, but we settled on using try/catch blocks with typescript's await keyword on calls we need to block on.

I wanted to use @usefultools/monads to have Maybe types, etc, but it's easier to just stick with promises throughout the chain. Essentially working with functions that return promises (like database calls with Mongoose) makes you convert your functions to also use promises. As far as I can tell there's no way to unwrap a promise (similar to Rust) without the await keyword inside a try block.

I don't know if this is exactly the best approach, but it's significantly less verbose than .then().catch() callbacks. I also learned about Promise.all() and Promise.allSettled() recently here [0].

I wish I could stick to futures with Rust or goroutines with golang. Bleh.

[0]: https://news.ycombinator.com/item?id=23223881

Re: What Color Is Your Function? (2015)

#65
I've been thinking why exactly async-await was chosen at the function level, and not the caller level.

I mean for languages that have event loops at their core, why isn't every function `async` by default? Let the caller decide how it wants to use the function.

`async` functions don't wait for a result of a `Future` unless `await` is used.

Instead of putting all that effort into putting `await` everywhere, why not invert that logic and make them `await` by default, and instead have the async-await syntax used by callers?

Like if one to were to write a transpiler to do this in dart, it might compile the following -

    void main() {
        Future xFuture = async doSomething()
        int x = doSomething()
         
        assert(await xFuture == x);
    }

    int doSomething {
        ...
    }
into -

    Future main() async {
        Future xFuture = doSomething()
        int x = await doSomething()

        assert(await xFuture == x);
    }

    Future doSomething async {
        ...
    }

This might be stupid, but my solution to the "what color is your function problem" is to make every function async.

I looks an awful lot like what go does with its `go` syntax, but because you have an event loop with Future and Async Streams, you don't need to worry about dealing with CSP.

Re: What Color Is Your Function? (2015)

#66
post #65

I've been thinking why exactly async-await was chosen at the function level, and not the caller level. I mean for languages that have event loops at their core, why isn't every function `async` by default? Let the caller decide how it wants to use the function. `async` functions don't wait for a result of a `Future` unless `await` is used. Instead of putting all that effort into putting `await` everywhere, why not in…

I suspect this is for two reasons:

* Many popular languages today predate modern asynchronous computing. This makes async-as-default impossible because it's an afterthought

* Async computing is harder to learn and confusing for those just picking up the language. There are some pretty major ergonomics issues that you have to solve if you want anything more than a DSL to achieve noteworthy adoption levels.

Re: What Color Is Your Function? (2015)

#67
post #65

I've been thinking why exactly async-await was chosen at the function level, and not the caller level. I mean for languages that have event loops at their core, why isn't every function `async` by default? Let the caller decide how it wants to use the function. `async` functions don't wait for a result of a `Future` unless `await` is used. Instead of putting all that effort into putting `await` everywhere, why not in…

I have thinking something similar but with generators:

    //A async candidate
    fun read_lines(file):
        for line in file:
            yield line  }

    let lines = read_lines("hello.txt").await //turn async 

I wonder why something like this is not used. Exist a bad interaction that could arise from this? Maybe about how nest stuff?

    fun uppercase_lines(file):
        for line in read_lines(file):
            yield line

    let lines = uppercase_lines("hello.txt").await //turn async, but also recursivelly read_lines?

Re: What Color Is Your Function? (2015)

#68

Earlier quoted context omitted.

Except in Go you could just as easily call: foo := bar() and if bar() is a function that does something annoying or time-consuming before finishing its work and returning the value, it just behaves like a normal, synchronous function call. “Await” is, as TFA explains, syntactic sugar for slapping an async function into behaving like a normal synchronous function call. In Go there is no reason you can’t just write you…

> In Go there is no reason you can’t just write your function calls synchronously in the first place. There are exactly the same reasons in Go to want asynchronous calls as there are in C# or Java or C++, except that performance of multi-threaded code (which is the semantics of goroutines) is much nicer in Go. Sure, channels can sometimes be a nice alternative to locking, if you can afford all of the copying. But Go…

> There are exactly the same reasons in Go to want asynchronous calls

Which isn’t the comparison I’m replying to here. Calling an asynchronous function with “await” forces it to behave synchronously. You would only do such a thing if you were operating in a framework or language with colored functions.

> in C# or Java or even C++ you can chose between using that OR asynchronous code with futures.

Java, C#, and C++ have channels?

Re: What Color Is Your Function? (2015)

#69
post #30

Earlier quoted context omitted.

Except in Go you could just as easily call: foo := bar() and if bar() is a function that does something annoying or time-consuming before finishing its work and returning the value, it just behaves like a normal, synchronous function call. “Await” is, as TFA explains, syntactic sugar for slapping an async function into behaving like a normal synchronous function call. In Go there is no reason you can’t just write you…

You mean if you rewrite bar so it didn't use a channel? That's syntactically different though, isn't it? Would the Go runtime lock you to that specific thread and block it or sleep that goroutine and move it to another thread when its no longer sleeping? Maybe in Go there's no difference in those concepts but in many languages certain threads are special. UI threads are often used for event serializiation but beyond…

Worth pointing out that invoking “await” in some languages also permits a context switch.

Re: What Color Is Your Function? (2015)

#70
post #16

Earlier quoted context omitted.

I don’t see how futures can be more flexible than go routines. Could you explain that more? And why couldn’t you just spawn a goroutine to avoid blocking your main thread?

Sometimes you want to block the main thread, or at least finish what you were doing. With async/await and cooperative concurrency you can be explicit about what will run on a thread. You retain control of the thread until you yield or await. You can ask tasks to complete on other threads or post back to the main thread. You have a lot of control. Its easy to write code without locks that runs concurrently on the main…

I'm working on a UI app in Go, yes I think you'd use channels and message passing but it doesn't look all that bad in practice.

First you'd probably call runtime.LockOSThread() to tie a goroutine to an OS thread if the native API you're coding against needs that (like Cocoa, OpenGL, etc).

To perform work on the main thread using closures is probably the most convenient way. So you just have a RunOnMainThread(func(){...}) that puts the closure on a channel to run on the main thread (or uses some similar feature from the underlying native framework).

It's not terribly inconvenient - it's similar to the old Cocoa / UIKit performSelectorOnMainThread: method except that you have closures to make things simpler.

There's probably a little syntactic overhead compared to if you had several async/await functions running concurrently on the main thread. But on the other hand it's probably a bit easier to reason about, since the flow of execution on the main thread is very straightforward (if you have multiple async/await functions running at the same time on the main thread, it seems like every time they await a result they'd have to worry about other code being run on the main thread, and make sure they release any locks, etc).

Post reply on HN