Futures implemented in Go

2,353 views
Skip to first unread message

Ryan Slade

unread,
Oct 3, 2012, 12:29:54 PM10/3/12
to golan...@googlegroups.com
Hi

I read this article earlier and it made me wonder what the best way to implement something similar in Go would be, specifically the bit about futures near the beginning:

I have a naive solution below using waitgroups but I feel I'm missing a more idiomatic / cleaner way of doing this. The only constraint is that I don't want to have to modify the name(), score() or friends() methods. I'd like a nice way of firing off a few methods concurrently and waiting until they've all returned a value.

package main

import (
"log"
"sync"
"time"
)

type Profile struct {
Name    string
Score   float32
Friends []int
}

func name() string {
time.Sleep(1 * time.Second)
return "Ryan"
}

func score() float32 {
time.Sleep(1 * time.Second)
return 50
}

func friends() []int {
time.Sleep(1 * time.Second)
return []int{1, 2, 3}
}

func main() {
log.Println("Getting things")

var n string
var s float32
var f []int

var wg sync.WaitGroup

log.Println("Getting name")
wg.Add(1)
go func() {
n = name()
wg.Done()
}()

log.Println("Getting score")
wg.Add(1)
go func() {
s = score()
wg.Done()
}()

log.Println("Getting friends")
wg.Add(1)
go func() {
f = friends()
wg.Done()
}()

wg.Wait()
p := &Profile{Name: n, Score: s, Friends: f}

log.Println(*p)
}


Thanks
Ryan



Henrik Johansson

unread,
Oct 3, 2012, 12:54:48 PM10/3/12
to Ryan Slade, golan...@googlegroups.com

Why would you need futures when you have goroutines and channels? I think they pretty much covers it.

Den 3 okt 2012 18:29 skrev "Ryan Slade" <ryan...@gmail.com>:

chl

unread,
Oct 3, 2012, 1:13:05 PM10/3/12
to golan...@googlegroups.com
I made the code shorter and closer to your ideas of futures:

Ryan Slade

unread,
Oct 3, 2012, 1:24:23 PM10/3/12
to Henrik Johansson, golan...@googlegroups.com
Exactly, I'm asking what the Go way of approaching it is using channels and go routines. But I want to avoid making the methods providing the data return channels.

David Leimbach

unread,
Oct 3, 2012, 1:55:59 PM10/3/12
to golan...@googlegroups.com, Henrik Johansson
If you want you could have a function that creates a channel, and a goroutine that uses that channel to deliver the result, returning a function that returns the answer.

Or you can just return the channel (not sure why to avoid it)

See below:

Ryan Slade

unread,
Oct 3, 2012, 2:22:06 PM10/3/12
to golan...@googlegroups.com, Henrik Johansson
Thanks all. 

I like both approaches but prefer the Waitgroup one from chi as it doesn't affect the implementation of the other methods.

Joubin Houshyar

unread,
Oct 4, 2012, 9:42:36 AM10/4/12
to golan...@googlegroups.com


On Wednesday, October 3, 2012 12:29:54 PM UTC-4, Ryan Slade wrote:
Hi

I read this article earlier and it made me wonder what the best way to implement something similar in Go would be, specifically the bit about futures near the beginning:


A go channel of depth 1 is basically a denuded future, and semantically identical.  The minor select/wait on timer garnish is the light standard fare to dress it up. Whether to type that in call site or having it wrapped behind an API is more sensible is a tough call in Go. 

The positive points of the API approach are clearly decoupled implementation from semantics, and, possibility for more uniform treatment of richer interaction semantics.  

For example, we can define a 'Promise' to be a specialized future with a fixed deadline set at instantiation via (say) future.NewPromise(deadline time.Time).  That fixed deadline (time.Time) is a bit of state that we have nowhere to put if using a channel, so every call site of the 'Promise' would (for example) need to get this bit of info (e.g. for the select timeout bit.)  

Thomas Bruyelle

unread,
Oct 1, 2013, 9:02:35 AM10/1/13
to golan...@googlegroups.com
Hi folks

I'm a go newbie so sorry if my question sounds stupid. 
About futures implementation in go with channels, I found it really nice. The way I prefer is to make the async functions return a channel, but I faced a drawback. The data can only be retrieved one time from the channel, after that the future is useless. 

So I created a type Future, see http://play.golang.org/p/AZ7_kdNZnx, which keeps the value from the channel and can deliver it any time. 

As nobody in this thread suggest solution like that, I wonder if it's stupid or no ? Or probably already exists somewhere ?

Thanks

Steven Blenkinsop

unread,
Oct 1, 2013, 11:41:51 AM10/1/13
to Thomas Bruyelle, golan...@googlegroups.com
This isn't synchronized properly. There's no guarantee that a goroutine observing f.result != nil will also observe the correct value for f.result.

How about this:

Of course, the issue with doing it this way is that you have to use interface{}, or rewrite the type for each result type. Considering how basic this is to do with channels, it might not be worthwhile.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

atomly

unread,
Oct 1, 2013, 1:47:23 PM10/1/13
to Steven Blenkinsop, Thomas Bruyelle, golan...@googlegroups.com
think you meant "f" and not "future" on line 11...

also, i think it would make sense to handle errors with your future...

i wouldn't use this, but just to make a point:


:: atomly ::

[ ato...@atomly.com : www.atomly.com  : http://blog.atomly.com/ ...
[ atomiq records : new york city : +1.347.692.8661 ...
[ e-mail atomly-new...@atomly.com for atomly info and updates ...

Steven Blenkinsop

unread,
Oct 1, 2013, 2:16:28 PM10/1/13
to atomly, Thomas Bruyelle, golan...@googlegroups.com
On Tuesday, 1 October 2013, atomly wrote:
think you meant "f" and not "future" on line 11...

 I did, yes ;).


also, i think it would make sense to handle errors with your future...

i wouldn't use this, but just to make a point:


That's not an error case, since the whole point was to let you call Get more than once. When the channel returns ok == false, that just means that f.result already has the result stored in it.

Steven Blenkinsop

unread,
Oct 1, 2013, 2:19:54 PM10/1/13
to atomly, Thomas Bruyelle, golan...@googlegroups.com
Also, I suppose I should mention again that that isn't proper synchronization, since you have unsynchronized reads and writes to the error field.

atomly

unread,
Oct 1, 2013, 2:31:23 PM10/1/13
to Steven Blenkinsop, Thomas Bruyelle, golan...@googlegroups.com
oops, duh... well, yeah, regardless i've always known futures to allow error cases, though perhaps that is an unnecessary complication.

:: atomly ::

[ ato...@atomly.com : www.atomly.com  : http://blog.atomly.com/ ...
[ atomiq records : new york city : +1.347.692.8661 ...
[ e-mail atomly-new...@atomly.com for atomly info and updates ...


Thomas Bruyelle

unread,
Oct 2, 2013, 3:29:46 AM10/2/13
to golan...@googlegroups.com, Thomas Bruyelle
Thanks Steven your solution solves a sync issue and is very elegant ! 
You are right, the main issue is the need of rewrite the type, and afaik there is no generics in go.

I coded a kind of constructor for Future so the user doesn't have to create the channel. Also I tried to implement error handling, but don't know if it's good to use the same interface{} value to store the result or the error. Maybe I should use multiple return values.




Rasmus Schultz

unread,
Jan 12, 2014, 11:23:33 AM1/12/14
to golan...@googlegroups.com, Thomas Bruyelle
I was initially tempted to implement lots of little things, such as promises/futures, in Go - but I am quickly learning to change my opinion... the truth of the matter is that futures is just a pattern - and in Go it is actually a very easy and simple pattern to implement without any framework or helper functions:

https://sites.google.com/site/gopatterns/concurrency/futures

I find that a lot of things for which I would write (or find) "frameworks" in other languages are simple and natural in Go, and frameworks for things that are already simple in Go don't tend to add anything - in many cases, they actually take something away.

Just my $.02 :-)

Caleb Spare

unread,
Jan 12, 2014, 2:31:16 PM1/12/14
to Rasmus Schultz, golang-nuts, Thomas Bruyelle
In Go the typical style is for APIs to be blocking. The "future matrix" API in that link is quite unidiomatic. It's so simple for users to call your API function asynchronously, if desired, that there's no reason to add it into the interface directly. This also adds complexity to your library code and makes it harder to use.

If you look at the standard library, there are very few packages where the interface presented is a channel. The ones that do involve messages which need to be delivered at some unknown future time: os/signal and time.{Timer, Ticker} come to mind.


Reply all
Reply to author
Forward
0 new messages