I'm guessing this is following the same / highly similar motivation as rsc's coroutines post about a month ago: https://research.swtch.com/coro
I welcome people embracing function closures more, as I feel they're a drastically under-used language tool (particularly in Go where everyone crams everything into single-func interfaces which are almost exclusively far worse). More exposure to it helps people become comfortable with it, and now that we have generics it's finally not a pile of pain and agony at all times. And just using functions makes it very flexible / doesn't repeat the "X for me but not for thee" issues of past Go versions.
I'm not entirely sure how I feel about this proposal though. It kinda feels like it's trying to put a round peg into a square hole. If you try hard enough, everything can go in the square hole! But that doesn't mean it's a good fit.
> particularly in Go where everyone crams everything into single-func interfaces which are almost exclusively far worse
Depending on scope, I've grown to prefer the single func interface.
For a smaller scope use, say filepath.Walk, absolutely, pass in a closure, good stuff.
For larger stuff, like say a chain of http middleware and handlers, I think it can be very limiting.
you get a big benefit from using the interface over a function: the underlying type _can_ have more methods and implement.
this gives room to implement a parallel interface, for example, to collect all possible routes, or to trace what handlers a request would pass through.
I don't think there's a one-size-fits all approach, use whatever gives the effort:utility tradeoff you're looking for.
Func references can have methods as well fwiw :) They just need to have a named type.
But sure, if you want to build a multi-method thing (or upcast to detect optional methods), an interface is the natural choice - it's no longer a single func.
It would be nice if the proposal defined the semantics of getting single item from the iterator (or detecting that it is out of items) and exposed an api for this, then defined the behavior of range as equivalent to some pattern using these primitives.
As is the proposal says "there should be some coro.Pull type thing" which is a bit terse and says nothing about whether the implementation of coro.Pull will be fast or whether it will be built on range + channels
I suspect getting a single item out will be that you just make a
func next[T](i iterfunc[T]) T {
var item T
i(func(it T) bool){
item = it
return true // likely
})
return item
}
and use it. Semantics should be the same as ranging would desugar to (since that's all this is), though the variable arity will be slightly annoying of course.
Aah, yes, that's an excellent point. Thanks! That does complicate things a lot more.
Quite a lot more, as it essentially means you can only consume from iterators you create... which sorta defeats the purpose of iterators as a value, and degrades them almost completely to just a `range` helper.
>To convert one of these functions to a "next-based" or "pull-based" iterator, we plan to add a function to the standard library that runs the yield-based ("push-based") iterator in a coroutine, along the lines of the coro.Pull function in my recent blog post.
that's... an option I guess. Unless the runtime special-cases this coroutine though it seems like it'll probably have non-trivial performance cost, roughly equivalent to the channel + goroutine equivalents people sometimes build now. Which seems... not great. Bad enough that anyone even slightly performance-sensitive will probably avoid it.
(his coro post also mentions a proposed runtime change to improve the performance, but afaik previous attempts to get this kind of change into the language haven't worked out reliably, and you can't trust callers to be single-threaded)
I don't believe so - the post is pretty much all about how you can do this right now in normal Go code. And e.g. a bit after that is a link to the "full code" which is runnable: https://go.dev/play/p/hniFxnbXTgH
Possibly you're interpreting yield as a new keyword? It's just an argument to Pull.
The only not-(currently-)available-in-Go stuff that I can recall from the post is the performance optimization at the very end.
I welcome people embracing function closures more, as I feel they're a drastically under-used language tool (particularly in Go where everyone crams everything into single-func interfaces which are almost exclusively far worse). More exposure to it helps people become comfortable with it, and now that we have generics it's finally not a pile of pain and agony at all times. And just using functions makes it very flexible / doesn't repeat the "X for me but not for thee" issues of past Go versions.
I'm not entirely sure how I feel about this proposal though. It kinda feels like it's trying to put a round peg into a square hole. If you try hard enough, everything can go in the square hole! But that doesn't mean it's a good fit.