Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: extend language or stdlib with future-like sync primitive #22293

Closed
daskol opened this issue Oct 16, 2017 · 13 comments
Closed

proposal: extend language or stdlib with future-like sync primitive #22293

daskol opened this issue Oct 16, 2017 · 13 comments
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Milestone

Comments

@daskol
Copy link

daskol commented Oct 16, 2017

Three things define completely programming language. They are type system, memory model and standard library. In Golang type system is not outstanding for now, quite good standard library and pretty modern memory model which is strong side of language especially in concurrent environment. Bellow I propose how to make strong side of lang stronger.

Motivation

Additional synchonization primitives on the level of standard library or language itself can improve readability and simplify source code. First of all, one should think about future.

The main advantage of future object is that panic can be handled nearby the place where a value of a delayed call is needed. Thus, if panic occures in some go-routine, it is handled in another go-routine. This behavious is relevant for wrapping of external library code.

On the other hand, there is a need to get the result of an asynchronous procedure execution. Certainly, channels could handle such communication issue. They allow a transmision of a value from one go-routine to another where it is required. First, the channel could be represented as a pair of promise object and future object in such communication scheme. However, such representaion is excessive. Second, there is no simple way to get the state of the go-routine execution (only the state of the result).

Examples and proposals

In my opinion, the best way to start is to check out an idiomatic modern C++ implementation.

    std::future<int> future = std::async(std::launch::async, [](void) int {
        return 42;
    });

    .....
    std::cout << future.get() << std::endl;

The C++ example is not exactly portable due to the absence of generic types for now, so it forces us to use dynamic types (interfaces).

    future := Go(func() RType {
        return 42
    })
    .....
    result, _ := future.Get()
    println(result.(int))

Moreover, it seems that it would be better to specify behavior of the go keyword in Golang to allow go-routine to return a value and forward panic situation.

    future := go func() int {
        return 42
    }()
    .....
    result, _ := <-future
    println(result)

Probably, more drastic change in the language is an addition of the keyword await. It will increase readability and clarity, however, to my mind, this change is too dramatic since it suppresses flexibility and complicates implementation.

@gopherbot gopherbot added this to the Proposal milestone Oct 16, 2017
@ianlancetaylor ianlancetaylor added v2 A language change or incompatible library change LanguageChange labels Oct 16, 2017
@ghost
Copy link

ghost commented Oct 16, 2017

Some previous discussion: #17466

@index0h
Copy link

index0h commented Oct 16, 2017

@daskol You don't need features.
If you want to recover panics from another goroutine you should resend them to current goroutine, by error channel, or something like result{value, error} object. Because in any case you will wait for result.

With future/promise you will increase complexity, without real profit.

@daskol
Copy link
Author

daskol commented Oct 17, 2017

@index0h Sure, it is not necessary to add new language expression. Here I implement future using standard lib. You presisely described my implementation. But what do you mean? In any case anyone will wait for result of execution a line of his or her code.

With feature/promise you will increase complexity, without real profit.

No, this is completely wrong. Look at definition of channels and futures. Future is read reduction of channel. Both channel and future are high level sync primitives. Channels are usefull in general case, however if it is needed only to get value people use futures(or channels avaliable for reading) to get result.

@ianlancetaylor
Copy link
Contributor

Channels provide a capability that is otherwise unavailable in the language. The version of futures that you are presenting is already available in the language. So I don't agree that they are both sync primitives; channels are a primitive and futures can be built on top of channels.

It seems to me that you are suggesting some syntactic sugar for a particular channel operation. That then leads to the question: why this sugar? Is this an operation so common that it merits extra support in the language? My guess is that it is not. For example, this syntactic sugar offers no way to cancel the operation if you decide later on that you don't need it.

@daskol
Copy link
Author

daskol commented Oct 17, 2017

@ianlancetaylor I think academia guys do not think so[1]. Moreover, notion of eventual consistency actually is defined with happened-before relation which is promise or future or channel. So, future and channel are located on the same level. Future and channel hardware implementation is based on CAS, TAS or LL/SC operations that are real primitives. In this sence you are right that future is not primitive.

In my point of view, such syntactic sugar is usefull at least in web application development. See issue #17466 where @funny-falcon gave examples in context requests to KV storage.

Well, the current golang version cancels only with context package. It is tricky and not very convenient. Is it appropriate if go statement made object that provides ways to cancel or identify a go-routine?

There is another one argument in order to "overload" go statement that is conceptual integrity. Why does the keyword apply only to functions which do not return anything but take arguments and even capture the execution context?

@ianlancetaylor
Copy link
Contributor

@daskol I agree that futures and channels are on approximately the same level of power, but my point is simply that when you already have channels, you don't also need futures. The paper you cite is describing a system that doesn't have channels, so that the fact they find futures to be useful doesn't tell us much one way or another.

Your other comments do not seem to me to be clearly related to this proposal. Perhaps they should be discussed on golang-nuts or some other forum.

@index0h
Copy link

index0h commented Oct 17, 2017

@daskol

Sure, it is not necessary to add new language expression.

proposal: extend language or stdlib with future-like sync primitive

It sounds like a contradiction.

With feature/promise you will increase complexity, without real profit.

No, this is completely wrong. Look at definition of channels and futures.

My comment was abount language change.

Channels are usefull in general case, however if it is needed only to get value people use futures(or channels avaliable for reading) to get result.

If you want to use futures, it's ok. But channel is a "generic" type, where you can set type, which it'll process. In futures you can use only interface{} so you must always cast types, or use code generation.

Here I implement future using standard lib.

Your example is not working(( Use channels

func main() {
	result, ok := future.Go(func() future.RType {
		time.Sleep(time.Second * 2)
		return "test"
	}).Get()

	println(result.(string), ok)
}
// panic: interface conversion: future.RType is nil, not string

Look, I'm writing this because your proposal not solve any problems, but it creates new instrument to resolve same tasks as channels.
For example JS: at first time you had only callbacks, one function for error and result. Next you got promises, two functions for success and error results. Next you got async/await, cool! But in real life you also must use callbacks + promises + async/await. Hi backward capability.

In golang it'll be just the same.

@funny-falcon
Copy link
Contributor

Imho, my proposal (#17466) were much more in Go's way.

This one is more "lets use technique I learned from other language" than "lets implement useful primitive in a way useful in this language".

I could be mistaken.

@andlabs
Copy link
Contributor

andlabs commented Oct 18, 2017

The question to ask now is: what can futures do that Go cannot already do with channels and/or package sync?

@daskol
Copy link
Author

daskol commented Oct 18, 2017

@ianlancetaylor Next time I will refer to more sensible papers. However, the paper intoduces notion of future. Everyone should know definition of channel but not future. So now everyone can just compare definition and understand that future and channels for reading are the same.

Okay, except one point. You can think that I propose generalization of go statement to functions that return value. It seems to be natural continuation.

@index0h You improperly understand the appearance futures in JS. The fact is the simplest way to implement asynchronous behavior like coroutine, generators, channels, etc is to wrap some piece of code in future-promise pairs.

Your example is not working(( Use channels

You use my example in wrong way. This is not the right place to explain. (Hint: read from closed channel of type chan interface{} and cast to string).

I don't understand rest of your comments. They are quite messy.

@funny-falcon Futures, promises and channels is about 40 years old. Well, they are older than almost all modern language. I pull this idea from academia. I suppose that closed form of go statement is useful, consistent and clear. By the way there is a paper that propose the current generic type model in Java. So, may be Golang borrows a priori some concepts from other languages. I know nothing specific for Go.

@andlabs That is not right question. I can you ask similar questions too. What can channels do that Go cannot already do with package sync? What can channels do that Go cannot already do with channels? Every language has a bit of redundancy or excess. That things differ programming language from DSL.

@daskol
Copy link
Author

daskol commented Oct 18, 2017

Try repeate again. In general I propose the following code to be correct in some sence

    future := go func() int {
        return 42
    }()

The important thing is needed to discuss what properties does future have? The future could be a channel or new generic type or new not generic type and so on.

@funny-falcon
Copy link
Contributor

@dascol

Futures, promises and channels is about 40 years old.

Yes, I know. And I also proposed future/ivar. I just made it closer to Go's way, I think. And your way is more like C++.

I could be mistaken.

@ianlancetaylor
Copy link
Contributor

This is essentially the same as #17466, which was closed. Closing this one as well. See #17466 for discussion.

@golang golang locked and limited conversation to collaborators Mar 21, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Projects
None yet
Development

No branches or pull requests

6 participants