Skip to content

proposal: x/exp/future: new package to implement futures #56461

Open
@DeedleFake

Description

@DeedleFake

Futures have been proposed previously in various forms, most notably in #17466 where it was turned down due to being too unimportant to be done as a language change. Since then, generics have been added to the language, making it quite simple to implement futures in a type-safe way without needing any language changes. So here's a proposal to do just that.

Conceptually, a future functions similarly to a one-time use channel that can yield the value that it's given more than once. The benefit over just a normal channel is that it simplifies a 1:M case where multiple receivers need to wait on the same piece of data from a single source. This covers some of the cases, for example, that #16620 is meant to handle.

Here's a possible implementation:

package future

import (
	"fmt"
	"sync"
	"time"
)

type Future[T any] struct {
	done chan struct{}
	val  T
}

func New[T any]() (f *Future[T], complete func(T)) {
	var once sync.Once
	f = &Future[T]{
		done: make(chan struct{}),
	}

	return f, func(val T) {
		once.Do(func() {
			f.val = val
			close(f.done)
		})
	}
}

// Unsure on the name. Maybe Ready or something instead?
func (f *Future[T]) Done() <-chan struct{} {
	return f.done
}

// Maybe Value instead of Get? Blocking in Value feels weird to me, though.
func (f *Future[T]) Get() T {
	<-f.done
	return f.val
}

And an example of the usage:

func example() *future.Future[string] {
  f, complete := future.New[string]()
  go func() {
    time.Sleep(3 * time.Second) // Long-running operation of some kind or something.
    complete("Voila.")
  }()
  return f
}

func main() {
  r := example()
  fmt.Println(r.Get())
}

This API is patterned after context.Context. The exposure of the done channel makes it simple to wait for a future in a select case, but it's not necessary to use it to get the value in a safe way.

If the package was deemed to be useful after sitting in x/exp, it could be promoted to either x/sync or directly into sync.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Incoming

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions