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: Go 2: Generator and yield syntax #56320

Closed
masx200 opened this issue Oct 19, 2022 · 6 comments
Closed

proposal: Go 2: Generator and yield syntax #56320

masx200 opened this issue Oct 19, 2022 · 6 comments
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided.
Milestone

Comments

@masx200
Copy link

masx200 commented Oct 19, 2022

Author background

generator syntax is built into many popular languages, such as JavaScript, python, kotlin; In Go, to implement such a similar function requires the help of coroutines, which makes the code very cumbersome, and it is not possible to automatically garbage collect the coroutines where the generator is located as other languages do, and if you forget to close the coroutine, it will cause the coroutine to leak.

I think the new generator syntax may be able to learn from JavaScript's generator syntax, which can be used without Coroutines and can be used for automatic garbage collection.

  • Would you consider yourself a novice, intermediate, or experienced Go programmer?
  • What other languages do you have experience with?

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Generator

https://docs.python.org/3/reference/expressions.html?highlight=yield#yield-expressions

https://kotlinlang.org/docs/sequences.html#from-chunks

function* InOrderIterator(root/*: TreeNode | null*/)/*: Generator<number> */{
    if (!root) return;
    yield* InOrderIterator(root.left);
    yield (root.val);
    yield* InOrderIterator(root.right);
}
func* InOrderIterator(root *TreeNode ) *genIter[int] {
  if root == nil {

			return
		}
    yield* InOrderIterator(root.Right);
    yield (root.Val);
    yield* InOrderIterator(root.Left);
}
fun main() {
    val oddNumbers = sequence {
        yield(1)
        yieldAll(listOf(3, 5))
        yieldAll(generateSequence(7) { it + 2 })
    }
    println(oddNumbers.take(5).toList())
}
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

let g = gen();
// "Generator { }"
def echo(value=None):
    print("Execution starts when 'next()' is called for the first time.")
    try:
        while True:
            try:
                value = (yield value)
            except Exception as e:
                value = e
    finally:
        print("Don't forget to clean up when 'close()' is called.")

generator = echo(1)
print(next(generator))


print(next(generator))

print(generator.send(2))

generator.throw(TypeError, "spam")

generator.close()

Related proposals

  • Has this idea, or one like it, been proposed before?
    • If so, how does this proposal differ?
  • Does this affect error handling?
    • If so, how does this differ from previous error handling proposals?
  • Is this about generics?
    • If so, how does this relate to the accepted design and other generics proposals?

Proposal

  • What is the proposed change?
  • Who does this proposal help, and why?
  • Please describe as precisely as possible the change to the language.
  • What would change in the language spec?
  • Please also describe the change informally, as in a class teaching Go.
  • Is this change backward compatible?
    • Breaking the Go 1 compatibility guarantee is a large cost and requires a large benefit.
      Show example code before and after the change.
    • Before
    • After
  • Orthogonality: how does this change interact or overlap with existing features?
  • Is the goal of this change a performance improvement?
    • If so, what quantifiable improvement should we expect?
    • How would we measure it?

Costs

  • Would this change make Go easier or harder to learn, and why?
  • What is the cost of this proposal? (Every language change has a cost).
  • How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?
  • What is the compile time cost?
  • What is the run time cost?
  • Can you describe a possible implementation?
  • Do you have a prototype? (This is not required.)
@gopherbot gopherbot added this to the Proposal milestone Oct 19, 2022
@masx200
Copy link
Author

masx200 commented Oct 19, 2022

Note that you must remember to cancel Coroutines here.
If you forget to cancel Coroutines, there will be no error,
but it will lead to Coroutines leakage!

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

func kthLargest(root *TreeNode, k int) int {
	i := 1
	ctx, cancel := context.WithCancel(context.Background())
	gen := ReverseInOrderIterator(ctx, root)

//Note that you must remember to cancel Coroutines here.
// If you forget to cancel Coroutines, there will be no error, 
//but it will lead to Coroutines leakage!
	defer cancel()
	for v := range gen {

		if i == k {
			return v
		}
		i++
	}
	panic(errors.New("unreachable"))
}
func ReverseInOrderIterator(ctx context.Context, root *TreeNode) (gen chan int) {
	gen = make(chan int)

	go func() {

		defer close(gen)
		if root == nil {

			return
		}


	select {
		case <-ctx.Done():
			return
		default:
			{}
		}

		YieldAll(ctx, gen, ReverseInOrderIterator(ctx, root.Right))
		YieldOne(
			ctx, gen, root.Val)

		YieldAll(ctx, gen, ReverseInOrderIterator(ctx, root.Left))

	}()

	return
}
func YieldAll(ctx context.Context, target chan int, source chan int) {

	for v := range source {
		YieldOne(
			ctx, target, v)

	}
}
func YieldOne(ctx context.Context, target chan int, source int) {

	select {
	case <-ctx.Done():
		return
	default:
		target <- source
	}

}

@seankhliao seankhliao added LanguageChange v2 A language change or incompatible library change WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. labels Oct 19, 2022
@seankhliao seankhliao modified the milestones: Proposal, Go2 Oct 19, 2022
@ianlancetaylor
Copy link
Contributor

I'm sorry, I don't understand what the actual proposal is here. I see Kotlin code, and I see existing Go code, but I don't see any suggested new Go code.

You may find it interesting to read the appendix of #54245 as it covers a similar topic.

@masx200
Copy link
Author

masx200 commented Oct 20, 2022

I'm sorry, I don't understand what the actual proposal is here. I see Kotlin code, and I see existing Go code, but I don't see any suggested new Go code.

You may find it interesting to read the appendix of #54245 as it covers a similar topic.

runtime.SetFinalizer(r, (*genIter[E]).Stop)

Notice the phrase "runtime. SetFinalizer", this is the biggest trouble with using generators under the current syntax!

@ianlancetaylor
Copy link
Contributor

Thanks, I understand the problem with the current syntax. What I don't understand is what you are proposing.

@seankhliao seankhliao added WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. and removed WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. labels Dec 17, 2022
@gopherbot
Copy link

Timed out in state WaitingForInfo. Closing.

(I am just a bot, though. Please speak up if this is a mistake or you have the requested information.)

@masx200

This comment was marked as spam.

@golang golang locked and limited conversation to collaborators Apr 27, 2024
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 WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided.
Projects
None yet
Development

No branches or pull requests

4 participants