Futures in Go
blog.charmes.net
Futures in Go
1–10 of 15 posts
Re: Futures in Go
#2 var (
ch = make(chan *http.Response)
err error
)
....
return func() (*http.Response, error) {
return
It seems to me like calling that returned function is destructive -- if you block on it more than one, it would block forever. Is that right? It seems like a different contract than the usual Future / Promise shtick.Re: Futures in Go
#3var ( ch = make(chan *http.Response) err error ) .... return func() (*http.Response, error) { return It seems to me like calling that returned function is destructive -- if you block on it more than one, it would block forever. Is that right? It seems like a different contract than the usual Future / Promise shtick.
Re: Futures in Go
#4You should return a chan. In this case, all crawlers should be returning along the same chan, errors or results, rather than creating one per request. Then you A: don't have a problem where you could be processing result #5 but result #3 is blocking right now, increasing the total latency, B: having multiple consumers using the same channel is trivial and at the user's discretion, and C: the resulting chan can participate in a "select" of the user's choosing, unlike a function call. There is no way in which using this approach is superior to using a chan.
If you look at the wikipedia page for "futures", which has a lot more than just any one community's idea of "futures" in it, you can see that a channel that you expect to read one value from is, if not necessarily exactly any one of the given definitions, certainly a family member. It isn't in the type system, but it is still the case that if you have a need for a "future" in Go you're better off with a chan with that constraint documented, rather than returning a function that can be called, because of the inability to select on such a function. http://en.wikipedia.org/wiki/Futures_and_promises
Re: Futures in Go
#5Create a new goroutine and block on the action. You can continue working in another goroutine and communicate between them using channels.
The advantage of this programming model over futures/promises is straight-line code that is easier to read and reason about. For example, when bugs appear you get a nice stack trace that reveals the state of the call. Future leave you in a callback-like mess.
See http://blog.golang.org/pipelines for more details.
Re: Futures in Go
#6In Go, each value on a channel goes to a single reader and it's rare to need multiple readers. One way to do it might be to return a channel that receives an infinite stream of the same value.
Re: Futures in Go
#7The correct way to do this is to return results on a chan of (error, response) regardless of how much you "don't like it" for unspecified reasons.
Re: Futures in Go
#8Futures are not necessary in Go, since they are a hack around not having the concurrency that Go supports natively. This is why the Go community never talks about "futures"... as in this example, they are a regression vs. the already-supported primitives, not progress. Channels are already a more general, more powerful, and most importantly, more composable version of the "futures" idea that is what most people consi…
Re: Futures in Go
#9Note that when designing servers at Google in Go, we strongly discourage the use of concepts like futures and promises. They exist as abstractions that work around heavyweight threads. In Go, you have lightweight threads which remove the need for futures. Create a new goroutine and block on the action. You can continue working in another goroutine and communicate between them using channels. The advantage of this pro…
It seems to be because each goroutine has a single parent (which launched it), so there is typically a single reader for its return value.
Re: Futures in Go
#10Note that when designing servers at Google in Go, we strongly discourage the use of concepts like futures and promises. They exist as abstractions that work around heavyweight threads. In Go, you have lightweight threads which remove the need for futures. Create a new goroutine and block on the action. You can continue working in another goroutine and communicate between them using channels. The advantage of this pro…
I don't think I've ever heard it clearly explained why you rarely need futures in Go. (The blog article doesn't explain it either.) It seems to be because each goroutine has a single parent (which launched it), so there is typically a single reader for its return value.
Generally, futures/promises just solve the problem of how to simulate blocking within an event-loop-driven (i.e. cooperatively-scheduled) platform, e.g. Node. When actor-processes are cheap, your consumer can be an actor which just actually blocks on a result—and then continues execution in the same linear block of code when that result arrives.
To put it another way: in non-actor languages, you'd see a block of code like this...
do_random_other_work();
bfn_args = some_setup();
blocking_fn(bfn_args).then(function(result) {
console.log(result);
});
more_random_other_work();
...while in actor-modelled languages, it'd go more like this: do_random_other_work();
spawn(function() {
bfn_args = some_setup();
result = blocking_fn(bfn_args);
console.log(result);
});
more_random_other_work();
Note how you have a closure in both cases, but they contain different parts of the code. The actor-modelled closure contains the setup work of sending the message, as well as the work of dealing with the reply—which must be linear with respect to one-another, but which can all happen concurrently to the random other work.As well, notice that the actor-modelled version of blocking_fn is actually a plain old blocking function—a synchronous-IO read, for example. The blocking_fn in the promise-modelled code, on the other hand, is a special function which must be defined using a promises library, and must do the work of ensuring its asynchronicity at definition-time, whether or not a given consumer wants to call the function in an async way relative to its own thread of execution.
In short, idiomatic actor-modelled code puts the consuming programmer in control of which tasks are linear and which are concurrent, which usually makes for succinct, reusable plainly-synchronous functions that Get Things Done, and higher-level glue code that represents all the policy of what concurrency happens where. Promises throw out all these advantages and mix policy with mechanism in a way that increases verbosity and decreases reusability.