Per the spec, a "for-range" statement using an iterator requires the iterator's yield
function to return bool
not an arbitrary (user-defined) boolean:
Range expression 1st value 2nd value
array or slice a [n]E, *[n]E, or []E index i int a[i] E
string s string type index i int see below rune
map m map[K]V key k K m[k] V
channel c chan E, <-chan E element e E
integer value n integer type, or untyped int value i see below
function, 0 values f func(func() bool)
function, 1 value f func(func(V) bool) value v V
function, 2 values f func(func(K, V) bool) key k K v V
We should generalize this to any boolean type, similarly to how we allow any string type (not just string
) when we range of strings.
Note that the original implementation of the type-checker accepted any boolean type, but the compiler's front-end had a problem with it (#71131). The (temporary) fix for that issue was to adjust the type-checker to match the spec literally. This avoided a compiler panic.
We should change the spec to reflect the original intent, and then revert the fix for #71131.
Comment From: gopherbot
Change https://go.dev/cl/640599 mentions this issue: go/types, types2: require iterator yield to return bool (work-around)
Comment From: gabyhelp
Related Issues
Related Code Changes
- go/types, types2: require iterator yield to return bool (work-around)
- spec: document for range loop over functions
Related Documentation
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: dolmen
We should generalize this to any boolean type, similarly to how we allow any string type (not just string) when we range of strings.
I'm not convinced by this. Generalizing implies a incompatible change that will affect introspecting at runtime (using reflect) functions that might be iterators.
So could you develop why allowing any bool derived type would be better? What use case does that open?
Comment From: griesemer
The implementation (type checkers, at least) were written from the start to accept any boolean type. The compiler front-end has a bug that causes it to panic when a user-defined boolean is used (#71131). Fixing that bug at this stage of the 1.24 release cycle is risky, so we opted for reporting an error in the type-checker, which is very safe, and which avoids a compiler panic in favor of a very clear error message. We should really fix the compiler front-end (early 1.25 cycle) and then we can also relax this restriction again. I don't know that there are particular issues with reflect at this point since such code didn't work anyway.
Comment From: seankhliao
Is allowing this really good for the ecosystem? these iterators wouldn't be assignable to iter.Seq / iter.Seq2 and would not be composable with any of the iterator helpers out there. It seems strange to allow for covariant result types in this specific corner of the language when its disallowed elsewhere with a faq entry for it.
Comment From: griesemer
@seankhliao That is a good point - I haven't thought about that (I simply opened this issue to document that we changed the original implementation, which allowed for user-defined types, were it not for the bug in the compiler front-end).
We may want to leave things as they are now (TBD).
As an aside, we wouldn't bring in covariance into the language, we'd just allow any boolean type as result type for the specific yield function. But of course, that yield function wouldn't play well with iter.Seq
and iter.Seq2
.
Comment From: rothelius
To be fair, this is already an issue one level up, with named yield func types: https://go.dev/play/p/3euYnrwXP1P
Comment From: gopherbot
This issue is currently labeled as early-in-cycle for Go 1.25. That time is now, so a friendly reminder to look at it again.
Comment From: rogpeppe
FWIW I agree with @seankhliao's reservations. I'd like to see a solid use case for this before it happens, whatever the intention of the original code.